I took stock of the year’s progress on one of the projects that I monitor for technical debt. The 70 developers were under considerable pressure to sidestep writing unit tests for all the lines and branches they added. The team was adding code at a rate 40% higher than last year. Furthermore, Sonar wasn’t fully functioning for the dot Net part for most of the year.
However, at the end of the year, 45% of the developers had no debt. This means that they covered all branches and lines and added no static violations (PMD, CheckStyle and FindBugs). Most also increased the quality of the programs in addition to avoiding debt.
So, although not TDD, at least no technical debt for nearly half even under project pressure. Admittedly, many paid back the debt after code release but at least the debt was paid back. The rest will be paying back the debt early this year.
Everyone seems to agree that programs shouldn’t be too complex. However, for technical debt purposes a specific complexity limit needs to be set. Sonar sets the limit at 60 McCabe Cyclomatic Complexity points. The Software Engineering Institute sets it at 50. It considers programs larger than that to be virtually untestable.
In my experience complexity correlates to defects. One logistic regression model I built with actually program failure data shows a near 100% chance of failure when complexity exceeds a certain amount. Troster in 1992 and Craddock in 1987 report the same.
Complexity is a coding metric but can also be a proxy for design issues. In fact it’s really the only design metric available in the C# Sonar plugin. So tracking the complexity metric in that environment serves to reduce defects and also to improve design quality. In a Sonar Java environment there are other design metrics like cohesion and coupling that can be used to better target which complex programs should be re-factored.
Unfortunately, complex programs can easily become a large part of the code base if they’re not monitored from the beginning. A large program attracts other code like flies as the inevitable one-line changes are made to the program. The maintainer doesn’t really know what the program does. They just find a safe place to insert the method or line, do a happy path test and declare victory, tip-toeing away from the class before it blows up.
In a new one-year old 1 million line system, only 3% of the classes exceed the 50 cc limit. That doesn’t seem so bad until you realize those 3% represents 34% of the code base by LOC.
It’s hard to remediate that kind of debt. Programmers resist re-factoring a class they don’t understand, especially for a one-line change. But you have to start somewhere. I usually run the cohesion metric, LCOM4, and see if there’s a natural break in the program structure. Eclipse allows methods to be extracted, which helps in the re-factoring. Train developers on safe re-factoring using the good resources out there on re-factoring.
Moral of the story for class complexity? Like voting in Chicago, track it early and often. Re-factor and fix it as you go.
The closer code comes to shipping, the more tech debt should be reported in a targeted manner.
Today a new release was cut along with new technical debt that was branched with it.
Shipped code is a natural time to take stock of recent technical debt. Why? Because debt should be minimized and shipped code with debt should also be minimized.
By reporting debt to the class, metric and developer level the debt was made actionable and visible. Such tech debt reports were always available during the sprint but often take back burner to seemingly more pressing development, But the targeted reporting by leads and reviewers to management started 9 days earlier. At that point 33 developers had debt. By the time of the branch, the targeted reporting had nearly halved that to 18.