After listening to a recent .Net Rocks show about Technical Debt I thought I'd share some tips I have on working with technical debt.
I worry if "Technical debt" is just a label we put on things so we don't have to think about them. e.g. "We know this will create technical debt and we can come back to it at some point in the future." This doesn't require that we do the best to mitigate the consequences of that debt now though.
At its simplest, technical debt is just a name for the consequences or side effects of the decisions that are made (intentionally or accidentally) when developing software. Dealing with technical debt therefore comes down to just dealing with the consequences of past decisions. Fortunately, there are lots of ways developing software that make it easier to deal with these consequences and lessen their impact.
- Have automated tests for the system at a high level. Regardless of what you call them, you need tests that verify that the application/system works at a high level, not just at a method/class level. When it is possible to verify that the system does what it should it removes the risks associated with changing how it does it, that is changing the code to remove the debt but without breaking the overall functionality. This is key to avoiding regressions and negative side effects of addressing debt.
- Remember the boy scout rule. (Leave the camp/code better than you found it.) If while working on something you find some existing code that needs improving, and you can do something to improve it without it being a major distraction from the task that was your original focus, you should make such improvements. Of course this depends on a working culture that allows code gardening.
- Allow code gardening. Just as with a real garden where you might tidy up some leaves, pull up a weed or prune a rogue branch from a hedge as part of the general maintenance of the garden, there are general maintenance tasks that can also be done to code. This is very similar to the boy scout rule but with an important requirement that it must be allowed for code unrelated to what you're supposed to be working on. Suppose you were working on Feature Y and it involved modifying Class X. With the boy scout rule, you may make other modifications to Class X that improve it. With code gardening it's also potentially acceptable to make changes in the completely unrelated Class Z. Say you just happened to glance across Class Z by chance and noticed an improvement you could easily make. If code gardening is allowed then you can improve it now. If code gardening is not allowed then the best that might happen is a bug gets raised to improve Class Z at some point in the future. The reality though is that it's a low priority bug and so never makes it on to the schedule. The opportunity to make an incremental improvement to the software is missed and so the slow degradation in overall quality continues. It's important to note that for this to be possible it requires that small frequent check-ins be allowed (not a one check-in per feature policy) and a branching strategy used that allows such small improvements to be easily made in isolation.
- Enforce code reviews on non-trivial changes. You can alleviate the risk of a low bus factor on an area of code or something that no-one working on a project wants to risk touching by using code reviews. Code reviews have a lot of benefits but the important point here is that the person who signs off on a review agrees that they will be able to support it in future, should the original author not be available. This is not about assigning responsibility or blame. This is about ensuring that the original code was clear enough that another developer can understand what has been created. If no one is willing to sign off on a review because they wouldn't be able to support maintaining it - improve the code until someone agrees that they could.
- Use "why-and" comments in code to explain intentional debt. Comments in code are often a bad thing. All things being equal, code should be self-explanatory. You shouldn't need code to explain what a piece of code is doing or how it is doing it. Where there is a general need for comments though is if why a piece of code was written the way it was isn't clear. From a business perspective sometimes deadlines are critical. You may have to put in a dirty hack to get something working to meet the deadline. This is an example of where comments are useful. Just adding a comment that acknowledges a hack (or other form of technical debt) has been made may be useful to help avoid people thinking badly of you in the future isn't enough to actually help other developers or the project. In such a scenario you should add the comment that acknowledges WHY the intentional debt is there AND what should be done to address it. Addressing the debt may mean more time is needed or waiting on a dependency. The important thing is to provide information that will help when someone comes back to work on the area of the debt. The aim is to avoid someone looking at the code and asking "why is it like that?" and also to provide context and information that will help when someone does have to modify it.
Just a few points but they've helped me in numerous projects in the past. Let me know what you think or if you have any other tips to make working with technical debt easier.