Perfect code
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
Damian Conway, co-designer of Perl 6
Good program code is defined by at least three attributes: uniqueness, effectiveness, and maintainability.
Uniqueness is primarily a coding style. The uniqueness is determined by the names of the variables and functions the programmer chooses, how he formats the code, how he processes errors and forms the structure of the code.
Efficient code is a code consisting of efficient algorithms. Effective does not mean fragile, complex or difficult to maintain. The effectiveness of the code is achieved by using the strengths of the language and at the same time avoiding its weaknesses.
The maintainability is that the code is written primarily for those who will accompany it. Accompaniment - ease of use of the written code, minimizing the possibility of errors when it changes.
Code Uniqueness
All programmers are lazy, sometimes in a good way, sometimes in the ordinary. And few people give each line of code enough time to put it in order in accordance with the criteria of good code. Especially in cases "the sooner I put this check in the function, the sooner I will go home." Hence the variables named by one symbol, and several expressions in one line, and many other tricks that the applied programming language is capable of.
Meanwhile, all existing code improvement techniques (and this article, too) are aimed at making life easier for programmers themselves. For example, to make your code understandable to other programmers (and you yourself N months after writing it), you must follow the coding style. Of the proposed styles for the language used, it is better to choose the one in which formatting of the code in the K&R style is recommended (according to the initials of Kernighan and Ritchie, authors of a book on the C language), because it differs from the rest in better readability and makes the code more flexible.
Do not hide your incompetence by disabling alerts. Language tools that suppress error messages or warnings are a weakness of the language. This practice may hide potential errors in your algorithm from you, which will make your code very fragile. When refactoring, such an algorithm will almost certainly be broken.
For the names of functions, variables and constants, choose such names so that their purpose is obvious without an accompanying comment (which does not eliminate the need to write comments). Of the abbreviations and abbreviations, it is allowed to use only the generally accepted and well-known: min, max, avg, sum, len, ctrl, src, msg and others. If you can’t find the short form for the variable name, leave it as it is.
Use named constants, avoid bare digits and string literals in your code. Even if the purpose of each number used in the code in its pure form is clear for your algorithm, adjusting the algorithm can break your code when you need to change one number to another. When using named constants, it will be enough to change the number in one place without touching the algorithm code itself. I would not recommend making exceptions even for well-known values, such as 3.14, 2.7, 9.8, 42.
Code performance
Many programming people (not necessarily working in their specialty) remain at the level of students for a very long time, who boast to each other that they reduced the function of calculating factorial to one expression. Constantly using a bit shift instead of division and multiplication is also not a good idea. These are precisely the cases of premature optimization, which are written and talked about so much.
Before you optimize any part of the algorithm, you need to be sure that it is it that needs to be optimized. You can change the postfix increment for a prefix increment in dozens of places for a long time and win a couple of nanoseconds of processor time, but it is better to optimize database queries and get a performance boost that is visible even to the naked eye. It is in order to find the weak link of the algorithm, it is necessary to profile and measure the execution time of the code at each stage of the algorithm.
Before you reinvent the wheel, you need to make sure that this is not done before you. And only if there are no bicycles, or they do not suit you for any reason (except for the reason "it is not written by me"), you can proceed with the implementation. Using third-party libraries allows you to focus on your own algorithm. The more popular the library, the more efficient the code it uses and the less likely it is to detect errors in it. If the library interface is cumbersome or too unified, write a wrapper interface for this library so that your code is in the same style.
If the algorithm often uses the results of long calculations, it makes sense to consider the possibility of caching - saving these results to prevent repeated calculations if the result of these calculations does not change.
Code Escort
When making changes to the code, there is always a chance to break it, so of all the alternative versions of the algorithm, always use the most flexible. For example, instead of several else-if or switch, it can be much more convenient to use a key search in a hash table. In such cases, to add another value to the list of alternatives, it is enough to add another value to the hash without touching the search algorithm itself.
If you are writing a library of functions, first design its interface and write code that will use the functions from your as yet unwritten library. Is it convenient to use? If not, then this is an occasion to revise the interface, and since the functionality itself has not yet been written, it does not have to be refactored.
Many in their practice constantly deal with inherited code. And sometimes, when it is necessary to use a function long written by someone, and if the interface of this function is designed sloppy, only its name pops up in the memory, since the set of its parameters is not remembered. In such cases, you have to look for a place in the code where this function is already used, and copy its call from there to your code. If you don’t devote enough time and effort to designing the interface, the same thing can happen to your own functions when it comes time for someone else to accompany your code.
Regardless of code complexity, comments must be present. At a minimum, in the header of each function / method: what it does, what parameters it takes, what it returns. Without comments, you can leave only the most obvious parts of the algorithm, it makes no sense to comment on each expression. The easiest way to determine if the algorithm requires detailed comments is to figure out how many seconds it takes to understand it. If more than 5 - comments are required.