Databases

Normalization vs denormalization

DatabasesData modellingDecision guide

Short answer

Normalize the authoritative write model by default so each fact has one owner and constraints are enforceable. Denormalize a measured read path when joins or fan-out are the bottleneck, and accept the resulting update, backfill, and consistency work as part of the design.

Written and reviewed by Sahil Srivastav

What each one actually is

Normalization separates facts into related tables and uses keys and constraints to avoid update anomalies. Reads may need joins, but a change has one authoritative place to land.

Denormalization copies or precomputes data to make a common read cheaper. It can be a column, aggregate, materialized view, search document, or separate projection.

The decision is about ownership and workload, not a moral preference. Keep the source of truth clear and make rebuilding a projection possible.

Side by side

 NormalizationDenormalization
Write duplicationLowHigher; multiple copies need updates
Read shapeJoins and aggregationFewer joins, faster known queries
ConsistencyConstraints and transactions are directLag, races, and repair paths must be handled
Schema changeOne fact’s shape is centralisedEvery projection may need migration
StorageUsually smallerMore copies and indexes
Write throughputFewer writes per factFan-out can be expensive
ReportingFlexible joinsFast precomputed reports for known questions
RecoveryRestore source tablesRebuild projections after restore or code change

Choose Normalization when

  • The domain has many writes and strong relational invariants
  • Facts are edited independently and must remain consistent immediately
  • Queries are varied or the schema is still changing
  • The team needs one authoritative source with straightforward migrations

Choose Denormalization when

  • A measured hot read is dominated by joins or repeated aggregation
  • The access pattern is stable enough to define a projection
  • A bounded consistency lag is acceptable
  • The team can run backfills, reconciliation, and rebuilds

The trade-off in detail

Denormalization turns a database constraint into application machinery. A copied customer name must be updated on rename, and a missed event leaves stale data; version projections and record the source version to detect drift.

Normalization does not mean one query per relationship. Proper indexes, joins, materialized views, and batching can serve substantial workloads without copying every field.

A projection should be disposable in principle. If nobody can rebuild it from authoritative data, it has become a second source of truth and the system now has an undocumented write path.

Things that are commonly said and are wrong

  • “Normalization always makes reads slow.” Indexes and query planning often make normalized reads efficient.
  • “Denormalization means no constraints.” Enforce source invariants and define how copies are refreshed and validated.
  • “A materialized view is automatically current.” Refresh mode and transaction timing determine its freshness.

Decide it in a real repository

Choosing correctly on a whiteboard and enforcing the choice in code are different skills. Gronex ships broken backend repositories whose tests assert the invariant, not the happy path.

FAQ

Should I denormalize early for performance?

Usually no. Start with a clear normalized model, measure the slow query, and add a projection or targeted copy when the workload justifies its maintenance cost.

How do I keep denormalized data correct?

Choose a source of truth, update through an outbox or transactionally captured change, make consumers idempotent, and run reconciliation that compares versions or hashes.

Can JSON fields replace normalization?

Only when the embedded data has the right ownership and query pattern. JSON can avoid a join, but it can also hide duplicated facts and weaken constraints.

Other decisions engineers weigh