This is another article in the development series that handles different topics important to software development. Our developers are challenged to describe the things they do in such an understandable way so that non techies can grasp it. One way to do this is by looking for a metaphor that can be applied to a certain topic, such as why 'quick and dirty' code is often not a viable solution. To understand this we bring you into the kitchen The following example is a good debt metaphor originated during development of financial software. The software engineer had to explain a non technical person that assumptions about how the system should work were not entirely correct. If they continued the way they did, they would have to spend more and more time and thus pay more and more interest.
The scrum framework provisions feedback moments after each sprint in the form of retrospectives. During a retrospective, the team sits together and is able to explain that it understands parts of the system (domains) better. It may also be that the team acquired more insight in the goals, needs and values of the user / archetype. Because of this, the team is better capable of coming up with more appropriate solutions. During scrum poker it can be brought to attention that changes will take longer and longer, because of the decisions made earlier. This is why the scrum framework is so useful.
Imagine for example stocking a cabinet in your kitchen. Either you just cram everything in there in a random order or you carefully categorize and order everything. Imagine it’s your new house and you haven't given your stocking process much thought. You will just put in your utilities in random order. At one point you need to use one of those items that is way in the back. Now you have to get everything out first, so you can reach the item you need. In the future you might consider taking a little bit more time in organizing those items you put in the cabinet so you won’t have to take everything out when you need just one thing.
Quick and dirty
Quick and dirty is when software engineers write code that is rushed and buggy. It might be the equivalent of patching something with duct tape; it might seem to fix the problem, but it will look or feel a bit rushed. The technical debt in this example occurs when continuing down this path - the duct tape will almost force you not to think about quality. However, at some point you have to start thinking about a more permanent solution, but you first have to remove all off the duct tape.
Quick and dirty is not bad in itself. For example, a method to quickly test functionality on your user base is to quickly develop it, and see how they like it or what they like about it. The important thing to note here is that it probably results in debt, and that it should be payed off at some point.
Debt is not necessarily a bad thing. When we think about monetary debt, one may be able to start a company by taking a loan. The same holds for a software engineer. A software engineer may be able to ship a feature or bug fix early by acquiring some technical debt. Later on this engineer has pay for it in the form increased development time until the debt is paid of by a proper overhaul. However, similar to monetary debt, as long as the technical debt is managed (this is where the scrum meetings come in), accumulating some debt is no intrinsic problem. Quick fixes may be as important to a client as good software design. A good software developer should be able to do both while managing technical debt. That is, once the technical debt becomes unmanageable s/he should sound the alarm and take the time to reduce the debt.
From a business developers perspective, putting a bunch of junior developers on a project could be a good idea when they release the first feature after the first sprint - they seem to be able to work together and deliver, so why not? In the next sprint the velocity is used from the last sprint. What is quite likely to happen over time is that the technical debt is increasing and the team will find out too late. Both because the junior developers will likely not be experienced enough to recognize bad patterns or otherwise not feel confident enough to suddenly inform the stakeholders of the accumulated debt.
Technical debt should always be payed - no matter what. Perhaps not with the most obvious tool - refactoring, but in the form of user flow optimization. Google Analytics might show that a large part of your UI is never used, or that users don’t know about certain features. This feedback loop from analytics is something that can help your team decide whether to implement this new fancy feature or redoing the one that is already in use. Also team sessions about the domain can help in paying debt - the better the team understands the domain and the user group, the more likely they will recognize opportunities.
When not fine-tuning software or otherwise fully grokking what software does, you are in a way, moving too fast. Doing so for too long will create technical debt. Putting the learnings of working software back into code is essential to making good working lasting software. This fine tuning can come in several different ways. Most obvious might be refactoring, or visually changing the GUI, or A/B testing. The important thing about this is to fully understand the domain and to pay off that debt once in a while. When putting up two cabinets you use nails. Then you stock the cabinets and after some time the nails for one of the cabinets loosen and the cabinet falls. Then we have learned something, namely, we should not use nails for putting up cabinets. The other cabinet may still be firmly attached to the wall, however, by not using our new knowledge on using nails we may run into the same problem again in the future. Instead, we can act on the new knowledge and change the nails for screws. During software development, the team is very likely to acquire a better understanding over time, just like they will build up debt. The feedback loop from sprint to sprint is extremely valuable both for the team in terms of knowledge and experience, and for the product in terms of quality.
Definition of Ready:
Screws are available
Definition of Done:
Cabinet can hold 45kg
Often developers might describe code to be impossible to work with. This can mean that they recognise bad patterns, yet unable to put into concrete words. Most likely what is going on is they have to perform loads of extra (debt related) tasks in order to change mundane things. When requirements are not fully understood, the result will reflect this. It’s not unusual to see implemented code where the developer didn’t have the slightest clue, or to read humorous comments like “this is what the client wanted”, the client will be none the wiser. When the time comes later to make changes to this code, you are not able to relate to the code because it wasn’t written logically, not properly documented, or lacking in code comments. Without these it can become extremely difficult to understand the use of the implementational use of the code. Let's take for example the following example. We’re going to create software for our financial app. In the app we have a user interface that let’s you insert the amount of money of your weekly daycare, the amount of money you spend on food, and the amount for gas. In code we could either write the following:
This is quick and dirty; were done fast so we can test how we like it.
This is the proper way. It’s obviously just a simple example, but if we test the app for about 2 months and wait for the results. We might not understand the numbers in the top example. We were done a little faster then we would when implementing it the proper way, but we would need some time to familiarize again; the second is more expressive and therefore more worthwhile. For example understanding what number belongs to what expense again. As a rule of thumb you could always keep in the back of your head that you write code once, but it will be read 10 times. This is why it’s in everyone's best interest to write good documentation.
Wrapping things up!
To end this article a bit more technical, let ρ the objective ratio between dirty code and clean code in a codebase C. Although, defining an objective ratio might be a problem in itself, for now we assume we know how to define this ratio. Obviously, not taking the extra effort into account, one would like ρ = 1, i.e., the codebase consists of one hundred percent clean code. Moreover, let Ρτ(C)be the perceived dirty/clean ratio by a programmer working on codebase C at a time point τ. Then, Assuming a static codebase, i.e., the codebase does not change over time and ∀τ[ρτ = ρτ + 1], I postulate the following:
Ρ τ(C) < Ρ τ' (C), for τ, τ' ∈ Ν+ and τ' >> τ.
In words, the perceived ratio between dirty and clean code increases over time, even when the codebase does not change. This can be attributed to the increased knowledge of the programmer on the domain or on programming in general. This also transfers to other codebases, that is, a programmer working on codebase Β may acquire new skills or find new solutions to general problems. An other codebase Α, which does not yet use the new favourable solution or technique, now feels old and dirty, however nothing has changed. One may now ask whether cleaning the codebase (also known as using a new favourite technique) is in the best interest of the client.