Everyone eventually rewrites either a small piece of code, an entire application or framework or refactor their work, and this process is in a constant cycle. You notice this everywhere, from small applications, libraries, frameworks and even Enterprise Software. Should we be concerned? Why is this such a common theme?
For Applications, one of the primary reasons for refactoring or rewriting is technical debt, so let’s have a look at some common reasons for technical debt, and I attempt to propose some solutions to each point.
1. We have Deadlines
All businesses have targets, and to achieve that, they all require some expected time of completion for any type of project in order to compete better in their market or for some startup’s, be the first in the market. This means there are going to be areas of the system that will be compromised one way or another, at both high-level (architecture and design) and low-level. Does this mean we have to accept an unrealistic deadline? Depends what we mean by Realistic Deadline, it’s a fuzzy word and isn’t very clearly defined in the realm of Software Development. A Product Manager’s understanding of “Realistic deadline” may be very different from what a Developer has in mind.
Management style may differ from company to company, in one extreme, the Developer’s are given 100% freedom to find the best solution to a problem with no set deadline, in another, management dictate deadlines for the Developers.
So, is leaving a Project without a set deadline not a good thing? Wouldn’t we end up with the best end-result?
It really depends. Some Developers are great at managing themselves and solving problems with a reasonable solution and still within good time, but there are some that may be too indecisive and need more guidance as they can get into a “paralysis by analysis” phase. A catch-all solution would be a balanced management approach, whereby Managers get to make the big decisions and freedom for developers to propose solutions and argue their case. Although, I would hope for more catered approach where each Developer’s management and technical skills to dictate the management style imposed upon them.
2. We don’t truly understand the Problem
Writing good software is mostly about understanding and solving the right problem. But here’s the thing with many of Application’s that are produced. Vast majority of the time, we don’t understand the the problem. We can never truly appreciate the requirements. There are some ways to get around this, such as spending time with people that will be using the system, shadow them and even take on their role for some time. This type of solution is rarely implemented however, as the cost involved can be unrealistic for most organisations.
We have different methodologies, such as Agile -which use a more iterative approach, increasing the chances of meeting all requirements greatly over the old Waterfall approach.
Here’s the reality of things:
- No one really knows what they’re doing until they get feedback from ‘real users’
- We don’t understand the domain
- Writing good software is all about solving the right problem
- Vast majority of assumptions are wrong until proven
- Our code is merely an expression of our solution to a problem we don’t completely understand at that time
- Ambiguity is always something that will always exist up until the day that we can just read people’s thoughts
- We never really have enough time to truly understand the problem
The main issue that is seen with agile approach is the bad execution. In most companies, design becomes an after thought, favouring product delivery over the right approach.
3. There are many unknown’s
The problem with Developing Software is that there are many unknowns, and to make it worse, there are two types of unknowns, there are the unknowns, and there are the “unknown unknowns”.
There are unknowns, and there are “unknown unknowns”
What are these unknowns?
The unknowns are some problems that we are aware of, but we don’t really have enough information to know the exact solution to the problem. These type of unknowns can usually be uncovered by doing research, documenting the findings and writing a quick prototype as proof of the solution.
What are “unknown unknowns”?
“Unknown unknowns” are problems that we will encounter, but we are not yet aware of them… or maybe never will. This can range from simple problems or a complete blocker.
The problem with these type of unknowns is that they can completely skew the development time by a large margin. This is why there is a multiplier used to make headroom for these unknowns. I would not really call multiplying the estimates by two as a complete solution however, but it’s a neccesity in this case.
There are certain things we can do to uncover these “unknown unknowns” during early phase of Development, this can include prototyping, writing user stories, writing acceptance criteria, mapping user journey, documenting the systems architecture and design and doing research. Although these things can be done, there is no solution that guarantee’s that all “unknown unknowns” are uncovered, simply because there are too many possibilities – if there was a way, we would have large-scale applications today that is built exactly within the estimated time and have zero bug count.
4. Design isn’t part of the process
One of the problems when focusing too much on delivery is that we sometimes forget that design and architecture is part of the development process. We don’t get to see the various options we can go for. When this happens, we tend to start off with good velocity – getting things done much quicker than most Developers might expect. But this type of situation can only last so long until it catches up to you. The speed of feature development will start to slow down and even potentially halt it completely.
Think, before you do. Here’s some quick design steps we could do to help us think a little bit about our solution.
- Write your understanding of the problem
- Write them down as smaller set of problems, if possible
- Write some technical specifications
- Write down technical challenges
- Draw a few diagrams
- Write a quick prototype and watch for the “unkown unknown’s” to show themselves and further technical challenges
Of course, you may not want to do process for trivial problems such as updating for updating a small piece of algorithm in a function, that would be a bit silly as it requires no design – it’s all about being pragmatic.
The other issue you might come across, (as referred at point 1), are the deadlines, as our primary focus is delivery and we simply neglect everything else.
5. Language Ambiguity
Human Languages, (or the ones that I know of), have too much room for misinterpretation. This can affect many parts of the Software, as the entire Software Development Life Cycle revolves around communication heavily. From the process of requirements gathering, writing specifications, design, and architecture, implementation, raising bugs, fixing bugs etc.
Communication is such a large part of successfully delivering any type of Application, in fact, even the implementation’s primary purpose is to communicate intent to ourselves and our peers – it’s not just about getting it to work, it’s about being able to communicate your solution in the nicest way possible. I firmly believe being able to communicate thoughts to both people of both technical and non-technical background is one of the most valuable skills a Developer can have.
6. “We already use this pattern” syndrome
When we use specific patterns that may or may not be the right solution for the job, and without actually considering other options is that we tend to stick to the pattern already used in the codebase. If the current codebase is in bad shape, then the subsequent code added to the codebase will also be in bad shape. The codebase should always reach a certain standard – leaving it in a bad state for a certain period of time without fixing the underlying issues will cause a snowball effect of adding more code smells to our codebase, and eventually, the nightmares will start.
It’s up to the more Senior Developers and Managers to set an example for the more Junior Developer’s to follow.
7. Human Error
Computers make no mistake, it does exactly what it is set out to do with near 100% consistency, bar a hardware failure. Humans on the other hand, aren’t as perfect – It’s easy to see how this can lead to technical debt.
There are of course various ways of preventing human error. In the context of actually writing code, there are tests, pair programming and code reviews.
Let me know if you have any thoughts, or if you have any interesting stories of things that lead to bad accumulation of tech debt.