# Normalization vs denormalization

> Store each fact once, or copy it where it’s read — and how to keep the copies honest.

Concept · Storage & databases · https://levelcode.org/concepts/normalization-vs-denormalization

**Normalization** stores each fact in one place: a customer's name lives in the `customers` table, and orders reference it by ID. **Denormalization** copies data to where it's read: each order row also stores the customer's name.

## Normalized

- Updates touch one row; there's no way for copies to disagree.
- Reads that need related data use joins, which a relational database does well with the right indexes.
- The default for transactional data. The SQL roadmap's schema-design lesson covers normal forms.

## Denormalized

- Reads are one lookup with no joins — essential in stores that don't join (key-value, wide-column) and useful at very high read rates.
- Every copy must be updated when the fact changes, and copies can disagree in between.

## Kinds of denormalization

- **Snapshots that should not change.** An order's shipping address and item prices *should* be copied at purchase time: a later change to the customer's address must not rewrite history. This isn't a compromise; it's correct modelling.
- **Read-optimised copies.** A feed entry that embeds the author's name and avatar; a product listing with its review count. Fast to read, needs updating.
- **Materialized views and precomputed aggregates.** "Sales per day per store" maintained incrementally or refreshed periodically.

## Keeping copies honest

- Derive copies from a **single source of truth** and propagate changes asynchronously through events ([transactional outbox](https://levelcode.org/concepts/transactional-outbox)), accepting that they lag briefly.
- Prefer copying **IDs** and looking up mutable fields at read time (with a cache) when staleness would be visible or harmful — the news-feed lesson's timelines store post IDs for exactly this reason.
- Be able to **rebuild** a denormalized copy from the source when it drifts.

Rule of thumb: normalize the system of record; denormalize in derived, rebuildable read models when a measured read path needs it. See also [event sourcing and CQRS](https://levelcode.org/concepts/event-sourcing-cqrs).
