The Pragmatic Programmer in 2025
Over the past few weeks, I read through the 20th Anniversary Edition of The Pragmatic Programmer, by David Thomas and Andrew Hunt (2019). I’ve often seen this book praised as a practical resource for software development. In this low-effort blog post, I’m just going to reproduce a few of my notes on what I found interesting or puzzling here. The book is divided into 53 Topics, which I use to organize my notes.
Two general comments to begin: First, I was very unimpressed with the opening chapter. It’s just urban legends, from stone soup to broken windows, crudely applied to software. This is what I associate with platitudes from a detached leadership. Fortunately, the book gets better. Second, I would not regard the 20th Anniversary revision as a major success. The book still feels a bit dated, with some complaints about “Agile” thrown in. I think it still works well if read for what it is: A certain snapshot of a certain set of problems in design, and it’s up to us to learn how to apply them.
Let’s turn to some specific topics.
Topic 9. DRY: DRY is not about not duplicating code, it’s about not duplicating knowledge or information. If two requirements are conceptually different, but coincidentally checked in a similar way, that’s not a violation of DRY. However, documentation that just describes what a function is doing would be a violation, because it’s the same information expressed in a different way.
Topic 12. Tracer Bullets: I really like this idea, and I think it’s what I already strive to do: Instead of building a bunch of totally separate modules and then trying to integrate them at the last step, build a single end-to-end implementation across your different layers first. This is not a prototype, it’s not throwaway. It gives you a proof of concept and it then provides a scaffold for increases. It’s important to recognize the highly modular code can be very difficult to integrate later, if integration is not considered from the start.
Topic 20. Debugging: I disagree that you shouldn’t “waste a single neuron on the train of thought that begins ‘but that can’t happen’”. If you think that can’t happen, that’s because you think you understand the logic of your code in a way that should rule out the bug. That is absolutely a place to start in debugging. As they say later, part of solving problems is distinguishing absolute constraint from what only appears to be a constraint.
Topic 22. Engineering Daybooks: The suggestion to keep an almost daily journal of insights and ideas. They recommend paper. It’s not a bad idea. Don’t make it a chore, but just have a place to note down ideas.
Topic 28. Decoupling: Long chains of methods can be a sign of coupling. It’s OK if it’s a pipeline with the same API for each level, but if each layer has its own internal logic and expectations, that’s a really bad sign. Instead, minimize your method chains by making sure that the conceptually important objects in your system have smart methods that hide their implementation details. In other words: Long chains of methods are a sign that your “objects” are exposing their implementation or are too numerous.
Topic 32. Configuration: Don’t just use static, flat configuration in environmental variables or configuration files. You need a service API that allows dynamic injection of configuration changes. Otherwise you will have low availability.
Topic 40. Refactoring: Construction is a bad metaphor for coding, because it has such a firm divide between blueprinting, building, and maintenance. Coding is more like gardening.
Topic 41. Test to Use: Testing is not about proving that your code works. It’s about providing a framework for you to think about the interface and intention of your code. This is why it’s so important to do it before, or at worst during, your coding. Your tests are the first users of your code. But don’t assume that TDD is a panacea; it can lead to burnout, useless tests, and doesn’t actually solve the problem of not knowing how to approach a problem. Sometimes you need a bit more of top-down reasoning to know how to do something well.
Topic 45. The Requirements Pit: The job of a programmer is to help a client understand what they want. To figure out the corner cases and iteratively work out how to handle them. The days of a fully specified requirements document were a relic of when computing was very expensive compared to manpower, that’s no longer true. These days, if you write up a fully specified document, it’s something the client will rubberstamp.
Topic 46. Solving Impossible Puzzles: It’s not about thinking outside the box, it’s about discovering what the box is: Which requirements and constraints are absolute, and which are not? Discovering the latter will give you the flexibility to solve the problem.