TITLE: Writing Code that is Easy to Delete AUTHOR: Eugene Wallingford DATE: January 02, 2019 2:22 PM DESC: ----- BODY: Last week someone tweeted a link to Write code that is easy to delete, not easy to extend. It contains a lot of great advice on how to create codebases that are easy to maintain and easy to change, the latter being an essential feature of almost any code that is the former. I liked this article so much that I wanted to share some of its advice here. What follows are a few of the many one- and two-liners that serve as useful slogans for building maintainable software, with light commentary.
... repeat yourself to avoid creating dependencies, but don't repeat yourself to manage them.
This line from the first page of the paper hooked me. I'm not sure I had ever had this thought, at least not so succinctly, but it captures a bit of understanding that I think I had. Reading this, I knew I wanted to read the rest of the article.
Make a util directory and keep different utilities in different files. A single util file will always grow until it is too big and yet too hard to split apart. Using a single util file is unhygienic.
This isn't the sort of witticism that I quote in the rest of this post, but its solid advice that I've come to live by over the years. I have this pattern.
Boiler plate is a lot like copy-pasting, but you change some of the code in a different place each time, rather than the same bit over and over.
I really like the author's distinction between boilerplate and copy-and-paste. Copy-and-paste has valuable uses (heresy, I know; more later), whereas boilerplate sucks the joy out of almost every programmer's life.
You are writing more lines of code, but you are writing those lines of code in the easy-to-delete parts.
Another neat distinction. Even when we understand that lines of code are an expense as much as (or instead of) an investment, we know that sometimes we have write more code. Just do it in units that are easy to delete. A lesson in separating concerns, from Python libraries:
requests is about popular http adventures, urllib3 is about giving you the tools to choose your own adventure.
Layers! I have had users of both of these libraries suggest that the other should not exist, but they serve different audiences. They meet different needs in a way that that more than makes up for the cost of the supposed duplication.
Building a pleasant to use API and building an extensible API are often at odds with each other.
There's nothing earth-shattering in this observation, but I like to highlight different kinds of trade-off whenever I can. Every important decision we make writing programs is a trade-off.
Good APIs are designed with empathy for the programmers who will use it, and layering is realising we can't please everyone at once.
This advice elaborates on the quote earlier to repeat code in order not to create dependencies, but not to manage them. Creating a separate API is one way to avoid dependencies to code that are hard to delete.
Sometimes it's easier to delete one big mistake than try to delete 18 smaller interleaved mistakes.
Sometimes it really is best to write a big chunk of code precisely because it is easy to delete. An idea that is distributed throughout a bunch of functions or modules has to be disentangled before you can delete it.
Becoming a professional software developer is accumulating a back-catalogue of regrets and mistakes.
I'm going to use this line in my spring Programming Languages class. There are unforeseen advantages to all the practice we profs ask students to do. That's where experience comes from.
We are not building modules around being able to re-use them, but being able to change them.
This is another good bit of advice for my students, though I'll write this one more clearly. When students learn to program, textbooks often teach them that the main reason to write a function is that you can reuse it later, thus saving the effort of writing similar code again. That's certainly one benefit of writing a function, but experienced programmers know that there are other big wins in creating functions, classes, and modules, and that these wins are often even more valuable than reuse. In my courses, I try to help students appreciate the value of names in understanding and modifying code. Modularity also makes it easier to change and, yes, delete code. Unfortunately, students don't always get the right kind of experience in their courses to develop this deeper understanding.
Although the single responsibility principle suggests that "each module should only handle one hard problem", it is more important that "each hard problem is only handled by one module".
Lovely. The single module that handles a hard problem is a point of leverage. It can be deleted when the problem goes away. It can be rewritten from scratch when you understand the problem better or when the context around the problem changes. This line is the heart of the article:
The strategies I've talked about -- layering, isolation, common interfaces, composition -- are not about writing good software, but how to build software that can change over time.
Good software is software that can you can change. One way to create software you can change is to write code that you can easily replace.
Good code isn't about getting it right the first time. Good code is just legacy code that doesn't get in the way.
A perfect aphorism to close to the article, and to perfect way to close this post: Good code is legacy code that doesn't get in the way. -----