A database schema designed under deadline pressure tends to work fine for the first six months and become expensive for the next three years. The mistakes aren’t exotic โ they’re a handful of shortcuts that are easy to take and hard to undo.
Storing data that should be computed
Caching a computed value (a total, a count) directly in a table can be a legitimate performance optimization โ but doing it without a clear strategy for keeping it in sync with the source data is how totals quietly drift from reality over time.
No indexes until performance becomes a visible problem
Indexes added reactively, after a slow query is already affecting users, is a common pattern โ and a backwards one. Thinking through which columns will be filtered and sorted on during design, not after a complaint, is cheap insurance against a much more disruptive fix later.
Overly generic “just in case” fields
A handful of nullable “extra_data” or “notes” text fields, added speculatively for flexibility, tend to accumulate undocumented meaning over time until nobody’s sure what’s actually stored in them or why.
Skipping foreign key constraints
Enforcing relationships at the application level only, without the database itself guaranteeing referential integrity, works until one bug or one manual data fix creates orphaned records that silently corrupt reports and logic downstream.
The fix is cheap early and expensive late
None of this is complicated to get right. The problem is always timing โ these decisions are nearly free to make correctly during initial design, and genuinely costly to unwind once real data and real dependencies have built up on top of them.
