Design Debt "Design debt can be hard to spot. If you see these problems, your project is already in serious trouble:
* Your team describes the software as "kludgey" or "hacked-together."
* The team often encounters unexpected problems.
* Each change results in a cascade of new defects.
* Defects thought fixed frequently reappear.
To avoid the above level of debt, make debt elimination a priority:
* Reduce schedule pressure.
* Establish a culture of design quality.
* Utilize refactoring, continuous design, and other code-quality practices.
* Make debt reduction a continuous process, not a one-time activity."
When I was working on Kowari/TKS I remember Simon mentioning his idea of the three stages of software:
* Stage 1 - New easy to understand, easy to add new features, classes, components, and frameworks are largely self contained. It's easy to estimate the time it will take to make changes.
* Stage 2 - Difficult to understand, changes have a larger effect that you expect - breaking tests that you don't expect, classes are generally a little too coupled. Generally a reasonably good programmer can achieve meeting deadlines as long as they make "pragmatic decisions" not to fixup all the code.
* Stage 3 - The code is basically impossible to understand as single classes - the voodoo stage. You change something and it breaks something else that appears to be completely unrelated. Making predictable changes is no longer possible.
The obvious answer is to rewrite but this is just what several people have suggested not to do. For example, in one of the Joel on Software articles, "Things You Should Never Do, Part I" he mentions: "It's harder to read code than to write it...This is why code reuse is so hard...When you throw away code and start from scratch, you are throwing away all that knowledge. All those collected bug fixes. Years of programming work."
A possible way to solve this is to prevent it from occurring. The focus on TDD is often on the testing part of it but it's the design part and the idea to explain intent and reduce the time reading code and increase the time writing it.
In, "Test-Driven Development Isn’t Testing": "Once you start looking at your test cases as a description of the software design, they start to look different. Pay attention to the names of your tests. Instead of writing tests named after a method on an object you're testing, try using the test name to capture the intended consequences...There's at least as much code in the test case as in the actual class being tested.
I guess this makes it sort of "design and test," but in the end we still haven't ensured our object is bug-free."