The Premature Optimization Principle
Make it work, then make it right, then make it fast — in that order.
The Premature Optimization Principle
In one line: Make it work, then make it right, then make it fast — in that order.
Donald Knuth: "Premature optimization is the root of all evil." Most code doesn't need to be fast. When you guess at what's slow without measuring, you're almost always wrong — and you've made the code harder to read in exchange for nothing. Build it. Clean it up. Then profile.
The rule
- Make it work. Build the feature end-to-end. Don't worry about performance yet.
- Make it right. Clean up the code, add tests, handle edge cases.
- Measure performance. If performance is fine, you're done.
- Optimize the bottlenecks. Profile-guided, not guess-guided.
Why it matters
- Most code doesn't need to be fast. Network and I/O dominate.
- "Optimizations" often turn out to be irrelevant when you measure.
- Premature optimization makes code harder to read, modify, and debug.
- You don't know what's slow until you profile.
When to optimize early
- Algorithm choice. O(n²) vs O(n log n) matters at any scale.
- Database query patterns. N+1 queries should never ship.
- Network calls inside loops. Always batch.
- Bundle size for client-side JS. Page weight matters from day one.
These are not premature — they're structural choices that are expensive to change later.
An engineer spends a week micro-optimizing an inner loop, hand-rolling a buffer pool, avoiding allocations. Benchmark shows ~3% speedup overall.
Meanwhile, a profiler run reveals the actual hot path: a function calling the database inside a .map() over user IDs. Replacing it with a single WHERE id IN (...) query cuts total response time by 80%.
The lesson: measure first. The intuition about what's slow is reliably wrong. Profilers are not optional — they're the only honest source of truth about where time goes.
"Premature optimization is the root of all evil" is often misread as "never think about performance." It actually means: don't tune implementations before measuring.
You should always think about structural performance choices from the start:
- An O(n²) algorithm where n grows with users will eventually melt.
- An N+1 query that ships will cause a 3 a.m. page.
- A 5 MB JS bundle is broken on mobile from day one.
These are not "optimizations" — they're correctness at scale. Fixing them after the fact is much more expensive than choosing right initially.
Common mistakes
- Quoting Knuth to skip thinking about performance entirely. "Premature optimization is the root of all evil" doesn't mean "ignore performance until users complain." Structural choices — O(n²) loops, N+1 queries, megabyte-sized bundles — are correctness decisions, not optimizations. Get those right from day one.
- Profiling in dev or with toy data. The hot path on your laptop with 100 rows is almost never the hot path in production with 100 million rows. Use real-shaped data, real traffic patterns, and ideally a profile from production itself before you tune anything.
- Adding a cache to dodge a real problem. Caching turns a slow query into a fast query and a cache-invalidation problem. If the underlying query is 200ms because it scans a table without an index, fix the index — don't paper over it with a cache that becomes its own failure mode.
- Treating "fast" as the last step you ever take. "Make it work, then right, then fast" is an ordering, not a one-shot pipeline. Code that was fast at v1's data size can be unusably slow at v3's. Re-profile when the shape of the workload changes, not just when it ships.
Page checkpoint
Did premature optimization stick?
RequiredWhat's next
→ Continue to The Documentation Trade-Off — document the things that don't change, not the things that do.