Lecture
Это продолжение увлекательной статьи про ошибки в проектировании программы.
...
make even more progress. Experienced programmers love errors; beginners hate them.
If these lovely little error messages irritate you, you need to change your attitude toward them. Look at them as helpers, collaborate with them, lean on them as you climb to new heights.
Some errors should be turned into exceptions, errors planned for on behalf of the user. Some should be left as they are and allowed to crash the application and close it.
Your body and brain need rest. Beginners tend to get carried away with work and forget about breaks. Be sure to include something in your workflow that reminds you to rest. Get up, take a walk, think about what work is next in line and how best to do it. Come back to the computer with fresh eyes.
We turned to our experts for clarification, and we present their answers for your attention.
A typical mistake of beginners in programming is the desire to fix on their own what seems to them like imperfect code or an imperfect solution. This happens for various reasons: some are simply afraid to bother more experienced colleagues with a question one more time, some are youthfully ambitious, some are perfectionists. Beginners tend to more often choose the most complex and universal solutions (and consequently the more labor-intensive ones) in cases where the task has a simple solution.
So what are the main mistakes that beginning programmers can make?
1. Not trying to search for information. If you are a beginning programmer, someone has surely already run into your question, and the solution is available in the open, in manuals, FAQs, and so on. Searching for an answer can also show the chosen solution to a problem from a different angle. And it may turn out that this solution will only make your project worse. It is very useful to draw on the experience of other programmers.
2. Not abandoning a wrong solution. If you have realized that the chosen solution is not the best, boldly discard it and start over. Bad code and a lot of “crutches” in a program will not make it convenient to operate, even if it ends up working.
3. This mistake follows from point two - making the code worse rather than better. You should always try, even if only a little, to improve the code, and not make it worse with temporary solutions.
4. Not planning. On this issue, it is important to find the optimal option, because overly detailed planning can only make preparing the project harder. There are no perfect plans, but, especially for large projects, planning is necessary.
A beginning programmer should also pay attention to some matters that concern the actual writing of code: encapsulation, well-chosen tools, testing (and better yet, automated testing), and so on. As they say, everything comes with experience, but if you account for possible mistakes in advance, you will gain that experience much faster.
I would put this question differently: «What dangers lie in wait for a beginner in programming?». In my opinion, the main one is overconfidence. If a beginner solves a task on the first try, they may get the impression that everything is easy and they can relax. But this is not so; on the contrary, if a task was solved, you have to work even harder to reach the level where mistakes arise, or where you cannot offer a solution. Otherwise, you will be overtaken by those who were not so confident in their knowledge but worked hard and diligently. On the other hand, the solution to a task may not be the most optimal, but without the drive to find other solutions and «play» with them, a beginner may remember their first solution and always propose it, even when it is no longer appropriate.
The main mistake of a beginner is that they are in a hurry. Instead of studying the language, platform, or framework, they type a question into Google/Stack Overflow and simply copy the result. This is bad not only because without systematic knowledge it is impossible to understand new technologies «from the inside», but also simply because there may be mistakes in the copied answers. If you do not thoroughly figure things out, instead of code you will get a «patchwork quilt» of pieces from Stack Overflow, which the author themselves understands only very superficially. As a result, it will be extremely difficult to develop this solution further.
Choosing the wrong learning resources. The profession of a programmer is an occupation that involves daily learning and mastering an enormous amount of information. Sometimes you even have to relearn things, because old experience and old knowledge can get in the way more than they help. That is why two of the three key mistakes are related specifically to learning.
Today there is a large number of information sources of varying quality: from the classics of Computer Science written 10-15 years ago, to modern schools of learning where practitioners teach. I would recommend being especially cautious about modern info-businessmen who promise to make a programmer out of you in a week.
A typical mistake of a beginner is jumping from source to source, trying to cover everything. This practice leads to a funny effect: imagine you are trying to learn to dance by attending classes at different schools; most likely you will never learn to dance. But this approach is quite applicable when it comes to choosing the dance school itself. That is why it is important to choose the right source and work with it. The key criteria for selecting sources: the relevance of the information, the level at which it is presented, and how well it is filled with practical tasks (the ability to solve concrete tasks).
Lack of practice. I have very often encountered the situation where at first some beginners understand everything and simply do not do the practical assignments, because everything there is simple. After some time, they quite plainly cannot write anything that, in their understanding, is no longer so simple, because they have no practical skill.
The key criteria: the availability of practice for the material being studied and the closeness of that practice to real life.
Not understanding the context of the task. Unfortunately, from childhood we are taught that a task states all the initial data and conditions, and clearly defines what needs to be done.
In the development of software products, however, you need to analyze competitors more, because they have already done the work of solving the task before you. Let me give a very simple example: if you give a beginning programmer the task of building a service for selecting debit cards, say by the interest rate on the balance, then they will build you a service in which the rate is a single number. But if you look at real products that solve a similar task, the rate may not be just a single number. That is why you need to learn this right away, and for that you need to solve not toy tasks, but tasks that occur in the real world (even if in a simplified form).
1. Fear of contact.
Beginning programmers often try to do everything on their own, or, on the contrary, are afraid to solve tasks alone. In the first case, a person can make a large number of mistakes that could be avoided by turning to colleagues for help. The second extreme carries the risk that the programmer will not be able to prove themselves and will remain a beginner. The best way to cope with excessive overconfidence or fear is to enlist the support of an experienced colleague who can act as a mentor. First and foremost, the programmer should try to find the solution to a task on their own, and if that does not work out, turn to a colleague. At the same time, it is important not to be afraid to ask additional questions until the solution is clear.
2. Copy-paste.
Some beginning programmers copy other people’s publicly available code, and that is normal. It is bad if the code is taken in its entirety, without breaking down its components and reading the documentation about them. Such an approach carries the risk of late errors during the operation of the code, which are difficult to diagnose and fix. First, the developer has already managed to «forget» the problematic code. Second, the fix may affect not only the problematic code, but also the surrounding code that uses it.
3. Opaque names of variables or functions.
Reading code takes 10 times longer than writing it, so it is important that it reads well and that the programmer quickly understands what exactly this code does. Let us take the Python language as an example.
Inexperienced programmers might name variables like this:
def a(b):
return math.pi*b
But it is better to write it like this:
def circle_length(diameter):
return math.pi*diameter
4. Premature optimization.
Many developers strive to make code fast, but beginning programmers often rush and do not figure out the reasons for slow performance. Frequently, such an approach leads to code that reads poorly, while the application does not become any faster. This is caused by the fact that the code being optimized is code whose execution was already fast compared to other parts of the application. That is why you should optimize code at the moment when it is clear that it is working slowly. And the optimization itself should begin by identifying the spot that takes the most time.
For example, code like this:
def print_tree(tree)
nodes = list_tree(tree)
for node in nodes:
print get_node_data_from_database(node)
You can spend a lot of time optimizing the list_tree function, but most likely the speedup will not be noticeable against the backdrop of sending a multitude of queries to the database at the next stage. It is better to focus on preparing a single query to the database and then iterating over the received data:
def print_tree(tree)
nodes = list_tree(tree)
nodes_data = get_data_from_database(nodes)
for node_data in nodes_data:
print node_data
5. Chasing trendy technologies.
As a rule, new technologies have an advantage in some areas at the cost of losing out in others. Until a beginning programmer has an understanding of these particulars, it is better to use time-tested universal solutions. For example, if a programmer is developing an application that stores some data, they should not rush to use the very latest NoSQL solution just because it is trendy. In most cases, an ordinary SQL database (MySQL, PostgreSQL, SQLite) will do, with a large amount of documentation, standardized approaches, and problems that have been described and long since solved.
Finally:
• Always test your code; ideally, write automated tests;
• Make backups of your data, including a version control system for your code;
• Do not deploy new code to the production server on a Friday evening, because there is a chance you will have to spend the weekend fixing bugs.
Our developers left the status of programming beginners behind long ago. However, even those who became gurus have something to look back on. After all, they too were once at the dawn of their careers and made mistakes. And here they are: using unverified information in their work, an unwillingness to dig deep into a problem, studying it superficially, choosing an incorrect learning resource (which later had a bad effect on the results of their work), developing a new feature for the sake of a feature (saw something new and set about doing it, forgetting about the nuances and pitfalls), and unnecessary, useless nesting in the HTML.
If 15 years ago someone had told me about these 5 mistakes, I think my path as a developer would have been easier. I am sharing my experience with you.
Typical mistakes of a beginner programmer:
1. Reinvents the wheel. Many tasks have already been done before; you just need to google and find a good example with open source code.
2. Grabs the first solution that comes along. As a rule, if you think a bit longer, you can arrive at a better solution to the problem.
2. Does not think about architecture. Solves the problem in the current spot without analyzing how the new code might affect other places in the program.
4. Crutches. Instead of getting to the bottom of the problem, the developer hastily starts inserting patches into the code that deface the program.
5. Does not write tests. Tests are needed; they make life easier.
The topic of typical mistakes of beginning programmers, the reasons they arise, and the ways to eliminate them has interested me for a long time, and whenever possible I try to help my colleagues. Let us take it in order.
Mistake number one is not knowing the tooling. In 2018, writing even a small application requires the use of a dozen different tools: platforms, frameworks, and libraries. A superficial knowledge of them leads to the appearance of gross errors. This is especially aggravated by the availability of a large number of ready-made recipes on the internet. You can produce somewhat working code while completely failing to understand the basic principles of how the platform you are using works.
Let me give an example: a beginning programmer adds rows with information about an online store’s orders to a table in a relational database. For each row, they need to obtain a unique numeric order id. They know the algorithm for finding a maximum and know how to select rows from a database. As a result, every time they need to insert a row, they select all the rows from the database, find the maximum in the application code, and create a new row with an id equal to the maximum id plus one.
During debugging everything works well; with real customers, various unexpected effects begin. Here the gross error is obvious, and they would not have made it if they had read a book about any relational database.
Mistake number two is the lack of understanding of how, where, and on what hardware the program will be used going forward. Any code, while it executes, takes up some amount of processor time and requires a certain amount of memory. If in the process the results of the work are saved to disk or sent over the network, it would be good to understand how much and what kinds of resources this will take.
A very small number of beginners have even the slightest idea about this. As a result, applications appear that require an unjustifiably large amount of resources for simple actions.
Mistake number three is not understanding a simple fact: programs are written for people. When writing code, it is very important to understand which problem of which group of people this program solves. In exactly what way the program will make people’s lives easier once you finish writing it. That said, this problem is characteristic not only of beginners, but that is already another story.
The main problem is writing code in a brute-force way. Gaps in the knowledge of the language and its constructs push a developer toward straightforward implementations. Hence the multiple nested loops and everything that is called “spaghetti code” that is impossible to maintain. Reading good code by colleagues, the code of open-source projects, and documentation will help you cope with this mistake.
Just as often you encounter a mistake related to writing your own libraries, implementing functionality (reinventing wheels) when ready-made or generally accepted ones exist. Besides the obvious loss of time on writing the code and subsequently maintaining it, the code of a single person cannot possibly be better (there are exceptions, of course) than code that has been thoroughly tested and used for several years by many developers all over the world.
Many beginning developers use the copy-paste technique. We live in a wonderful time where any problem can be solved in a few seconds with the help of a search engine. But you should not abuse this. Thoughtlessly copying solutions from Stack Overflow will not only fail to add to your store of experience, but may also break something in the project. After you have found the problem, try to solve it with the help of documentation; if you cannot do that within a reasonable time, then you can turn to the great Google for help. After a solution has been found, you need to understand how it works, and only after that can you use it in your code.
There are mistakes involving the use of inappropriate tools. Many juniors, before they have even finished writing their first hello world, are already rushing off to master new technologies and breakthroughs in the world of software, while being unwilling to study the fundamental things that are the basis of the entire modern technology stack and do not change radically over decades. As a result, several significant problems appear:
Using technologies that are excessive for the task, which someone read or heard about somewhere, purely for the sake of using the technology itself. This makes the code harder for other developers to maintain, and besides, not all technologies are suitable for production.
A lack of understanding of where, how, and, most importantly, why to use a particular technology or tool has a negative effect on the quality of the code and the project as a whole.
An attachment to one particular language/technology/OS/IDE and attempts to impose them on others (evangelism) also often indicates that a person does not understand or cannot objectively compare a particular tool for solving a specific task.
Knowing C or Assembly to perfection is not required, but knowing what protocols exist, how they work, and having a basic understanding of fundamental technologies is necessary for any developer. Building on this knowledge, you can already understand what exactly the problem is and how and with what it can be solved.
There is also fixating on a problem and fear of criticism. It is hard for all of us to admit our mistakes and failures. And for a beginning developer it is doubly hard: they want to prove themselves to the maximum, to show that they can work independently. A frequent mistake is a negative reaction to comments from colleagues about the quality of a beginner’s code. You have to understand that every mistake of yours that is found makes you stronger, and that colleagues are not mocking you but want to help.
Or a beginning programmer cannot always solve a problem in a short time and starts to burrow into it, writes workarounds, or writes nothing at all and gets very upset. The main mistake here is not that the developer does not know how to solve the problem - everyone started at this level - but that they did not turn to anyone for help after the first 3-4 failed attempts. It is worth emphasizing that you should not immediately run to a mentor or a forum; mistakes are experience.
What typical programming mistakes do beginners make:
1. They reinvent the wheel. Beginning programmers, due to their limited outlook and youthful maximalism, tend to use their own solutions rather than existing modules. Beginners can avoid such a mistake thanks to communication with more experienced colleagues and strict deadlines for delivering work.
2. A tendency toward «beautiful» solutions. If there is a choice between solving a task in a complex and beautiful way or a simple and utilitarian one, a beginner will choose «beauty». Naturally, such «solutions» are harder to maintain, scale, and reuse. Working in a team and getting regular feedback from colleagues who are forced to suffer with such «beauty» will help get rid of this tendency.
3. Using objects for purposes other than intended. This is also related to a lack of experience. It can be corrected with regular practice in a team and constant study of the working tools.
4. Disregard for coding style, formatting, and the naming of variables and objects. Of course, colleagues take no joy in reading such code. Working in a team and feedback from a supervisor will help eradicate this disregard in a beginner.
5. Insufficient or incorrect commenting of code. It is no secret that after a month has passed, reading uncommented or poorly commented code turns into a quest. This is certainly exciting, but it takes a great deal of time. Let alone the attempts of colleagues to use such code.
Summary. You have to understand that all these mistakes pertain to programming for money. If you program for pleasure, that is a different matter. There are quite a lot of programming mistakes that pertain to work. Only the main ones are listed above. One could also mention disregard for «usability», insufficient testing, and a negative attitude toward having your code assessed by colleagues, and much more besides. But a beginner should not despair, because working in a team will allow them to get rid of all these shortcomings.
When speaking about typical mistakes of beginners in programming, people usually look at professional knowledge and skills and overlook possible mistakes in the work process itself. Based on my experience, I can say what a beginner definitely should not do:
Being afraid to approach someone with a question when something is unclear. All people are prone to the fear of looking stupid by asking an obvious question. We hope that we will figure everything out on our own. But one simple question on Stack Overflow or to a colleague can save a couple of days. This does not mean you should not figure out the problem yourself; of course you should, and it is useful. But it is extremely difficult to embrace the immensity of everything, and around you there are many well-rounded people, and many tasks can be solved faster with their help.
Thinking that since you are a beginner, everyone around you is smarter than you. It is great (and this is how it should be) when the team has people who are far more experienced and knowledgeable, ready to help and give advice. But it is not always worth relying one hundred percent on their words. Rather, treat them as an opinion. For example, you have run into a problem and you are told: «ah, well, it is not right here». You go to figure it out, cannot find the error in the indicated spot, and conclude that you simply have not studied the topic enough, since you are a junior. But it may turn out that the senior themselves does not quite understand what is going on. It can be hard to fully immerse yourself in the context without missing a single detail. We are all human. So you should not rely on a senior as the «ultimate truth»; be guided also by facts and evidence.
Overworking. By working from morning until midnight, without resting or getting enough sleep, you are borrowing health and strength from yourself. Someday you will have to pay it back. If I cannot handle the workload within the ~8 working hours, it means: a) I am not competent enough and overestimated my abilities, or b) I have problems with time management and took on more work than can realistically be done in that time. Both options are a reason to reflect and correct the situation. By overworking, you make things worse not only for yourself but also for your employer. Who needs an employee who has not slept enough for weeks and who, out of exhaustion, cannot even invert a binary tree? Yes, there are cases when a deadline is looming, everything has gone wrong, and it «was needed yesterday». But after such situations, you absolutely need to rest and do an assessment: what should I do to avoid allowing this in the future?
Answer usefulness rating:
Programming is not a simple process, and the creation of not a single program goes without mistakes along the way. Some bugs, the most unpleasant ones, are logical in nature. They are extremely difficult to track down, and often such errors «surface» only during operation. Syntax errors and typos are the most harmless. In almost all languages, they are caught by interpreters and compilers. Some things can be caught during automated or beta testing.
But right now we have decided to talk about the most common mistakes that practically everyone makes. For beginners, this reminder will help them pay special attention to the most popular kinds of bugs. For experienced developers, this list will simply refresh their memory. After all, we are all human, and the slightest inattention can lead to «dancing on old, well-known rakes».
The essence of the mistake is simple. You start using a variable that was not specified in the variable declaration block and did not receive its own type. How the program reacts to this depends on the chosen language:
Do not forget to check all variables and make sure that you have declared them. And with implicit declaration, it is advisable to use some additional means of improving your style. For example, comments.
It is not enough to declare variables; you need to keep an eye on their initial values. In most languages, before you «put» something into the allocated area of memory, there will be residual «garbage» stored there, that is, any binary code that remained in the cells before the program started running. This leads to unpleasant mishaps.
Let us give an example of such code:
[code]int num1, num2;
int sum = num1 + num2;
cout << "Enter two numbers to sum: ";
cin >> num1;
cin >> num2;
cout << "Sum = " << sum;[/code]
As a result of running it, you can enter, for example, the numbers 2 and 5, and get 2384 as the result.
If you read the code carefully, the cause of the error is obvious. The program first performs the summation of num1 and num2, and only afterward requests the input of their values. Whatever was stored in the memory cells before, that is what the computer added up. Move the data input lines above the addition operation, and the problem will be solved.

This mistake occurs especially often when using ready-made functions stored in separate files or libraries. No compiler will let such a bug through. But the search for the cause of the constant «bailing out» on the line with the function often takes a lot of time. Simply because the developer sees the line with the error and starts looking for the cause somewhere nearby.
It might seem that the situation is so obvious that there is nothing to talk about. But recall how many times you have spent time trying to find a bug like this. And how long it took you to «realize» that at the very beginning of the program you forgot to write in the inclusion of a file or library? It is precisely for this reason that this most popular mistake takes its place on our list.
Sometimes it happens that the code looks logical, but the program crashes with an error due to problems with allocating amounts of memory for a particular type of variable.
Let us give an example in C++:
[code]A = B + C;
char* G = new char[A];[/code]
These lines of the program perform the addition of two variables, after which a certain amount of memory is allocated for the variable G, whose value is stored in A. It would seem that there should be no problems.
But if the values of B and C are large, their sum will «not fit» into the amount of memory that A occupies. As a result of such an overflow, instead of the expected positive value, A will contain a negative number. And on the line allocating memory for G, the program will show an error.
Avoiding this is simple: do not forget to add a check of the values against the maximum allowable ones.
The buffer overflow error has become one of the most legendary in programming, since this vulnerability led to the creation of a whole series of «worm» viruses, starting with the Morris «worm». Some modern languages are protected from this vulnerability, and so as a result of an overflow the program simply crashes with an error. Others are still susceptible to such a bug, and as a result the user gets «holes» in their computer’s defenses through which malicious code can penetrate.
The simplest example of such a problem can be described as follows: text of a larger size is written into a string variable that can hold a maximum of 255 characters. If the last of the characters, which no longer fit into the allocated memory stack, are «read» by the compiler as additional memory cell addresses, it accesses them. And it is ready to write the «surplus» of information into that memory area. This is exploited by «worms», which dump «garbage information» into the variable, followed by malicious code that the program «dutifully» places in the computer’s memory.
Problems also arise in the case of trying to read something from such a variable. Even if the vulnerability is not exploited by malicious code, when reading, the program accesses memory cells located outside the buffer as well, and the program begins to process the additional cells. But they may long since contain any kind of «garbage» data, that is, random information that was stored at the given addresses.
Combating this phenomenon is helped by ordinary «foolproofing», set up for every case of receiving data from the user or from an external utility. That is, a check for conformity of the type, the range of values, the absence of executable code, and other parameters important for the uninterrupted operation of the program.

There is a belief that such an error can only be made in C or C++. In fact, accessing a nonexistent element of an array is possible in Python, Java, and many other languages. The essence of the problem is that the programmer, due to inattention or a mistake in calculations, accesses an array element with a nonexistent index.
The simplest example:
In doing so, the program accesses an unused area of memory and assigns this random value to the element with index 10. The result of processing this «garbage» information is, of course, unpredictable.
To avoid problems, do not be too lazy to write an additional check for boundary values. It is better to write a couple of extra lines of code than to run into unintelligible results because of a random bug.
This problem arises with manual memory management, as well as when working with databases or when creating unbounded arrays. Without strict control and the specification of limits, you risk getting one of two outcomes:
Handle array limiters carefully, check databases when merging them, and when directly accessing RAM be sure to specify boundary values.
This error is most popular among C programmers, since here, after finishing work with a block of memory, the cells are necessarily freed. But similar problems occur in other languages too, for example in the case of forced cleanup to save resources.
The essence of the problem is that the program accesses freed memory after the cleanup. And, naturally, does not get the expected data.
It might seem that everyone knows about this error. And checking a program for such a bug is quite simple. In fact, the bug is extremely popular even among experienced developers. It is not for nothing that news constantly appears about the «freezing» of major software products because of such a failure.

These two types of injection are actually hacker attacks that end with an attacker gaining access either to databases (SQL) or to root access on the user’s computer.
The causes of SQL injections are a low level of site protection. Most often, they are carried out through the sending of messages by users (a feedback form, adding a post to a forum, a chat request, and so on). If the «hole» in security is not closed, the attacker sends malicious code through these forms, and the server begins to execute it. And the hacker gains access to all the databases.
With OS commands the situation is similar. If you give the program permission to use the ready-made commands of the system, you absolutely must set up protection against attackers, so that the application executes only those commands that were specified by the developer, but cannot use anything more.
One of the «favorite» mistakes of juniors. It consists in the programmer either starting to «reinvent the wheel» when trying to protect users’ personal data or other important information, or, on the contrary, using the first option they find without even checking it for vulnerabilities.
With the first case everything is clear. Data protection is not the area where you should rely only on your own, rather modest, capabilities. An example of the second case is using the SHA-1 hashing algorithm. If you use a search, you will very quickly learn that this algorithm is already outdated, that many vulnerabilities have
продолжение следует...
Часть 1 Mistakes in Writing Programs and Designing Architecture
Часть 2 25) Not resting - Mistakes in Writing Programs and Designing
Часть 3 - Mistakes in Writing Programs and Designing Architecture
Comments