How long has it been since you faced the frustration of fixing bugs or adding new functionalities into a nasty project full of bad code? I’ve started to work on one this week. The thing is that the project itself is fun and innovative, but the code.. oh gosh.. the code is awful, full of thousand lines files, code smells, bad naming and a long, etc. Is not like I haven’t written code like that in my first years, but hope you understand my point.
When I read the code I felt annoyed. At first, I was tempted to make the changes as quickly as possible, keeping the same line of careless coding the previous development team introduced. Then I thought about all the things that could go wrong like introducing new bugs and eternal debugging sessions; keeping in mind that this is a new project for me, I’m just learning about the main domain rules.
Refactoring is the way to go?
After some deliberation in my thoughts, I came to the conclusion that I should refactor the code before adding new changes. And I know it is so obvious it could not be another way, but I’ve been struggling until I accepted this fact. Anyway, a bigger problem appeared: there was not a single unit test in the solution.
By chance, have you read “Refactoring: Improving the Design of Existing Code” by Martin Fowler? If you didn’t, I strongly suggest you do! Fowler explains that you can’t refactor code if you have no tests. Because if you happen to do so, you wouldn’t be confident you’re not breaking functionality. And refactoring does not imply breaking the functionality in the process; otherwise is not refactoring, is mostly deleting and redoing.
So that was my problem: no tests and no possibility of refactoring (at least in the Fowler way). But Fowler considers a situation like mine in his book, only that he recommends reading another book to know how to proceed: “Working Effectively with Legacy Code” by Michael Feathers. This book completely changed my mind about unit tests.
Along with my career, I’ve had the opportunity of learning how to write good unit tests. I’ve learned about how to identify testing scenarios, testing data boundaries, reducing test fragility, decoupling, mocking vs stubbing, TDD (I’m convinced writing the test first is beneficial for specific reasons), testing the tests, etc. I’ve applied this knowledge in Clarika, and made an effort to promote it with my colleagues, and the benefits were noticeable. Nevertheless, there were times when I’ve just avoided unit tests, to be honest, and mostly because of laziness or deadlines. But now I’m strongly decided to never push legacy code again.
Stop writing legacy code
Yes, you’ve read right, I’ve said legacy code. Because Feathers taught me that code without unit tests is legacy code. And it makes a lot of sense when you think about it. What do we generally consider legacy code? Well, we can say is old bad written code we receive from another programmer (and that programmer could be our past self). But that’s not the whole picture. Legacy code is about code you struggle to read and understand. And, more important, legacy code is code you are reluctant of making changes in.
In other words, as soon as we push our shiny new code (without unit tests) into a repository, it automatically becomes legacy code ????.
“Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.” – Michael C. Feathers
Then, how do we make sure our code does not become legacy? By writing clean code, we do our most to make our code readable for newcomers, to decouple our components by using abstractions so it’s easier to change. All that’s good stuff. But how do we prove our code is free of rot at all? Have we convinced our concept of clean code is the best for everyone? Is clean code assuring by itself that newcomers will be able to introduce new changes with confidence?
Unit Tests to the rescue
The answer is no. And here is where unit tests come into play. No matter how good we think our code is, it’s not sufficient to give future developers or our future selves peace of mind. Unit tests are the only way of supplying reliance to other people. And we talk specifically about unit tests because they’re fast and can be run frequently in a matter of seconds (a few minutes at most).
Feathers book talks about the techniques required to go from legacy code to good code. It’s not an easy task, but his advice certainly makes it more bearable. I highly recommend it for anyone who’s facing a situation like mine and sometimes feels overwhelmed. It’s helping me a lot with my new project and is such a pleasure to see how it’s becoming a more robust software day by day.
My new commitment
I feel bad for those people how had the bad luck of working with any of the code I’ve left behind uncovered because I know how it feels to be in their place. Programming should be joyful work, and I really want to contribute to that goal not only for my future self but for my colleagues. Let’s make a commitment to never write code without tests ever again until there is no more legacy code! As if that were possible ????.
Ready for your next software project? Our team will turn your idea into a successful product! Contact us.