Simplicity limits unnecessary complexity so software remains understandable, maintainable, and changeable. It helps teams support existing code, find and fix bugs, and give newcomers a usable path into the project. section 1.2.1
Treat simplicity as one success factor that must be balanced against delivery and requirements:
- Accomplish the goals: deliver the system when needed while managing quality, budget, deadlines, support, and risk. section 1.2.1
- Meet the requirements: provide the agreed functions and properties and make the system work correctly. section 1.2.1
- Preserve constant simplicity: keep the code understandable so refactoring, testing, maintenance, and bug fixing remain affordable. section 1.2.1
- Choose compromises consciously when fully satisfying every factor is unattainable. section 1.2.1
A maintainability decision
A complicated implementation may satisfy today’s requirement, but colleagues who join later can struggle to understand or modify it. A simpler implementation may require deliberate design effort, yet its lower support cost leaves more capacity for refactoring and testing. The practical choice is not “simple at any price,” but a conscious balance among simplicity, goals, and requirements. section 1.2.1
Figure 1.4 Compromising between simplicity, goals, and requirements
Use the trade-off diagram to compare simplicity with goals and requirements rather than treating simplicity as an isolated target. section 1.2.1
Table 13.1 Comparison of approaches (view table figure)
| Simplicity |
Boilerplate |
Convenience |
Advanced language features |
|
|---|---|---|---|---|
| Free monads |
Simple |
Some |
Not for this task |
Nothing special |
| Final tagless |
Simple |
Some |
Probably not |
Type classes, phantom types, wrapped monads |
| GADTs |
Very simple |
Almost none |
Very convenient |
GADTs |
| Service handle |
Very simple |
Almost none |
Very convenient |
Nothing special |
| ReaderT |
Very simple |
Much (due to lifting) |
Probably not |
Nothing special |
Use the comparison table to examine how functional design approaches trade simplicity against boilerplate, convenience, and advanced language features. section 13.2.7



