Why Do Developers Write Bad Code?
Why do developers write bad code? It’s because they ignore the principles of good software design. The reasons for this might surprise you.
Software design principles are important.
The most fundamental principle in software is to write code that can handle change. If code is difficult to change then developers can’t respond quickly to changing business requirements. When changes break other parts of the code base deadlines slip and releases fail. If this happens frequently then a company is in trouble.
As a result of a badly designed code base and the inability to produce a stable product within an agreed timescale, customers start to leave. If a company is not large enough to absorb this loss of revenue, then it starts to make job cuts. I've seen this happen and it’s not nice for anybody.
Software design principles are not just for fun. They protect the reputation and profitability of a company and they protect jobs.
Software design principles protect jobs.
The notion that a design needs to be able to cope with change is so fundamental to successful software that it should be at the forefront of a developer’s thinking process when writing code. But it isn’t.
Why do developers ignore software design principles when writing code?
There are several reasons why developers ignore software design principles when coding. I've divided these into explicit (or environmental) and implicit (or cognitive) factors. By explicit I'm referring to factors that we are aware of. Implicit refers to factors that we’re not aware of.
In this post, I focus on the implicit reasons but here is a brief outline of some of the explicit influences.
Explicit reasons why developers ignore software design principles
In software development, we’re almost always constrained by a deadline. The pressure is on and we feel rushed to get the work done. We don’t take the time to step back and think about the quality of our code. This results in sloppy code and the accumulation of Technical Debt that we will “clear later”. Of course, later usually means never.
2. Lack of knowledge
Sometimes lack of knowledge prevents us from writing good code. The solution is to get that knowledge but not all developers want it. Our job as professionals is to persuade other developers that the knowledge will be worth it.
Our environment shapes our behaviour. Morale and organisational ‘red tape’ affect a developer’s ability to code. If a developer is unmotivated or constrained by strict policies then he may have no desire to write well-designed code.
Another factor that affects a developer’s environment is the availability of competent and more experienced developers. When developers have access to more experienced senior developers a mentoring culture can emerge and the quality of code can improve through practices such as code reviews and pair programming.
Implicit reasons why developers ignore software design principles
What if I told you that the main reason developers don’t write clean code is that their brains don't want them to?
Developers like all people are subject to cognitive biases. A cognitive bias is a type of thinking error that our brains make when processing and interpreting information from the environment.
These biases exist for evolutionary reasons because they simplify information processing allowing us to make faster decisions. The problem is that biases can sometimes lead to poor decisions and bad judgements.
When it comes to writing clean code and adhering to software design principles we fail because of the following cognitive biases. This is not an exhaustive list but a sample of some that influence our thought processes when writing code.
The choice-supportive bias tells us that when we make a choice we tend to feel positive about it even if it has flaws. It implies that when we choose a particular design we fail to see its weaknesses or downplay them because we chose that design.
This is related to the IKEA effect, a cognitive bias that occurs when people place a disproportionately high value on a product that they partially created. If we spent a long time and effort hand-crafting a set of inheritance-based classes then boy are we going to feel positive about them! Our memory of the choices we made along the way is distorted and our brain chooses to remember the positive rather than the negative aspects.
The more cognitive load and decision-making we are tasked with, the greater the likelihood of us being affected by the choice-supportive bias. So activities like coding are particularly susceptible.
We need to be careful that we aren’t seeing our designs through rose-tinted glasses by seeking out the opinions of others and through code reviews and pair-programming.
Priming is an unconscious memory effect in which exposure to one stimulus influences our response to another stimulus. If a person reads a list of words that include the word ‘computer’ and is later asked to state a word that begins with ‘com…’, the probability that he or she will answer ‘computer’ is greater than if they’re not primed.
When we’re exposed to an existing code base with a ‘bad’ design we’re more likely to repeat those patterns elsewhere in the code.
Perform a constructive critique of an existing code base and discuss the design with other developers before jumping straight into coding. If the outcome of the discussion involves any redesign then you'll be primed to make positive changes.
The negative effect of priming is made worse by Informational Conformity.
When reading confusing code it’s sometimes possible to think that the developer that wrote it ‘knows something that we don’t’. Based on this assumption we adhere to the behaviour of the code because we’re unsure of how to change it. We copy existing code even if we know it sucks.
An example of Informational Conformity is not writing unit tests simply because none exist currently.
If you’re unsure about why the code has been written in a certain way then ask the developer that wrote it, if they’re available. Otherwise, talk to your peers and discuss alternative designs or approaches to make the design cleaner and more coherent.
Closely related to Informational Conformity is the Halo effect. We take the code written by a developer that we perceive as more competent than us as having a good design even if that code is bad. We fail to challenge or change it and we even copy it.
This effect also applies to our own code. We take one positive aspect and associate it with the entire code base. We ignore the flaws of our code because the service we created to retrieve customer data is pretty awesome!
We also need to be careful we don’t apply the reverse-halo effect by dismissing ideas from developers that we perceive as less competent. We need to remember that everybody has something to contribute.
Given two similar rewards, we prefer the one that arrives sooner rather than later. More specifically, we discount the value of the later reward by a factor that increases with the length of the delay. This is Hyperbolic Discounting.
As developers, we act in favour of the present state of the code over investing in the future, i.e. refactoring into a better design because we want the reward of it being finished now.
If we build software in a time-boxed, iterative manner, time-frames for deliverables are always going to be relatively small. This makes it easy for us to be satisfied with messy code that works rather than putting in the effort to refactor into code that can easily be maintained and changed.
The hyperbolic discounting effect is compounded by the fact that we suffer from another cognitive bias: procrastination. We tell ourselves that we'll remove that duplication later!
According to the Principle of Least Effort, people are inherently lazy. If there are several ways of achieving the same goal, we will opt for the least demanding course of action.
In the case of developers, this usually means avoiding refactoring until later. Those unit tests will get added in our next Pull Request... sound familiar?
Reactance occurs when we feel that someone or something is taking away our choices or limiting the range of alternatives available to us. So we do the opposite of what the rules (or principles) suggest to prove our freedom of choice. We ignore software design principles because we want to do our own thing!
This bias makes us commit to an opinion more strongly when we are challenged or pressured to adopt another viewpoint. Sometimes our responses are justified but sometimes we may just be being irrational.
Have you ever worked with a developer that grossly overestimates their own technical ability?
The Dunning-Kruger effect occurs when people fail to adequately assess their level of competence at a task and consider themselves more skilled than everyone else. Not only this but they also fail to recognise genuine competence in other people.
The opposite is also true. Competent people will underestimate their ability compared to others. This is known as the imposter syndrome. Either way, we're pretty bad at assessing our own abilities.
What can we do about this?
When people are exposed to training they do then recognise and acknowledge their own personal previous lack of skill. We don’t know how incompetent we are until we're given the skills or knowledge to realise what we don’t know. This is why training is so important in software development.
Conversely, doing well in an assessment or completing a training course can give us a sense of achievement. This helps combat imposter syndrome.
Putting it all together
Software design principles play a key role in making software successful. They exist to guide developers away from writing bad code.
Developers ignore these principles because they are constrained by their environment and are affected by ‘thinking errors’ that influence their decision-making process. Being aware of these cognitive biases is the first step toward being free of them and writing better code.