The rails project I’m talking about is over three years old and has seen commits from 27 developers in that period. These developers were both co-workers, freelancers, off-shore developers and designers of different levels of expertise.
Technical Debt Inventory
Needless to say, as most projects of this size and age, this one has plenty of technical debt. Let’s make an inventory.
The test suite takes approximately eighty minutes to run. These are all RSpec tests, including features. Luckily we can split the entire suite up into smaller parts using Travis, but still the entire thing takes about forty minutes. The main cause for these slow specs is a lack of understanding about how to write good tests. For example, testing if search and pagination works, someone thought it fine to create fifty ActiveRecord objects in a before block. These big offenders are easily remedied. Others are a bit harder, as some specs require an elaborate tree of models to test functionality. This project has no cucumbers to discuss and review features with Product Owners. These cucumbers would have been awesome to discuss features both with product owners and team members. But at the start of the project cucumber was deemed too much hassle and Rspec feature specs were used instead. The main issue now is that the line between features specs and unit specs is a thin one and people often mistake one for the other. This is probably one of the reasons so many specs create many models and make the entire suite slow. There are parts of the code that are not well tested - or not at all. This is a dangerous one. Because, from the start, nobody monitored test coverage or did proper code reviews, there are patches of code that are not well tested, and there are some that are not tested at all. Pressure to finish a product has led to to cutting corners in code quality and test coverage. Lots of smells and lots of noise. All the points above indicate there is a certain amount of technical debt in this project. Another pointer are code smells. To name a few I’ve seen over the past few months:
- Long methods
- Conditional Complexity
- Data Clumps
- Alternative Classes With Different Interfaces
- Indecent Exposure
- Uncommunicative Names
- Divergent Change
- Shotgun Surgery
- Lazy Classes
- Inappropriate Intimacy
- Train Wrecks (or Message Chains)
- Feature Envy
All of these sneaked their way into the project.
Why does this matter?
The product we ship works. Although the specs take a long time to run, and there are some code smells in there, the suite is actually green and we have confidence the product works as expected. So why complain about technical debt? In my opinion as a software craftsman, software should
- be clean and readable
- allow for change
- be well tested
Well, the code works. But clean, readable, easy to change and well tested are debatable. Responding to change is still possible, but it’s a bit more painful than it should have been. There are some untested patches of code that we would really would like to see tested. And of course, code should be readable and easy to understand. We don’t always have these things and it’s holding back progress sometimes
So, you have some Technical Debt on your hands
Recently two new developers joined this project and they immediately asked the right question: What can we do to make this project better? In my opinion, as a software developer, this is the one magic question. What can you do, right now, to make this project better.? Besides the fact that you ask yourself what you yourself can do, there’s another important component in that question: better. It’s not about fixing all the things. It’s about improving the things you touch just a tiny bit. Over the course of a few weeks or months these small improvements start to add up and really make a difference. It’s often called the Boy Scout Mentality. Leave things behind in a better state then how you found them when you arrived. So, although the projects works fine, we as developers are now constantly improving the code and specs we touch.
Eliminating Technical Debt
Just like 100% Test Coverage, Low Technical Debt is not and end goal, but an ongoing mission. Your application will always have some form of technical debt. Even if you have the best programmers in the world, it will happen. What you can do is focus on minimizing technical debt. The team currently working on this project now has formulated a few basic guide lines to help us bring down the technical debt of this project. We are not going to do it over night, but in a few months time we will have made quite a dent.
The overall objective is this: Whatever you do, leave it in a better shape than you found it in. This goes for code, documentation, specs, whatever. When implementing features or changes, we use a few simple steps to help us along:
- Is this code I’m about to change well tested? If not, fix it now. Update those specs to be more efficient, handle the edge cases you see popping up right now. Write cucumbers.
- With your improved specs green, refactor the existing code Your new specs suite is fast and elegant. Use it to refactor the existing codebase to make change easier.
Congratulations, you have just made your life much easier. Note that you have not written a single line of code yet for you new feature or change. But the cleaning up you just did will greatly benefit you and the time it takes to write this feature.
- Write specs for your change or feature This is quite easy now, as it should add nicely to the changes you made in the first step.
- Implement your features or change Because you refactored most of the code smells out in the second step, you should be able to easily change your code.
These are four easy steps to minimize technical debt in your project. You should not wait until you hit the technical debt limit of your project. Minimizing technical debt is just as important as writing tests. It should be a team priority from Day 1. Minimizing technical debt is not all too different from doing proper test driven development. It requires rigorous discipline and skills. And just like with tests, you will not handle that technical debt after this crunch period. Next time you open a project, ask your self, “How can I leave this project in a better state than I just found it?”