Am I Tilting at Windmills?
I don't know if my prior rants on the topic of Technical Debt have provided sufficient clarity. In them, I try to better define Technical Debt and point out the differences between it and cruft. But I do not provide any tangible examples. Herein, I attempt to remedy that situation. Today's installment is in narrative form. I intend to provide a later post with code examples.
Technical Debt - A brief repriseWard Cunningham coined the phrase Technical Debt
"Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite. Objects make the cost of this transaction tolerable. The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object- oriented or otherwise."As he clarifies later in a You-Tube video, Ward was specifically talking about design decisions made in the course of developing software that allowed for more rapid delivery. Rapid delivery not for the sake of meeting a deadline. Rapid delivery to illicit quick feedback, thereby providing the data necessary to adjust the design to be more congruent with actual needs. The object design should change to reflect your current understanding of the domain, even if that understanding is partial. Technical debt is payed back through code refactoring as our understanding of the domain matures.
Ward clearly states, "The ability to pay back debt [...] depends upon you writing code that is clean enough to be able to refactor as you come to understand your problem."
Clean Code is actually a prerequisite for Technical Debt. If you don't have Clean Code, you cannot expect to pay the debt back. If you have cruft, you cannot reasonably incur Technical Debt. In fact, you are not incurring debt, you are merely adding to the existing mess.
Examples (in Narrative Form)
Example OneWe are building a new application to allow our customer to sell widgets and parts to distributors.
- Widgets represent 75% of the sales.
- 82% of the customers order on terms.
- The cross-section of these two groups represents 69% of all sales.
This is a strategic decision. We are creating a design that we know will have to change. We know we will re-visit this code and perhaps even throw away some (or much) of it.
But we write it well, we test drive it, and we do not compromise quality. We will defer to the last responsible moment and make the design adjustments based on what the business determines is the next highest value activity when it is decided.
Example TwoWe are working with a third party API for determination of flood plane coverage for a given property. We want to know this so we can decide if we will or will not provide an applicant a loan. We don't give loans on properties in flood planes.
We can make numerous calls to the service. One, which is simple, will tell us if the property is in a flood plane or not. This is all we need to know for an accept or reject call.
We decide to go ahead with this basic functionality and make sure the service is consistently reliable in terms of uptime, speed, and accuracy. We are going to run one server against the new service and leave the others running against the old (slow and more expensive) service.
Once we've determined the service provider is reliable, we will also code the pieces for garnering more specific data. It would be good to have this on the loan application record if there are any questions in the future, but it is not mandatory.
This is a strategic decision. We are creating a design that we know will be either eliminated or expanded.
We write all of the code well. We test drive it. After a period of learning, we will either toss the code or we will add new features.
CruftWe are working on a system to schedule production of widgets on the factory floor.
We have a bonus initiative to get the application done by the end of the year. It is September and we're just getting started due to delays in the delivery of our prior project. According to the schedule, we should have started in May. If we miss the deadline, there will be much gnashing of teeth. If we miss the deadline, we will not get our bonus. We ask our boss if we can deliver only part of the application, but her bonus is much more significant and requires complete delivery. She denies the request, admonishes us for putting her in this situation, and reiterates the priority of this project for "the business". She let's us know that the CEO is watching this one. The CEO was watching the last project too. That's how important IT is to the corporate strategy.
So we work like crazy. We skip unit testing. We work in silos and make decisions on the fly about ambiguous requirements. We notice the FillQueue class is getting quite large and everything is dependent on it. But we don't have time to change it. It works and we are under pressure to deliver. Besides, we could no longer test these classes in isolation if we wanted to. We'd have to tease them apart at the seams and that could take weeks. It is getting more and more difficult to write the code. It is like a bowl of gelatin; you touch it down here and it wiggles up there? Why does it do that? Whatever. We need to make the deadline.
This is the wrong decision for the business. This is not a strategic initiative. This is not professional or even ethical behavior. This is a failure.
But bonuses got paid.