In programming, self-criticism is the ability to recognize counterproductive decisions in design, code, processes, and behavior. Knowledge of harmful solution patterns is useful for a programmer. In this article, I will describe the anti-patterns that I have seen on my personal experience from time to time.
Some of them are directly or indirectly related to
cognitive distortions of the human consciousness - in these cases I give links to the corresponding wiki articles. Also interesting is a list of known cognitive distortions.
1 Premature optimization
In 97% of cases, one should forget about the effectiveness of small parts of the program: premature optimization is the root of all evil. But in 3% of cases it is not necessary to forget about optimization.
Donald whip
Although never often better than right now
Tim Peters, Zen Python
')
What is it
Optimization carried out before you have all the information you need to make informed decisions about where and how you need to carry it out.
Why bad
In practice, it is difficult to predict where the bottleneck will meet. Attempts to optimize to obtain empirical results will lead to complication of the code and the appearance of errors, but will not bring any benefit.
How to avoid
First, write clean, readable, working code using well-known and proven algorithms and tools. If necessary, use the profiling tools to look for bottlenecks. Rely on measurements, not guesses and assumptions.
Examples and signs
Caching before profiling is done. The use of complex and unproved heuristic rules instead of mathematically correct algorithms. Choosing new, untested frameworks that may behave badly under load.
What is the difficulty
The difficulty is to know when optimization will be premature. It is important to leave room for growth in advance. You need to choose solutions and platforms that will easily optimize and grow. You can also sometimes use premature optimization as an excuse for bad code. For example, using the O (n
2 ) algorithm is simple because the O (n) algorithm is more complicated.
Too long, did not read
First, profiling, then optimization. Do not change simplicity to efficiency until empirically obtained data reports this.
2 Bikeshedding
(
comment. - the Anglo-Saxons like to invent verbs. This term is also called "The Law of Parkinson Triviality", and appeared after this Parkinson paid attention to how people like to spend time at meetings for all sorts of nonsense, instead of discussing the real problems Specifically, the designers of the nuclear power plant argued for a very long time what material should go on the bike shed - bike-shed ).
Periodically, we interrupted the conversation to discuss the typography and color of the page. After each discussion, we voted. I thought it would be most effective to vote for the same color that we chose at the previous meeting, but I was always in the minority. Finally, we chose red (and eventually we got blue).
Richard Feynman, “Why do you care what others think of you?”

What is it
The tendency to spend time discussing trivial and subjective things.
Why is that bad
Waste of time.
A detailed letter from Paul-Hening Camp about this.
How to avoid
Remind other team members of this propensity, and that in these cases the main thing is to make a decision faster (throw a coin, vote, etc.). If we are talking about things like a user interface, turn to A / B testing instead of discussing it in a team.
Examples and signs
Hours or days are spent in discussions about the background color or button layout in the interface, or using tabs instead of spaces in the code.
What is the difficulty
Bikeshedding is easier to notice and prevent than premature optimization. Note the time required for making decisions and compare it with the complexity of the problem.
Too long, did not read
Do not spend a lot of time on the simplest solutions.
3 Analytical paralysis
A desire to predict something, an unwillingness to act when it would be simple and effective, a lack of clarity of thought ... All these are properties that make us repeat the story endlessly.
Winston Churchill, Parliament Debates
Better now than ever
Tim Peters, Zen Python
What is it
The excess of analysis to such an extent that progress and actions stop.
Why bad
An oversupply of analysis can slow or stop progress. In severe cases, the analysis results are not needed by the time they are ready, or even the project does not leave the analysis phase at all. It often seems that the more information you have, the more it will help to make hard decisions. See
informative distortion and
illusion of admissibility .
How to avoid
Watch for iterations and improvements. Each iteration provides feedback and information that can be used for more meaningful analysis. Without this information, the analysis will be only speculative.
Examples and signs
Months and years spent in analyzing the requirements of a project, interface, or database structure.
What is the difficulty
It can be difficult to understand when it is time to move from planning, requirements analysis and design to implementation and testing.
Too long, did not read
Instead of excessive analysis and speculation, use step-by-step development.
4 God Class
Simple is better than difficult
Tim Peters, Zen Python
What is it
Classes that control many other classes that have many dependencies and a lot of responsibility.
Why bad
God’s classes grow until they turn into a nightmare of support. They violate the principle of one responsibility, it is difficult to conduct unit tests with them, to debug and document.
How to avoid
Break the responsibility into small classes, with the only responsibility that is clearly defined, unit-tested and documented.
Examples and signs
Look for classes with the names “manager”, “controller”, “driver”, “system” or “engine”. Look suspiciously at classes that import or depend on others, control too many other classes, or have many methods that do something unrelated to their main activity.
What is the difficulty
Projects, requests and the number of programmers are growing, and small specialized classes are slowly turning into classes of God. Refactoring such classes can take a long time afterwards.
Too long, did not read
Avoid large classes with too much responsibility and dependencies.
5 Fear of adding classes
Sparse is better than dense
Tim Peters, Zen Python
What is it
Believing that an increase in the number of classes complicates the design leads to fear of adding new classes or breaking up large classes into small ones.
Why is that bad
Adding classes reduces complexity. Unraveling several small balls of yarn is easier than one large one. Some simple classes to support and document are preferable to one large and complex class with many dependencies (God class).

How to avoid
Note those places where adding classes can simplify the design and chop up unnecessary links between parts of the code.
Examples and signs
class Shape: def __init__(self, shape_type, *args): self.shape_type = shape_type self.args = args def draw(self): if self.shape_type == "": center = self.args[0] radius = self.args[1] # Draw a circle... elif self.shape_type == "": pos = self.args[0] width = self.args[1] height = self.args[2] # Draw rectangle...
Now compare with the following:
class Shape: def draw(self): raise NotImplemented(" Shape 'draw'.") class Circle(Shape): def __init__(self, center, radius): self.center = center self.radius = radius def draw(self): # ... class Rectangle(Shape): def __init__(self, pos, width, height): self.pos = pos self.width = width self.height = height def draw(self): # ...
The example is fairly obvious, but it demonstrates that large classes with conditional or complex logic should usually be broken down into simpler ones. The final code will have more classes, but they will be simpler.
What is the difficulty
Adding classes is not a panacea. Simplifying the design of breaking up large classes requires a deep analysis of areas of responsibility and requirements.
Too long, did not read
A large number of classes is not a sign of bad design.
6 Effect of the internal platform
Those who do not understand Unix are doomed to reinvent its bad copies.
Henry Spencer
Any rather complex C or Fortran program contains a rewritten, unspecified, buggy, and slow implementation of half of Common Lisp.
Greenspan's Tenth Rule
What is it
The tendency of complex software systems to reinvent the capabilities of the platform on which they operate, or the language in which they are written.
Why is that bad
Platform level tasks — task scheduling, disk buffer, etc. not easy to implement correctly. Bad decisions often cause bottlenecks and errors, especially as the system grows. The re-creation of alternative constructions for what is already possible to do with the help of a language leads to the complication of the code and to the rise of the learning curve for beginners. It also limits the benefits of refactoring and code analysis tools.
How to avoid
Use the available features and properties of the platform or OSes. Do not create language constructs that compete with existing ones (especially if you are not used to a new language and miss the old one).
Examples and signs
Using the database as a queue of tasks. Reinventing the disk buffer instead of using the capabilities of the operating system. Writing a task manager for a PHP web server. Define macros in C to support Python-like constructs.
What is the difficulty
In very rare cases, it may still be necessary to recreate the platform’s existing capabilities (JVM, Firefox, Chrome, etc.).
Too long, did not read
Avoid reinventing those features that already exist in the operating system or platform.
7 Magic numbers and lines
Explicit is better than implicit
Tim Peters, Zen Python
What is it
Use unnamed numbers or string constants instead of named constants in the code.
Why is that bad
Without an explanatory name, the semantics of a number or string is hidden from us. This complicates the understanding of the code, and the need to change the constant can lead to errors. Consider the following code:
def create_main_window(): window = Window(600, 600) # .....
What are these numbers? Suppose the first is the width, the second is the height. If you later have to change the width by 800, then by searching and replacing it will be possible to hook randomly and the same height.
Using unnamed string constants is not as error-prone, but it complicates possible localization, and can also lead to errors due to the use of the same strings in different senses. The word “dot” can denote a pixel, a punctuation mark, or an end to reasoning — as a result, finding and replacing a word will lead to errors. This can be avoided by replacing strings with a mechanism for retrieving strings from the outside.
How to avoid
Use named constants, means for getting resources from outside
Examples and signs
Given above. Such an anti-pattern is easy to recognize.
What is the difficulty
It is sometimes difficult to say whether the number used will be magical. 0 in languages ​​in which indexing starts from zero. 100 for interest, 2 for parity, etc.
Too long, did not read
Avoid using numbers or string constants without names and explanations.
8 Management through quantity
Measuring the progress of a programmer by the number of lines of code is the same as measuring the progress of building an aircraft by weight.
Bill Gates
What is it
Decision making based on numbers alone.
Why is that bad
Numbers are good. The first two anti-patterns, premature optimization and biked-shedding, should be avoided with A / B testing and obtaining some quantitative results. But based only on numbers is dangerous. For example, the numbers are experiencing those models in which they made sense, or the models become obsolete and no longer correctly reflect reality. This leads to bad decisions, especially when they are made automatically (
distortion of automation ).

Another problem in the use of numbers for decision making (and not for simple information) is that measurement processes can be manipulated to achieve the desired goal (the
effect of the observer and expectations ). The Wire series shows colorfully how the police department and the education system moved from meaningful goals to playing with numbers. The following graph illustrates this question. It shows the distribution of marks on the test, in which the minimum mark for passing the test is 30.

How to avoid
Use dimension and numbers wisely, not blindly.
Examples and signs
Use number of lines, number of commits, etc. to assess the effectiveness of programmers. Measuring employee performance by the number of hours spent in the office.
What is the difficulty
The larger the firm, the more decision-making is required, the stronger their automation and belief in blind numbers will begin to penetrate into the process of making them.
Too long, did not read
Use numbers to inform, not a basis for decision making.
9 Useless (poltergeist) classes
Apparently, perfection is achieved not when there is nothing to add, but when there is nothing to take away.
Antoine de Saint-Exupery
What is it
Useless classes without dependencies, used to call methods of another class, or simply add an unnecessary layer of abstraction.
Why bad
Poltergeist classes add complexity, code for support and testing, and make code less readable. It is necessary to determine what a poltergeist does (and usually almost nothing), and train yourself mentally to replace its use with a class that really works.
How to avoid
Do not write useless classes and get rid of them whenever possible.
Examples and signs
A few years ago, when working on a diploma, I taught freshmen to programming in Java. For one of the labs, I was given material on the stack and the use of linked lists. And they gave me a "solution". That was the decision, almost verbatim:
import java.util.EmptyStackException; import java.util.LinkedList; public class LabStack<T> { private LinkedList<T> list; public LabStack() { list = new LinkedList<T>(); } public boolean empty() { return list.isEmpty(); } public T peek() throws EmptyStackException { if (list.isEmpty()) { throw new EmptyStackException(); } return list.peek(); } public T pop() throws EmptyStackException { if (list.isEmpty()) { throw new EmptyStackException(); } return list.pop(); } public void push(T element) { list.push(element); } public int size() { return list.size(); } public void makeEmpty() { list.clear(); } public String toString() { return list.toString(); } }
Imagine my confusion when I read it, trying to understand why the LabStack class is needed and what students will understand from such a useless exercise. If this is still not clear, this class does nothing at all. It simply transfers calls to the LinkedList object. It also changes the names of several methods (makeEmpty instead of clear), which is even more confusing. The error checking logic is not needed because the methods in LinkedList do the same (just through another exception, NoSuchElementException). To this day I can not understand what was in the mind of the authors of this material.
What is the difficulty
At first glance, the advice would be the opposite of the advice in the “Fear of adding classes” section. It is important to understand when a class performs a valuable role and simplifies design, and when it increases complexity in a useless manner.
Too long, did not read
Avoid classes without real responsibility.