Only he who does nothing makes no mistakes. Programmers are people too and make mistakes all the time. Let’s consider five typical design and programming mistakes occurring in source code of programs but not affecting their performance. Most often they occur because of developers’ inattention, laziness and carelessness.
Mistake #1. Too narrow a name of a class or interface
It is not always possible to predict the direction of the program and/or library. This can lead to the fact that the name of the class or interface is too narrow for its actual use.
The first variant of such a problem is that the name no longer corresponds to the inheritance hierarchy. For example, initially at the root of the hierarchy was the Camera interface. Then it turned out to be extremely convenient to implement a class for working with an electron telescope Telescope, inheriting this interface to be able to use the existing polymorphic code. But a telescope is not a camera, so it’s not a good idea to break logic.
Another form of this error has to do with extending the functionality of the class. The XMLHttpRequest class, included in the standard Javascript language library, immediately comes to mind. Actually it can work not only with XML data, but it’s not obvious from its name, which can be misleading.
Such error can be solved by means of refactoring or source code search/replacement operation. But there is one but. If you are developing not a self-sufficient project, but an application library, it can be very difficult to do, because other projects depend on your code. So you can’t just change the name (as in the case of XMLHttpRequest). In this case, you can rename the class, but for compatibility, create an alias that matches its old name. The old name should be marked as deprecated.
Mistake #2. Uncontrolled expansion of inheritance hierarchies
Inheritance is a useful and powerful programming technique. But you have to know the limits. If there is an entity in an application, it does not mean that there must necessarily be a class for it.
For example, if you develop a more or less complex game, there will be many entities: game character, enemies, obstacles, weapons, etc. If you create a class for each individual entity (e.g. sword, gun, rifle, etc.), the program will simply drown in hundreds or even thousands of similar classes.
The solution is to select a few basic classes that characterize the concepts: enemy, obstacle, weapon, etc. Next, the enemies themselves can be divided into ground enemies, flying enemies, etc., and given some typical characteristics (movement speed, size, attack power, etc.) in the form of class fields. Do the same for other entities as needed. For dynamic aspects of behavior (movement trajectory and artificial intelligence elements), use delegation, which is also useful for parameterizing the behavior of classes.
Mistake #3. Calling functions not by sense, but out of “convenience
Sometimes, when you are pressed for time to complete a project, you are tempted to cut and simplify something. This almost always leads to problems in the future.
For instance, you have a function that handles a button press. It performs a certain sequence of actions. Suddenly we notice that the same sequence of actions must be reproduced when a certain condition occurs in the program. It is hard to resist, and not to make a call to the handler directly. However, this leads to non-obvious dependencies that will turn into errors in the future if we need to change the handler code.
The correct solution in this situation is to create another function (or possibly more than one) that has a meaningful name and does what originally happened in the handler. Then this new function will be called in the handler and in all the places where it is needed. When the time comes to change something, if the changes are of a general nature, they can be made in the created function, otherwise they can be made individually on the spot, without affecting the logic of the rest of the code.
Mistake #4. Deliberate duplication of code fragments
Duplication in programming usually causes only troubles. But it is one thing when it happens by accident and another thing when the code is duplicated deliberately. Most developers face this problem. It seems sometimes that it is easier to copy a piece of code from one place and paste it into another than to allocate a separate function (or class), for which one still has to find a proper place, think up a good name and a successful signature. But this feeling is deceptive.
Very often it turns out that if some fragment had to be copied once, it will have to be copied again. The worst thing is when the developer himself realizes the depth of the problem, but can’t give up what he has started and uses “copy-paste” once again. Thus he drowns himself. For if it turns out later that the copied and pasted code contains an error and it must be corrected in every duplicated fragment, you will have “fun” hours of routine work with lots of potential errors.
The only right solution in this situation is not to be lazy and create auxiliary functions and classes in time. In the future, the time you have spent will pay off many times over, and you can avoid a lot of difficulties and problems. If you fail to do it in time, the refactoring tools can help you. In many IDEs, they are so smart that they can find duplicated functions by code fragments themselves.
Mistake #5. Inappropriate function and variable names
This error type overlaps in many aspects with errors 1 and 3, but I decided to mark it separately because there are conceptual differences between them.
We often give wrong names to functions and variables in haste. Sometimes this is due to an incomplete understanding of the subject area or design errors. But that’s no excuse.
Of course, there’s no need to be 100% pure either. In a simple for loop, it is fine to use variable i, but the larger the scope, the more meaningful the name must be.
Even worse, if the name is meaningful but misleading. That is, the name implies one thing, but in fact the variable stores something else; or the function does not do what you would expect it to do (for example, there are side effects).
The solution is to critically analyze the code, because in most cases a simple renaming is not enough. A bad variable or function name is often related to a design mistake, i.e. something is in the wrong place or does more than it should. So fixing the problem requires care and time. But it is worth it, because the clearer the code is, the less chance there is of errors appearing in it.