I went to a really neat workshop on ‘Code Debt’. The idea behind this term (as I see it) is that you should constantly maintain your software. When you make a change, but don’t take the time to refactor the code to play well with your change, you incur code debt. You didn’t ‘pay the price’ at the time so it stays around. As I read one some blog once, I think developers understand what this phrase means when they hear it, even if they’ve never heard it before.
It’s really easy to incur code debt. Who hasn’t had to fix a bug where adding the one-off change takes a minute, but “doing what you should do” would take hours? You have some edge case that just doesn’t move in the same way that the rest of your abstraction does. You’re releasing soon. Hey, it happens.
But just like keeping a monetary debt costs you over time, so does having a code debt. In the workshop, they illustrated this in a very vivid way. They broke the class into two groups, and asked each group to add a small piece of functionality to a bit of code. They gave us all party poppers and told us to pull them when we finished. Within five minutes, people on team A started popping their poppers. No one on team B did. Afterwards, they showed us the two code bases, and we all knew why – team B’s starting code was *awful*. I mean, really bad.
I should stress that both code bases were developed using TDD. Both had tests that fully passed (except for the cases where we were adding the new stuff). But when the presenters had written the ‘B’ code, they started from one requirement, and as they added new ones, they just added new cases – no refactoring. They didn’t really change existing code – just tacked on more stuff at the end of the function. They took us through the history of developing it. In each case, you could sort of tell where they were coming from. “This is just a little different from the other case – I’ll just add a boolean to change between the two cases.” By the end, you couldn’t make heads or tails of it.
The ‘A’ code, on the other hand, was really clean. They added had added functionality bits one at a time (just like ‘B’), but when things got complicated, they refactored to make things more clear. They got rid of duplication. They abstracted out the bit that changed between the cases. And the code at the end was great. But more over, it was easy to modify. What they showed us was that while they may have been able to write the ‘B’ code faster, that productivity wasn’t free – we *had* to pay that debt some time.
They went on to talk about that there is a continuum on this debt scale – from “aware of debt” to “critical of it” to “addressing it” to “managing it”. The last stage was “unencumbered by it” – the state at which there wasn’t any code debt that held up developers from making changes. Which I think was pretty clearly illustrated by the exercise.
I really like the code debt metaphor, because I think it makes things clear to people, even outside of development. Telling someone that the code “smells” doesn’t really mean anything to them. Telling them that we are incurring debt is a lot clearer. Plus it is really true – writing code later really does cost more because of it.