Principle · Technology and AI
Make it Work, Make it Right, Make it Fast.
Source: Kent Beck, Extreme Programming Explained (1999), Addison-Wesley. The phrase is widely attributed to Beck and to Donald Knuth's related teaching in The Art of Computer Programming on premature optimization.
The Principle
Engineering work has three phases, in this order: make it work, make it right, make it fast. The first phase produces a version that returns the correct output, even if the code is ugly, the structure is wrong, and the performance is bad. The second phase keeps the behavior the same and cleans up the structure: better names, better separation of concerns, fewer special cases. The third phase optimizes for speed or cost, only after the first two are settled.
The temptation is to compress the three phases or to reorder them. Compressing produces buggy, unclean, slow code that has to be rebuilt anyway. Reordering, especially trying to make it fast before making it work, is worse. Knuth's famous formulation, "premature optimization is the root of all evil," names the trap. You cannot optimize what does not yet behave correctly. You will optimize the wrong path, encode the wrong assumptions, and bury the actual bug under a layer of clever code.
The reason the order is fixed is that each phase reveals what the next one needs to know. Making it work tells you what the actual problem is. Making it right tells you which parts of the design are stable. Making it fast tells you which parts to invest in. Skipping or reordering hides this information and produces work that has to be redone later, slower than if you had just done it in the right order.
Why It Matters Here
Technology and AI is the department where the temptation to skip phases is strongest. The pressure to ship fast and the desire to look impressive both push toward "let me build the elegant, optimized version first." That is how systems end up taking three times as long to ship and breaking in production. The CAIO who holds the discipline of work, then right, then fast, ships systems that hold up. The CAIO who skips it ends up rewriting from scratch, often more than once.
Signals (When to Apply)
- An engineer is debating architecture before any version of the system runs end-to-end
- The first version of a feature is being designed for scale before it has any users
- The team is rewriting code that is correct but ugly, when there is no business reason to
- Performance is being optimized before correctness is verified
- A deadline is approaching and the team has spent more time on design than on building
How to Apply
- Phase one: get a version that produces the right output for the simplest realistic case. Hardcoded values are fine. Ugly code is fine. The only requirement is that it returns the correct answer for at least one input.
- Phase two: refactor the structure without changing the behavior. Rename variables, extract functions, remove duplication, separate concerns. Run the same tests against the new structure to confirm behavior is preserved.
- Phase three: profile the system to find where time and money are actually spent. Optimize only the parts that profiling identifies. Optimizing parts that do not appear in the profile is wasted effort and adds complexity for no benefit.
- Resist the urge to combine phases. "I'll just make it right while I'm making it work" is how you produce code that is neither working nor right. The discipline of one phase at a time is what makes each phase fast.
- Communicate the phase. When asked for status, name the phase: "It works. I am about to make it right." Stakeholders pressure faster work because they cannot see what the engineer is doing. Naming the phase makes the work visible.
Examples
Applied well
A team needs to ship a new content-summarization automation. Day one, the engineer writes a script that loops through three test files and prints summaries. The script has hardcoded paths, no error handling, no concurrency, and is ugly. It works. Day two, the engineer extracts the summarization step into a function, adds basic error handling for the file-not-found case, and runs it against twenty real files. It is now correct on real inputs. Day three, the engineer profiles a run on a hundred files and discovers that the LLM call is the bottleneck. They add concurrency to that step. Total time to a production-quality system: three days, with each phase building cleanly on the last.
Misapplied
The same team starts day one by designing the architecture. By day three, they have a beautiful diagram with five services, message queues, retry logic, and observability hooks. They have not yet run a single summarization. By day seven, they have built three of the five services, and the first integration test reveals that the summarization step itself does not produce the output they need. They start over. Total time to a production-quality system: three weeks, and the team is exhausted.
When to Break It
- In domains where correctness has to be verified before any version ships, like security-critical or safety-critical systems. There, the design has to be right before the code runs. Most operational and AI work does not meet this bar.
- For known problem types where the correct architecture is well understood and "make it work" is not informative. Reaching for the standard pattern is fine when the standard pattern actually applies.
- When phase three has a hard deadline (a known traffic spike, a contractual SLA) and you must build for the load from day one. Even then, build the simplest version that meets the load, not the most elegant one.
Further Reading
- Kent Beck, Extreme Programming Explained (1999). The foundational text.
- Kent Beck, Test-Driven Development by Example (2002). The companion discipline.
- Donald Knuth, The Art of Computer Programming (1968 onward). The source of "premature optimization is the root of all evil."
- Martin Fowler, Refactoring (1999, second edition 2018). The practical guide to phase two.