The old adage ‘if it’s not broken, don’t fix it’ was coined in the days when technology was beginning to enter people’s houses, in an age when trying to get rid of a whirring sound in your refrigerator could end up with a great deal of melting ice cream. It is also a saying that many if not most developers have heard at least once in reference to code.
On the surface, it appears to be not just correct but self-evident: if a piece of code is doing what it should be, then surely there can be no reason to change it. But is that really true?
The short answer is no. Not wanting to improve code is a sign that one is afraid of the consequences of changing it, which happens almost exclusively when one doesn’t properly understand the code. And you should always understand the code you are working on, or at least strive to understand it.
The longer answer is… it depends.
Reasons to change working code
The trouble with applying this adage to the world of programming is that it’s harder to say when exactly changing code qualifies as ‘fixing’ it. If I rewrite part of a project to simplify it and make it easier to read, but I keep its fundamental logic and functions intact, am I really fixing it? Or am I simply giving it a new sheen?
Leaving aside issues related to the code’s legibility, there are in my opinion 3 main cases in which amending functional code qualifies as a fix. The first is when code is made more efficient, which is necessary when the code is indeed working, but not as quickly or as well as it could. The second is when the code does what it should, but its maintenance is inordinately difficult. The third and most important is when the code only appears to be working, meaning that it passes most tests but cannot deal with edge cases, or else it can handle data at a certain scale but not outside of it. In these circumstances, you should definitely consider fixing code that works.
In addition, you should consider that refactoring is an excellent way to train new teammates. This may not be as essential as in the 3 examples above, but it can help new developers understand the architecture of the code while simultaneously improving it, killing two birds with one stone.
How to assess when code should be changed
Another famous adage says that perfect is the enemy of good, and this certainly applies to coding. Before deciding whether to fix code which can stumble along without being touched, you should ask yourself three questions.
The second is how substantial is the fix? Will it make a tangible change to the final product, or is it mostly superficial? Are you improving performance by 50%, or by 2%? If changing the code means building a useful new feature, then you’re clearly adding value to the project. If you’re simply making it more elegant, then that’s nice, but is it really indispensable?
Finally, before making any changes you should always ask yourself, do I have a robust testing architecture for my code? This should be a given, but sometimes you may find yourself dealing with inherited code that comes without that sort of architecture. In that case, the order should be to build your tests first and then implement your fixes, lest you find yourself with all sorts of unexpected, intractable bugs. If you don’t have the time to do both things, then you don’t have time to ‘fix the code’ in the first place.
If you have considered all of these criteria and determined that changing the code is time-efficient and worthwhile, then go for it! You should always look for ways to make your code as easy to read and to maintain as possible.
On the other hand, if it looks like you’re setting yourself up for a time-consuming process which is as likely to create new problems as it is to solve the old ones, then that’s when you should listen to popular wisdom, and simply don’t fix what isn’t broken.