Optimize for Code Deletability
It's not my first time to talk about this idea in this blog: we should optimize our code for its deletability, reversibility, replacability, etc.
- Adding more code is a code smell - dsdshome
- Livable and Disposable Code - dsdshome
- No Code is Better Than No-Code - dsdshome
- Don't Rewrite, Replace - dsdshome
Yet this post is my another attempt to connect the dots around this idea about deletability.
Why is deletable code great?
It all comes down to changeability and maintainability.
In his great talk about the art of destroying software, Greg Young mentioned that the philosophies behind Erlang, Microservices, OOP, Unix are all the same: Writing code for the purpose of deleting it.
- Writing code for the purpose of deleting it
- Erlang
- Microservices
- Objects (in Smalltalk)
- Unix
- How to optimize for deletability?
- Decouple
- 18-month project -> 1-week project
- Benefits
- Reduce your fears
- Increase your velocity
- The difference between great code and sucky code is only the size of the program.
- Don't try to predict the future. Optimize for re-writability.
This idea resonated with my personal experiences pretty well. When I first started learning programming, I wished to write a program that can be used for decades without changing a single line of code for it. This wish turns out to be somewhat naive, because in the real life, most of the requirements are constantly changing and so would our implementations. The best we can do is to make our code easy to change.
Code that's easy to delete is easy to replace or change. We can delete the part which doesn't meet our new requirement and write a new one to replace it.
How to write deletable code?
Then the question becomes: How to write deletable code to make our codebase more maintainable?
TDD & TCR
We can understand TDD from the perspective of code deletability as well: all the tests we write is to make our life easier when we delete a portion of our code. These failed tests would point us to the broken part of our application caused by the deletion.
When Justin Searls introduced his favorite way to TDD, he also had this exactly the same idea: when he needed to update a piece of feature, he would first find the minimal sub-tree of classes in his application that affects this feature, delete it, and rewrite it completely.
He thought it was one of the drawbacks of London-school TDD compared to Detroit-school TDD: Radical implementation changes favor rewrites over refactors. But it might be a major win if we want our code to be deletable, isn't it?
Then, TCR (Test && Commit || revert) got introduced recently as a successor for TDD. The idea of TCR is simple: automatically revert to the latest stable point when the new change introduces any test failure.
When Kent Beck introduced this new workflow, he said:
It’s kind of nice to have incorrect code instantly deleted. If you don’t want a bunch of code wiped out then don’t write a bunch of code between greens.
In another word, TCR is a workflow that forces us to embrace code-deletion. You need to think hard about how to organize your code and how to make your changes so that you are no longer afraid of the code-deletion.
Single Responsibility Principle
I also realized that Single Responsibility Principle was also about code-deletability as well. Only when the code we need to delete has a single responsibility, can we delete it without the fear of affecting other irrelevant responsibilities.
The philosophies mentioned in the art of destroying software are great examples for SRP:
- Unix
- Do one thing and do it well.
- Erlang
- Each real world concurrent activity should be mapped onto exactly one concurrent process in our programming language.
- Microservices
- Each microservices has only one single responsibility.
- OOP
- Multiple objects are better than a single large one.
And to keep things small is just an over-simplified version of SRP: if a thing is too big, then it's a smell that it has more than one responsibilities.
Don't rewrite, replace!
StranglerApplication mentioned by Martin Fowler is a great technique to refactor a large application. And it's exactly a thinking of making code deletable first, and then delete it.
Code-deletion can happen on the fly
Finally, there is a trend to embrace the fact that code is going to be deleted, and the deletion may happen on the fly.
Take Netflix's chaosmonkey for example, it's an extreme of deleting the code when the app is running. And by simulating these extreme cases actively, we as developers can build a resilient system.
Erlang's "Let it fail" philosophy is based on the same assumption: failure is inevitable. When a failure happens, it's no different than this part of code is deleted. So Erlang developers struct their apps with tons of small processes and a Supervision Tree to make sure the "code gets deleted" is minimum and we can restore it back easily.
Bonus: A CTO needs to help the team write less code
Based on this thinking, we can get a simplified answer for "What's a CTO's responsibility?"
I believe a CTO is responsible for an organization's technical excellence. And to make a team become more tech efficient, a CTO needs to help everyone write less code.
She can build a framework around the domain the team is working on.
She can introduce new technologies (languages, libraries, tools, processes, etc.) to the team.
She can help cut the scope of a project and define the MVP.
But the goals are the same: to write less code and get more things done.