Web Development: The 10 Coding Antipatterns You Must Avoid
Editor’s note: This article is part of our Code Optimization series, where we take a look at how to optimize coding for better efficiency in a bid to be better coders.
Designing the architecture of a website or an application, or setting up an effective coding workflow frequently make us deal with recurring problems. We don’t necessarily need to solve these software design problems from scratch, as solutions on the architectural level can be reused in the same way as code snippets on the micro-level.
Design patterns are generally reusable solutions for certain scenarios, that can come in handy to solve commonly occuring problems, and can hugely help us optimize our code.
While design patterns are great means to improve our development process by using well-tested formulas, sometimes we can also go wrong with them. These are called antipatterns.
What are Antipatterns?
The term “antipattern” was coined in a book called AntiPatterns in 1998. It refers to reused solutions that initially seem useful, but later turns out to do more harm than good.
This can happen for different reasons, for example if we don’t use the patterns in the right context, setting, or time (solutions that were effective in the past may not always work in the present), or in other cases the whole paradigm was just bad from the start.
Antipatterns are also frequently called patterns of failure. The good news is that it’s possible to recognize and avoid them.
In this post we will have a look at 10 common coding antipatterns in web development that may delude us into thinking we have well-optimized code. (Note that the antipatterns listed in this post are not necessarily the same as what you can find in the book mentioned above.)
1. Premature Optimization
Good timing is a crucial factor in code optimization. We can easily reproduce the antipattern of “premature optimization”, if we pay attention to small efficiencies and optimize for them too early in the development process, before we exactly know what we want to do.
If we optimize for performance before setting up an effective architecture, we may lower code readability, make debugging and maintenance harder, and add superfluous parts to our code.
To prevent premature optimization it’s a good idea to follow the YAGNI (You Aren’t Gonna Need It) programming principle, which advises to “always implement things when you actually need them, never when you just foresee that you need them.”
2. Reinventing the Wheel
The “reinventing the wheel” antipattern is sometimes also referred to as “designing in a vacuum”. It happens when we want to do everything by ourselves and write everything from scratch, without looking for already existing methods, APIs or libraries.
Reinventing the wheel is not just a time-wasting thing to do, but custom solutions, especially for basic functionalities, are rarely as good as standard ones that have already been tested by many developers and users.
3. Dependency Hell
The opposite of the “reinventing the wheel” antipattern is another common antipattern called “dependency hell”.
If, instead of writing everything from scratch, we use too many third party libraries that rely on specific versions of other libraries, we can easily run into a hardly manageable situation when we want to update, as these subsidiary dependencies are in many cases incompatible with each other.
Dependency hell can be solved by using package managers that are able to smartly update interdependent dependencies. If we are overwhelmed too much by the problem, refactoring can also be a good idea.
4. Spaghetti Code
“Spaghetti code” is probably the most famous coding antipattern. It describes an application that is hard to debug or modify because of the lack of a proper architecture.
The result of poor software design is a bunch of code that is similar in structure to a bowl of spaghetti, i.e. tangled and convoluted. Readability of spaghetti code is very low, and it’s usually an almost impossible mission to understand how it exactly works.
Spaghetti code usually stems from the combination of different bad coding practices, such as the code not containing proper conditionals blocks, having lots of goto statements, exceptions, and threads, containing parts that belong somewhere else, has minimal relationships between objects, has functions or methods that cannot be reused, or isn’t documented properly or at all.
5. Programming by Permutation
“Programming by permutation” or “programming by accident” happens when we try to find a solution for a problem by successively experimenting with small modifications, testing and assessing them one by one, and finally implementing the one that works at first.
Programming by permutation can easily introduce new bugs into our code, worse still, they are bugs we don’t necessarily recognize at once. In many cases, it’s also impossible to anticipate whether the solution will work for all possible scenarios, or not.
6. Copy and Paste Programming
“Copy and paste programming” occurs when we don’t follow the Don’t Repeat Yourself (DRY) coding principle, and instead of creating generic solutions, we insert already existing code snippets to different places, and later edit them to fit into the given context.
This practice results in a code that is highly repetitive, as the inserted code parts usually differ only in minor discrepancies.
Copy and paste programming is not only committed by novice developers, but experienced programmers as well, as many of them are prone to use their own pre-written, well-tested code snippets for specific tasks, which can easily lead to unintended repetitions.
7. Cargo-Cult Programming
The name of “cargo-cult programming” comes from a specific ethnographic phenomenon called “cargo cult”. Cargo cults appeared in the South Pacific after the second World War, when the forced contact with advanced civilizations lead natives to think that manufactured products, such as Coca-Cola, TVs, and refrigerators brought by cargo ships to the islands, were created by supernatural methods; and if they perform magical rites similar to the customs of Westerners, the cargo filled with goods will come again.
When we commit the antipattern of cargo-cult programming, we basically do the same. We use frameworks, libraries, solutions, design patterns, etc. that worked well for others, without understanding why we do so, or how said technologies exactly work.
In many cases developers just ritually do what is hip at that time without any real purpose. This practice is not only bad because it makes our application superfluously bloated, but it can also easily introduce new bugs into our code.
8. Lava Flow
We speak about the “lava flow” antipattern when we need to deal with code that has redundant or low-quality parts that seem to be integral to the program, yet we don’t completely understand what it does or how it effects the whole application. This makes it risky to remove it.
It usually happens with legacy code, or when the code was written by someone else (usually without proper documentation), or when the project moved too fast from the development to the production phase.
The name of the antipattern comes from its ressemblance with lava coming from volcanoes, i.e. at first it moves quickly and fluidly without taking too many precautions, but later it solidifies and becomes hard to remove.
In theory, we can get rid of lava flows with extensive testing and refactoring, but in practice, the implementation is frequently hard or even impossible. As lava flows usually have high performance costs, it’s better to prevent them by setting up a well-designed architecture and a sound workflow from the start.
“Hard coding” is a well-known antipattern against which most web development books warns us right in the preface. Hard coding is the unfortunate practice in which we store configuration or input data, such as a file path or a remote host name, in the source code rather than obtaining it from a configuration file, a database, a user input, or another external source.
The main problem with hard code is that it only works properly in a certain environment, and at any time the conditions change, we need to modify the source code, usually in multiple separate places.
10. Soft Coding
If we try very hard to avoid the pitfall of hard coding, we can easily run into another antipattern called “soft coding”, which is its exact opposite.
In soft coding, we put things that should be in the source code into external sources, for example we store business logic in the database. The most common reason why we do so, is the fear that business rules will change in the future, therefore we will need to rewrite the code.
In extreme cases, a soft coded program can become so abstract and convoluted that it is almost impossible to comprehend it (especially for new team members), and extremely hard to maintain and debug.