Anti patterns in software development

Christoph Nißle
6 min readSep 11, 2022

--

In my previous article about anti patterns in software development I went all over the place, describing all sorts of different types of anti pattern.

I have decided to be more specific about it and I will write about four main categories:

  1. Anti patterns in software development itself
  2. Anti patterns in software development processes
  3. Anti patterns in software architecture
  4. Anti patterns in software organisations
Photo by Markus Spiske on Unsplash

This post will be giving a perspective on software development itself.
Please keep in mind, that for every anti pattern there might also be a good reason why it is happening and why it might not be that wrong. This is a very generic view on things.

Anti patterns

Onion

Instead of modernising old code, the old code will be wrapped in layers and layers of new code. While there are good reasons to do that temporary, the danger of leaving the old code in the middle of the onion behind untouched is too high.

It can often provide a shortcut to introducing new things but will leave you with layers and layers of legacy code that renders unmaintainable at some point. A more sustainable way would be, to refactor and modernise what is inside instead of leaving it wrapped like an onion.

Boat anchor

A boat anchor is code that is left for later. It is not currently needed, but it might provide useful sometimes. The idea behind it is often, that the probability of needing it seems high, or some other reason why the programmer is attached to it. And therefore it would be easy to leave it there and get it up and running once it is needed. As long as it is not used anyway, what damage could it do?

Like a boat anchor itself, this code adds weight to your project, and adds cognitive load to developers going through the code base or debugging. Reading through the code and understanding it, finding out why it is there but not hit by a debugger, it is extra unneeded code that adds to the complexity and readability of your codebase without having anything useful to bring to the table. Just don’t.

Dead end

A dead end is reached by modifying a reusable component if the modified component is no longer maintained and supported by the supplier. An often seen example is an external dependency that is not maintained for quite some time but still is with a legacy code base that experiences modernisation.

Now the old dependency needs to be modified and the only way somebody saw was to fiddle around with that dependency. At first, it becomes part of the code base, it experiences modifications and without thinking about it the burden to support, develop and maintain it has wandered into the project team’s responsibilities.

And even if it was not an old dependency, but one that is still maintained, then re-integrating improvements from the original maintainers becomes even harder. Modifying a reusable component is reaching a dead end.

Cut-and-Paste-Programming

Reused code by copying source code leads to significant maintenance problems. The company does not have a culture of code reuse. Lack of abstraction or lack of communication can also be the cause.

Without adding overall productivity the lines of code to maintain increase significantly, bugs reoccur throughout the codebase despite many local fixes. Code reviews are unnecessarily extended and bloated and you will end up having excessive software maintenance costs.

Alone the amount of time and effort spent of engineers creating multiple unique fixes for the same problem in multiple places is an easy neck breaker to every code base.

Lava flow

I personally feel that I have seen the lava flow the most often in code bases that started off as a PoC and were moved into production. So previous quickly done development efforts, just smashing some things together, have been the outbreak of the volcano. And that code flows down the volcano lava like and now has manifested into a hardened stone like, immovable, generally useless mass of code, that not really anybody can remember much about.

Whenever developers “just did things” to demonstrate something very quickly, or deliver for “just a demo”, they discarded common development and design practices along with sacrificing documentation.

You can put a price on that code during every code analysis, verification or testing effort. It might be the place where you have those unexplainable memory leaks or something just does not perform. And you spot it by unjustifiable variables and code fragments.

Blob

The big classes, having a lot of properties and methods. Those are blobs. It is also very likely that the properties and methods aren’t even really related to each other. It should be very easy to turn that blob into multiple classes.

It is basically an object written in a procedural style. It has a lot of responsibilities and often is referenced in many places throughout the code base.

Kitchen sink

The kitchen sink is an overly complex class interface. It has been created for all possible uses, and that attempt has added a huge amount of interface signatures to meet all possible use cases up front.

They often have dozens to thousands of method signatures just for a single class. It lacks focus on almost every aspect. It happens often when vendors are trying to make their software applicable for all sorts of use cases.

Its problems come to shine when you want to manage that complexity. It is not just difficult to understand or guess its intentional use, it also is hard to debug, document and maintain. The difference to the Blob is, that is actually follows an idea, and is not just a collection of things. So when you meet it, its creator will often state, that there is a good reason for why it has been designed that way, which is not an excuse you should allow for.

Poltergeist

Poltergeists are classes with very limited jobs and are mostly short living. For example, they are starting processes for other objects and disappear into oblivion. It is very likely that they can be absorbed into other classes and are not needed in the first place. Just like a poltergeist they show up for a very short amount of time, do something very quickly and disappear.

Golden Hammer

A Golden Hammer is a familiar technology or concept that is obsessively applied to many software problems. A lack of knowledge about alternative strategies is a common reason. In addition, the previous solutions have proven themselves and are used again and again.

I like to also call it the “new best friend”. Every developer has them. Read about a concept, about a pattern, now that has to be applied. Like a new best friend you need to show around everywhere.

It results in a misapplication of favoured tools or concepts. And it is a clear sign of a team unwilling to learn new approaches or invest time in finding out what is suited better. Especially in “our database is our architecture”-thinking, this can be witnessed a lot.

Continuous Obsolescence

As one of the toughest anti patterns to overcome it is also a very present one in bigger software products. It is known for engineers fail to catch up with the current version of the software. Having a hard time finding the dependencies that work together to create a product release.

It is not just something you want to look out for with the software components and products you use for your own efforts, but also when you release software to customers so they can still be in a good place using your software. Software products evolve through releases, by adhering to open standards for example, you decrease the probability of that happening a lot already.

Singletone overuse

Often project size relates linearly to the amount of singletons. The bigger the project, the more often you find them. While the singleton pattern itself is not necessarily bad, its overuse is. It makes sure that only one instance of a class is instantiated for the lifetime of a routine or application.

Golden hammering singletons though is a problem. They hide dependencies of your application and instead expose them through interfaces. Additionally, they violate the single responsibility principle because they control their own creation and lifecycle. Furthermore, they cause code to be tightly coupled and make testing them rather hard and they carry state around for the lifetime of the application.

While there are more around, I feel that collection alone helps to keep your eyes open in ensuring good software development practices.

If you want to read up on good patterns, I have two books on amazon. I strongly recommend reading Head First Design Patterns, as absolute must read for every engineer or engineer to be.

Links to read up on:

--

--

Christoph Nißle

⛰️ Leadership Nerd 🏄‍♂️ People Lover 🎯 Team Player 🚀 Organisational Developer 💻 Tech Enthusiast 👀 Views are my own