In my first three installments of this series, I discussed:
Please read these first three to get the full context.
Part 4: Constantly Improve the Design
The inverse of this guideline is to never improve the design. Sadly, I’ve seen my fair share of systems where the design is never improved. Instead, new features are wiggled into the existing design, even though there really wasn’t room. I’m sure you, the reader, can recall an application that grew on it’s own without a critical eye on the evolving design, and eventually, the application is bankrupt and has to be rewritten. I like the analogy of the credit card. There is only so much you can charge up on the card without paying for something. If the indebtedness continues without restraint, financial bankruptcy occurs. In America, law allows the wiping of debts and a fresh start (with horrible credit). In software, every shortcut is a charge to the credit card. Every feature added “around” existing code (because of fear of changing existing code) is a charge. Every assumption proven wrong, every unrealized assumption, every smelly class, every method with a cyclomatic complexity above X (pick your threshold) is a charge to the application credit card. The design left to itself will run up a huge technical debt and eventually max out the credit card. At that point, even if you try to pay down the technical debt, the payment will be less than the monthly interest charge, and the battle is lost. At that point, management calls for a rewrite, and everyone rushes in to write another application. . . using the same habits and techniques.
Recognizing Design Decay
It’s quite easy to recognize design decay. Refer to this article on code smells for a little background. If you smell something in the code, don’t be afraid to investigate. Look for the root cause of the smell. If the smell is duplicated code, look for a way to refactor out the duplication. The key is to identify the design problem so that it can be discussed. In a team environment, it’s important to discuss smells as an opening for improvement. Weird bugs are another symptom of design decay. If the system attracts strange bugs caused by unrelated code in other parts of the system, there may be unintentional coupling. Confusing code is a smell. If you can’t look at the code and know what it is doing, it may be rotten code. Well-designed code will read like a flowing sonnet to a programmer. Decaying code won’t read at all and has to be stepped through (in a debugger) just to get a feel for what purpose it serves. For those not using Resharper (for C#), you don’t know what it is like to know exactly who is calling a particular public method all the time. You do a compile to find things wrong with the code. Do a small experiment: download the Resharper trial and open an existing solution with it. Next, go through the code file and go through the warnings Resharper flags. It will find private method that are dead (not called by anything), member and local variables that aren’t initialized or even used, etc. Resharper has on-the-fly code checking to keep you safe from most code bacteria so that you can focus on the actual design. In conclusion, always be on the lookout for code that is calling out for improvement.
Don’t Fear Code Changes
In many programming shops, this is easier said than done. If your software has not automated tests, there is cause for fear. You have no feedback. If you make a code change, it might take a long time for you to know that the change didn’t have any side effects. Fear of changing code is a design smell. It means that you don’t fully understand what the code does. If you understood completely what the code does, it would be trivial to change it. The solution to this is to implement practices that make this fear go away. If you had test coverage for the code in question, it would make it easier to change. If the code were simpler (a 10-line method instead of a 300-line method), it would be trivial to change. Not fearing code change might not be an option when confronted with a mountain of technical debt, but conquering this fear is key to producing the software world. Applications that are write-only end up rotting and dying. Software that is easy to change can endure for a long time.
Use every opportunity to improve the design of existing code. Even a little technical debt is bad and incurs interest charges. Refactoring is not an investment in your code, it is a payment. Slinging code on the credit card runs up technical debt. Refactoring actually pays for it. Paying as you go is the best balance for a sustainable software product. Refactor to get your technical debt to zero, and constant refactoring will ensure your monthly balance is paid off every month even if you do encounter a small amount of technical debt from time to time.
Improve the Design
Wasn’t that the point of this article, you say? Yes, but it is hard to do if you are used to living in a world of technical indebtedness. Your team may be so busy producing code that you don’t have time to improve the way to produce code. <analogy>A lumberjack sawed away at a tree all day. Another sharpened his saw after every tree and delivered several trees in a day</analogy> The smart lumberjack took time to improve his methods and saw productivity soar above his peer. Are we sometimes to busy doing things that we can’t find time to improve the way we do things? This is counterproductive. Producing software has to include constant retrospection to identify things that need to be improved. If aspects of the software are hard to work with, take the time to improve that portion of the software so that you can move more quickly through it. We can talk about improving the design all day, but eventually, we actually have to do it, and it may include throwing away or replacing some code. Don’t consider this a loss. Lines of code don’t cost anything. They just represent the design ideas that you spent so much money fleshing out.
Research and Development
Car manufacturers have entire departments dedicated to finding better ways to do things. If a car company spent all its resources producing cars and no time finding better ways to produce cars, it would quickly go out of business.
Apply this lesson to your software team and constantly improve your design.