Non-technical reasons why your codebase is a mess

Normand Bédard
9 min readOct 29, 2021

--

(Une fois de plus, merci à Jason Tan pour son peer review!)

As a senior software developer, I sometimes like to take a step back and think about how I can influence people around me to develop their skills to level up their game. And not just their technical skills. A lot of time is spent outside typical programming activities. Workshops, brainstorming, planning, backlog refinement, daily meetings, sprint review retros, etc. Some of those activities are strongly related to technical tasks, but not all of them. And non-technical tasks can have a significant impact on the codebase.

Over the past years, I’ve worked hard to collaborate to find ways to increase our velocity. In the different teams that I’ve been in, but also in our department as a whole. But recently, I’ve been asked for my opinion about the reasons that we could not deliver faster. And that question has been stuck in my head ever since.

During the last 11 years, I’ve seen a lot of staff turnover (developers, managers, directors, VPs, etc.). And over those years, these same questions about the global velocity remain. I am sure that all businesses try to solve that same problem. If it is not the case for you, I think you should stop and do some self-reflection because there are always ways to accomplish things in a better fashion, to increase productivity.

There are many technical reasons that can help to explain lower velocity. I could write a whole blog post about that. But I prefer to highlight non-technical reasons, because they have a bigger impact over time, they are more subtle, and they are easy to avoid because of their complexity.

Not building the system in a scientific and methodical sequence

This is a controversial subject, because everybody knows about concepts like market shifts, client needs, opportunity windows, business vision, etc. And since those concepts are the reasons that a business is successful, they are rarely challenged.

The thing is, building a system in a scientific and methodical sequence (a.k.a. the technical sequence) is not always aligned with the sequence that matches the business requirements (a.k.a. the non-technical sequence). And when this clash occurs, guess how the work is typically prioritized. A good but not exclusive example of this is when a missing capability in a system needs a specific refactor to properly adapt the model or architecture, but that refactor is scoped out/deprioritized.

I need to be fair by saying that the non-technical sequence can be the best choice, even most of the time.

But not by default.

And not every time.

With a short-term vision, it can be appealing to prioritize the non-technical sequence, because it is easy to see the concrete benefit rapidly and translate that to direct or indirect money income. However, measuring the negative impact (and at the end, the cost) of that decision is hard to quantify upfront, and technical people are not always the best to communicate this in a way that the business will understand. And since the negative impact can be visible months or years later, it can be hard to track down the initial cause and then to finally take a step back to analyze whether it was a good decision or not.

Over time, building things in the non-technical sequence by default adds technical debt and complexity in the system. Over a several year period, you can finally end up having slowed down an entire department to the point that you are just not able anymore to talk about opportunity windows in the market. At that point, hiring new developers to increase your manpower cannot solve the problem.

Priority changes

This is similar to the previous point, but the causes and the impacts are not the same. Building an enterprise system can take years, and over time, priorities can sometimes switch quickly because of various business reasons. In a perfect world, each closed story should bring customer value and ideally keep the codebase in a clean state. But during complex refactors or implementation of new features, at some point on a timeline, the codebase can be in a temporarily invalid state. Scoping and planning work in sprints can imply taking 3 steps ahead, then 1 step back, then 4 ahead. Some argue that fixing this step back should be included in the related story, but sometimes it just does not make sense to do that.

Problems arise when an ongoing project/priority is halted because of a new one. Even if you take care to document the current state, the reason behind it and the next step to fix it, there are chances that this work will never be done in the end. Or there are chances that during months, new code will sit on top of that temporary code, adding exponential complexity and confusion. If you do not have the collective discipline to correctly finish a project by tying up loose ends before starting something else, this can lead to a serious mess. At that point, every new piece of code is just more complex because you need to handle use cases that you should not. And this has a massive impact on team velocity. And I am not even talking about the higher risk of introducing bugs.

Not having a proper balance between junior and senior developers

Measuring the performance of a developer is hard. I’ve had a lot of discussions about this over the past years. There is an oversimplified statement saying that senior developers are more productive that junior developers. But it is not as simple as that.

Junior developers can be more focused than senior developers and deliver more code and features in the end, because senior developers can have many other responsibilities. On the other hand, junior developers can be overwhelmed by the big picture of a project and might only be able to focus on their granular tasks. And it’s okay like that, it’s the normal learning curve and the more you progress in your career, the more you can handle complex problems.

Therefore, it can be interesting to evaluate performance by the rate at which someone makes mistakes / adds involuntary technical debt / adds bad coupling / etc., even if the code is working and the acceptance criteria are fulfilled. And this is what separates senior from junior developers. And don’t get me wrong, senior developers make mistakes too, just less often. Having only junior developers can work for some time, but because of the rate of mistakes, the codebase won’t scale over time.

Many reasons can explain why an enterprise have an unbalanced ratio of junior and senior developers. There is a global software developer shortage, and the reality of senior developers is even worse. But I heard a good analogy in the past: 9 Women Can’t Make a Baby in 1 Month. At some point, 9 developers can produce more code than 1 developer. But not 9 times more. Velocity versus number of developers is not a linear relationship. If an enterprise systematically prefers to hire 2 junior developers at 50k instead of 1 senior developer at 110k thinking projects will go faster, it won’t be the case in the long run. And the word systematically here is important because sometimes you need to hire juniors and sometimes, seniors. The key is a proper ratio. And when that ratio in not properly balanced, team velocity will drastically decrease over time because of the code degradation.

Systematically deprioritizing technical debt

Technical debt is sometimes a necessary evil. It can help to deliver features periodically in a more Agile way instead of just paralyzing an initiative for 2 months for non-critical concerns. Adding technical debt should be a conscious choice, which allows the team to understand the reason behind it, how it helps them to progress, what the next steps are and when that work should be done. Of course, when the technical debt is an unconscious choice made by mistake, then you are in trouble.

Technical debt can create big problems in a codebase. It can lead to the infamous Big Ball of Mud, causing velocity problems, application deployment pains and unexpected side effects/bugs.

At some point, the team velocity can be so problematic that the only perceived solution is either to hire more developers or to go all-in for a complete rewrite of the application (which is an entire blog post subject). But without changing the mentality about technical debt, all solutions are just temporary, and the same problems will come back again.

Prioritizing technical debt is not an easy task. At first, it is the role of the team to properly explain what the implicated risks are; no managers will do that for the developers. As professionals, it is our responsibility to do our due diligence to protect the code. But again, it is hard to oppose technical debt against new client features that bring in money. Both can have a big impact over time. The key is to have a strong technical voice in the decision-making process. Ignoring technical debt does not scale in the long run.

Having a reward system based on respected delivery dates

Being able to adequately plan a roadmap requires proper estimates. In some situations, adding pressure to deliver software at a specific date makes sense. For example, the success of a video game can be drastically affected if the launch date misses the holidays. Target dates can also be an option to control the cost or the return on investment in other contexts. Or it can also be completely arbitrary, just to be able to start another project right after that finished date. You probably saw many other reasons during your career.

When pressure is added to respect a delivery date, there are repercussions. In the best cases, only the scope is reduced. Problems arise when the code quality is compromised. And it won’t necessarily be in a way that developers voluntarily code in a worse manner just to be faster. Instead, it could influence people to systematically choose the shortest solution when they face problems. These shortest solutions could be implemented in a clean way, but have drawbacks in modularity, testability, robustness, coupling, etc.

Trying to systematically respect initial estimates just for the principle of respecting them 4 months later is also another way to influence developers on focusing on schedule instead of focusing on code quality. Sometimes, unexpected problems or complexities arise along the way, which makes estimates impossible to be respected. Or sometimes, people make mistakes in their planning or estimates. But bad planning skills should not result in bad code to compensate for those lacking skills.

But the real problem is when people make a habit of using those strategies because they are rewarded for having successfully completed a project on time. From a management standpoint, it could be difficult to tell the difference between a top performer and someone who just rushes things to make it work at the end. And it can be even more difficult when team reorganizations occur frequently.

But over time, developers’ velocity will slow down because of those cheats. Even if it is the reward system that silently (and sometimes unconsciously) pushed them to that path. And in the end, it is the developer’s problem. And fault. And if managers are rewarded too because of what the teams deliver on time, it just creates an endless loop.

So, what is the solution to all this?

There is no simple answer. Even if the previous examples seem to pit technical people against non-technical people, they work for the same business when the sun rises.

Technical people can be too close to the code and miss the big picture of the market. Technical people are also able to self-generate infinite technical work that brings nothing to the clients, just for the purpose of achieving perfection.

Non-technical people and managers are too far from the code, even if some of them were developers in the past. Good intentions and great ideas can turn into a disaster at the implementation phase if the codebase is not healthy and if they don’t consider technical people’s fears and concerns.

In my humble opinion, I think that the key is to take the time to have those challenging discussions, without anybody thinking upfront that they are right and the others, wrong. All this is based on mutual trust. In the end, technical people can understand client needs, market shifts, HR complexities and project management challenges. And non-technical people can understand the risks of bad software development practices and anti-patterns. If a proper balance is not achieved, problems are guaranteed to happen, regardless of the side that the balance is leaning towards.

And if this happens, everybody loses.

Truth is never owned by a single group of people.

--

--

Normand Bédard
Normand Bédard

Written by Normand Bédard

French Canadian senior software developer for SherWeb since 2010. Ultramarathon, drones and camping enthusiast!

No responses yet