skip to content
Tushar's Avatar Tushar Tripathi

Write code that is easy to replace

/ 4 min read

There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult. - C.A.R. Hoare

This quote is a poetic reminder for us programmers. It’s much easier to write complex than simple code. The big ball of mud is inevitable for any non-trivial project. The first conclusion one might draw from this is to make ourserves familiar with design patterns, plan more upfront, and be in general more careful about the code we write. But this would be a mistake and often impossible in practice. What we can strive for however is to distribute the complexity in a decoupled manner. Instead of building one giant ball of mud, creating many little ones.

Rewriting is often easier than refactoring

The goal of the best coding practices as ironic as it may sound, is to be able to delete your code. Being able to take a part of code, and replace it in few days to accomodate evolving requirements is extraordinarily valuable. It’s ok to compromise on the quality but not on the deletability of code. Everything is fixable as long as it’s easy to just throw it away and rebuild from scratch. This can’t be achieved if the whole codebase is a big ball of entangled mud where it takes days to understand what’s going on in midst of all the gotchas and subtle dependencies.

Do note that rewriting everything from scratch is a separate topic and often something you should never do.

Maintainable code is easy to replace

The only thing you would achieve if you try to make your code easy to extend by thinking of future possibilities is to make it more complex. The code will be harder to understand and harder to replace. And you would more often than not, build wrong abstractions or do premature optimizations.

The most important question to ask yourself while writing code is not whether it will stand the test of time. But whether it can be easily removed or replaced if needed. If you can replace it in a day, you can extend it in a day. The codebase design should embrace the fact that most lines written will be thrown away as requirements change and evolve.

This also cultivates a culture of building quick prototypes. So many features and projects are never released because they are never finished. It’s just weeks and weeks of taking care of hypothetical issues, before the next high priority feature comes along.

How?

  • Decoupling - Isolate complexity, with minimal API Surface - Make modules, libraries and services with a minimal, clear, well documented API.
  • Write tests - So when you the time does come, you’re more confident about not breaking anything. Of course, tests are not a silver bullet, but they can be quite close for a well decoupled codebase with clear boundaries.
  • Single responsibility principle - A function/library/service should do one thing and do it well. If it’s easy to understand what it does and what it doesn’t do, it’s easy to replace.
  • YAGNI - You aren’t gonna need it. In the words of Ron Jeffries - “Always implement things when you actually need them, never when you just foresee that you need them”.
  • Documentation - Documenting the thought process and rationale behind key decisions is invaluable. It not only helps the future team to make better decisions but also the current you to gather feedback and avoid feature creep.
  • The Art of Destroying Software - Greg Young. Some quotes from the video -
    • This is the unix philosophy, this is the Erlang philosophy, this is microservices, this is SOA(Service oriented architecure), this is actors.
    • You know the most about your project at the end after you’ve done everything, not at the beginning while planning.
    • The problem is most people are picking up these ideas and they are never understanding the fact that the goal is to delete your code.