“Software design is preparation for change; change of behavior”
Tidy First? is a new book by Kent Beck. It is a short little book, only about 100 pages (and lots of white space on them), but it contains some deep insights about software development. The book has three parts, going from concrete to abstract. First there is a list of 15 tidyings, which are small refactorings. The next part, Managing, discusses how and when to perform the tidyings. The final part, Theory, presents a great framework for how to think about software development, using the concepts of time value of money and optionality.
The author, Kent Beck, is of course the creator of extreme programming (XP). As I have written before, his article in 1999, presenting XP, gave me the biggest productivity boost of my entire career as a software developer. Even though this is a very short book, it contains a lot of wisdom. It is worth reading slowly, to really digest the content.
A key idea in the book is that before you implement a behavior change (B) in the code, it may be beneficial to first perform one or more structural changes (S). These changes do not alter the program behavior, and are almost trivially simple. These changes are called tidyings. The idea is that by doing these tidyings, the behavior change will be easier to implement.
Tidyings
There are 15 tidyings, and they are presented in very short, almost tweet-like, chapters.
Tidyings I Like the Most:
Guard clause – exit a function early if certain conditions are not met. This makes the rest of the function easier to write (no nested if-statements). Normalize symmetries – the same logic should be expressed in the same way everywhere it appears, since it makes reading the code easier.
I have noticed that many developers are reluctant to introduce explaining variables/constants. The idea here is to extract a subexpression into a variable named after the intention of the expression – typically done after reading the code and realizing what some part of it means. In the author’s words: “In this tidying, you are taking your hard-won understanding and putting it back into the code”.
New interface, old implementation. If the design was like this, it would be easier to make the change. So create that new interface, and in it delegate to the old interface (for now). I really like this way of thinking, and I am using it often: first I assume I have a function that does XXX, and using that makes the solution easier. Then I create the function that does XXX. In a way it is working backwards – first assuming you have something available, and later implementing it.
The simplest tidying of them all is chunk statements , which just means using blank lines to indicate which parts of code are closely related, and which parts a separate. An underestimated practice, even though sometimes it can be hard to know how to chunk things. Too many blank lines can also mean you fit less code on your screen, so it needs to be balanced. Extract helper is another underused technique. Like explaining variable, it lets you name a part of the logic. “Interfaces become tools for thinking about problems”.
One tidying that I don’t think I have used before is one pile. Normally, tidyings will divide the code up in parts, where each part can be understood in isolation. However, sometimes the way the code is divided can hinder understanding. In this case, bringing it all together in one place can be a way to understand it better. Then it can be subdivided (in a new, easier to understand, way).
Explaining comments/delete redundant comments. When needed, I am all for adding a comment with extra information that is not obvious from the code. Also, if the comment says exactly what the code does, delete the comment (there is a good example in the book how this can happen when tidying).
Other Tidyings:
Delete dead code – this should be easy, but I often see either dead code, or commented out code, still left in code bases. Reading order – put the code in the file in the order that makes the most sense when reading it. This advice is less important in the age of IDEs, where navigating in and out of functions is very easy. Still, keeping functions in a good order doesn’t hurt. It is also similar to cohesion order – keeping elements that change together close to each other. There is a similar theme for move declaration and initialization together – keep related things together.
If the arguments to a function are in the form of a map/dict, then use explicit parameters to make clear what the inputs are.
For many of these, the best way may be to try them and see if the resulting code is better than before. If not, undo the change. Many times I have been too reluctant to make a change to see how it looks (somehow it feels like wasted effort). But I have come to realize that actually seeing the changed code (not just contemplating it) is the best way of evaluating the change.
Managing
Each individual tidying is very simple. They only change the structure of the code, never the behavior. Even chaining several tidyings together will result in a change that is easy to understand, and easy to undo if necessary. Sometimes the behavior change will be easier if we tidy first, then implement the change. In other cases, it is better to make the structural changes later, or not at all. This is the reason there is a question mark in the book title. Regardless, structural and behavioral changes should be kept in separate PRs (or at least in separate commits).
In many work places, there are high fixed costs (in time and effort) associated with PR reviews. The ideal solution for this, according to the author, is to not require PR reviews for only tidyings. If this is not feasible, then at least keep the changes in separate commits.
A problem I often encounter is that once you start making behavior changes, you see structural changes that should be done. This results in a mix of B and S changes. Separating them out can be hard. There is a good discussion on how to handle this in the chapter Getting Untangled. Either you ship it as it is (tangled), or you untangle the different changes (I have been doing this using git’s interactive rebase), or you discard all the changes and re-implement the changes. The last option sounds a bit crazy, but the author thinks that this may lead to even better code in the end.
Theory
Beneficially Relating Elements
Software design is beneficially relating elements. On one extreme there is a single gigantic soup of tiny subelements, for example assembly code with a global namespace. Even though such a program can work and produce the correct output, it would be virtually impossible to modify. The key then is to structure the program to make it understandable and changeable. This is done by creating and deleting elements, and creating and deleting relationships between the elements in a way that aids the overall understandability (this is the beneficially part).
Time Value of Money, Optionality
How do we balance keeping the program well-structured with the need to add behavior? Now we get to perhaps my favorite part of the book – relating software development to the concepts of time value of money and optionality. These are in tension with each other, and explain the question mark in the title.
The time value of money simply means that a dollar today is worth more than a dollar tomorrow. Therefore, getting features out quickly, so you can start earning money earlier, is imperative. So don’t tidy first.
However, software creates value in two ways: what it does today, but also in what it could do tomorrow. As noted in the book: “The mere presence of a system behaving a certain way changes the desire for how the system should behave”. This explains why software is never done – using a system makes you continually see new usages for it. Just like options in finance have value even before they are exercised, so do options in software. The options in this case is the structuring of the code that enables quick changes. Tidyings improve the structure, thus creating more, and more valuable, options. Therefore you should tidy first.
Because of this tension between the cases, you have to find a balance for when, and how much, to tidy.
Coupling
The key reason a program is expensive to change is that changing one element requires changing other elements (because the elements are coupled with respect to that change). Changing the other elements can in turn necessitate more changes, i.e. cascading changes. Therefore, reducing coupling will reduce the cost of change.
Constantine’s Equivalence states that the cost of software is roughly equal to the cost of changing it. This cost of change is dominated by the cost of the big, cascading changes. Therefore, the cost of software is approximately equal to the coupling.
To Keep in Mind
Here are the key lessons from the book that I want to keep in mind when developing software:
- What structural change(s) (S) will make the next behavioral change (B) easier to implement?
- Keep S and B in separate commits (or even separate PRs).
- Create future behavior options by keeping a structure that supports change.
- Constantine’s Equivalence: cost(software) ~= coupling
Conclusion
There is a lot to like about this book. It has many concrete code tidyings you can put to use right away. It also has interesting discussions on how and when to perform them, as well as models to help you think about the tradeoffs present. Throughout the text there are numerous indications that the author has long practical experience, and has thought long and hard about software development.
This book is focused on the individual developer, and is the first in a series of three books. The next book will be about teams of software developers, and the third book will be about the cooperation between developers and non-developers. I really enjoyed Tidy First?, and I am looking forward to reading the next books in the series.
Top comments (0)