Reconciling YAGNI With Reversibility
Yesterday I started reading Andrew Hunt’s The Pragmatic Programmer, which has been sitting around waiting to be read for at least several months. Every programmer I talk to says that it’s a book that will really stimulate growth as a programmer, and I certainly need to grow.
Already several of the things the author has said have rung true, and I can see many of the scenarios he’s described as having happened, or nearly happened, in a few of the projects I myself has been involved with. Which is exactly what has piqued this post.
Hunt outlines the need for what he calls reversibility. He states that there are critical decisions to be made in each project, and each of these involves a commitment that often isn’t easily changed, such as committing to a type of database, or committing to a design pattern.
He goes on to describe the need for a Schrödinger’s Cat approach to software design. The basis is the Schrödinger’s Cat theory which illustrates quantum superposition – where at the point of a decision, multiple dimensions spawn off and you don’t know which dimension you’re in until the decision is observed.
Hunt argues that we should design code to be flexible enough to allow as many of the “possible dimensions” as possible. What he means by this is to allow for these critical decisions to be changed, with minimal impact on the code, hence the term reversibility.
To do this, we should code for possibilities. We should always keep in mind what might happen, and design our software so that we can accommodate the change. This flies directly into the face of another principle of programming, You Ain’t Gonna Need It (YAGNI), and the general belief that the more code you write, the more opportunity there is to introduce bugs in code (something Hunt himself mentions).
YAGNI outlines that you shouldn’t write code you don’t need, and cites several reasons why, and coupled along with the idea of writing less code is better, this is something I believe. However Hunt’s Reversibility idea is in almost direct opposition – he advocates writing code “just in case.”
Can these two ideas be reconciled?
I believe they can, and it’s an approach I’ve used in the past. I’ve found that it’s possible to quickly and safely recognise that there is the possibility of a change by writing interfaces and method stubs for new functionality, and have them throw not-implemented exceptions.
This way, you acknowledge that a decision might be changed and have already got the application hooks to hook into it, but you haven’t wasted time implementing something that may not come to pass. Your software will build, and at the existing functionality won’t be broken while you wait to implement these new changes.
This seems like a trivial thing to do, however it can really streamline the process in my experience.