When to denormalize (and when it's just laziness)
·2 min read ·Databases · Performance · Architecture
Database normalization is the process of organizing data to reduce redundancy. The third normal form keeps everything clean, every fact stored once, foreign keys maintaining consistency. And then your dashboard query takes 2.4 seconds because it's joining eight tables.
Denormalization—intentionally introducing redundancy—can solve that. But it's a deliberate tradeoff, not a shortcut.
What Denormalization Actually Costs
When you store the same data in two places, you take on the responsibility of keeping them in sync. That's not a technical problem the database solves for you anymore—it's your code's job.
Forget to update one copy when the source changes, and your data is inconsistent. Inconsistent data is one of the hardest classes of bugs to debug.
When It's the Right Call
Reporting and analytics queries. Queries that aggregate across millions of rows and join multiple tables are often good candidates for a pre-computed summary table.
-- Instead of joining orders + order_items + products every time:
SELECT * FROM monthly_revenue_summary WHERE month = '2024-01';
Refresh this summary table nightly or with a queue job when orders change.
Frequently accessed, rarely changing data. If you're constantly joining to get a user's display name, store it where you need it.
Search. Elasticsearch, Algolia, and similar tools are denormalized by design—they trade write complexity for read performance.
When It's Just Laziness
Adding a user_email column to the orders table because you 'don't want to join users' is not denormalization. It's a missing join. It will bite you when a user changes their email and you have 400 inconsistent order records.
Denormalize with a specific performance problem in hand, a measurement proving the problem exists, and a plan for keeping the copies in sync. Not before.