It is clear that the obvious answer (and the correct one) is “Because the code has to not only write, but also read.” This answer is hardly worth the whole post, but the author is not limited to them.Recently, my colleagues have been snapping at my tireless comments about style in reviews of their code. I pay more and more attention to what can rightly be called cosmetics. Yes, there are bad code styles, there are better ones, but all of them are just conventions. It is undeniable that some of them actually improve the overall quality of the code, but no matter how much we argue about spaces and tabs, they remain subjective preferences, often a matter of taste.
However, I am not worried about the quality of the code - not as much as the eye strain and random reading errors, which also determine the quality of the code. We often struggle with errors that could have been avoided if it were not for haste and misunderstanding.
')
Developers not only write code - most of our time we add, remove and edit small portions of it, in fact we make minor changes to a huge code. Independent code fragments written from scratch rarely come across - unless, of course, we are starting a new project. Most of our projects are the legacy of many generations of developers before us. Whatever we do - add new functionality or change the old one - we have to read the code to find out which team is doing what and how best to implement our idea. Even when working on a completely independent module or class, as soon as we wrote the first lines, we have to go back to them again and again to interweave the new code into them.
Strangely enough, the more experienced I become, the more time I spend reading the code before changing it, and this seems logical to me. Writing a new code is much easier than understanding the existing one and using it, but the easiest way is not always the best in the end. Of course, it will be faster to write this string comparison function case-insensitive for the thirtieth time when we need it again - or does it only seem to us? In fact, there is a possibility that a new error will creep into the new function and we will not notice them until it is too late. It is a little harder to make an effort and find an existing implementation that we can use - maybe even in our old code, but for that you need to be able to read it. (I do not consider bicycle invention to be a mortal sin, there are cases when it is the best choice, but that’s another story.) Another great side effect of reading a code is finding errors and reporting them, if not immediately corrected.
A reasonable choice between writing a new code or using an existing or library code can be made only if both options are taken into account - for searching for potential errors in the first case or for studying and embedding in the second. Unfortunately, the realization of this comes only with experience (
Dunning-Kruger effect ).
The question of supporting inherited code or writing your own is a completely different argument; I note that the improvement of the inherited code is almost always better than its destruction: it is less risky, there are always working fragments and there is code that can be taken as a basis. The idea of this article is that in order to avoid rewriting the code, the developer must be able to read the existing code, and read it well. And for this, you need to be able to write your code so that it is readable.
Let us turn to the specifics. This is what I consider important to improve the readability of the code.
1. Consistency.
I can easily read the code in almost any style; had to learn to adapt. If I think that I need to read the code, I have to agree that his style may be different from my favorite one. Only it is impossible to adapt to one thing - to the code, as if written by a five-year-old child, who has just switched from the QWERTY layout to
Dvorak . Whether you choose the style yourself or impose it on you - stick with it throughout the project.
2. Consistency.
It is so important that it deserves the first two places on the list. People experiment with brackets-in-next-line and brackets-in-this-line, then forget about it, and their experiments end up in the repository, annoying the rest of the team. Seriously, experimenting with style is good, but you don't need to commit them.
3. Add space.
The code is similar to the text: everyone hates reading the “sheets” of the text without formatting and paragraphing - so, this also applies to the code. Separate blocks and scopes with empty lines; the closing bracket is a great place for them. Want to add a comment? Great, but you don't need newspaper style when the code is formatted in two columns - the code on the left, the comments on the right. This is useful, but rarely, usually for comments, the place is in front of a block or a command, and not near them; before the comment is better to add an empty line too. Remember, there is enough disk space: break long functions, huge classes and long files. Let your functions be a reasonable size. Use metrics like
cyclomatic complexity to determine when your code becomes too complex and therefore difficult to read and understand. Do refactoring.
4. Post banner comments!
The desire to add a banner of five lines of the comment inside the function is a good sign of the need to start a new function, before which this banner will be located. I would prefer to do without it at all, but this is better than one function per thousand lines with half a dozen banners. In fact, if you want to mark the beginning of a new logical block, it is better to make it a function. The same applies to any long functions, classes and files.
5. Do not use magic numbers.
Constants are a great tool for naming numbers on which a code is based. The default values, container sizes, timeouts, and messages to users are just a few examples of what can be put into constants. Just do not need to replace any number with a constant - they are not invented for that.
6. Choose your name wisely.
There is a lot of literature on the naming of variables and functions and the dangers of choosing similar names. However, some irresponsible elements still insist on incomprehensible names and the reuse of variables outside logical boundaries. Titles should not only be meaningful and non-dual-valued, but also consistent in all functions, classes and modules, ideally in the whole project. Well, at least try!
7. Do not write in a chain.
Many practice the declaration of all variables of the same type in one line separated by commas. This comes from languages that required this and did not allow variables to be declared after commands. If the language allows it, declare variables as close as possible to the place where they are used, and do not reuse them (except for exactly the same purposes).
A similar code chaining scheme occurs when the results of calling other functions are passed as an argument to one function, without storing them in intermediate variables. Some believe that this turns out to be more compact, and piled on 3-4 nested functions. Often it turns out exactly the opposite, especially if you want to check all the side effects of functions in the machinations of an error.
8. Limit the size of lines and files.
Horizontal scrolling is terrible: you can't expect a person to read a text by flipping it left and right on each line. Different people have different screen sizes, many use larger fonts due to poor vision. At the same time, many developers use the maximum width of their screen, and sometimes they do not limit the length of the line at all. This is very inconvenient, because in many development environments, the unfortunate line-by-line transfer of code destroys its formatting.
You can start with the historical standard - 80 characters per line: not too narrow and supported with most monitors and settings. If the whole team can work with strings of 100 or 120 characters in length, they can be taken as the standard; but here it is important not to overdo it, since too long lines are hard to read by themselves. Think of newspapers and magazines - how narrow their columns are and how they are read. Lines that, when read, have to be moved not only with the eyes, but also with the head, create additional physical tension and slow down work.
9. Make scope visible.
In many languages, the scope is implicit. The first command after a conditional branch or loop in C type languages clearly falls within its scope, but not the following commands. If the formatting is unsuccessful, it is rather difficult to track the end of the block, in order to find out which commands belong to it and which ones no longer belong to it. Formatting, highlighting blocks explicitly, adding parentheses and an empty string after the closing parenthesis, even if the body of the loop is all from one command, greatly improves the readability of the code.
10. Comment.
The code, even tangled and foggy, is perfectly understood by computers. People, on the contrary, need to understand not only what the code does, but also its purpose of the code and the reasons for which it was written that way. Without an understanding of certain constructions, operations, and values, no developer can meaningfully support code. Comments are a great tool for conveying non-obvious code nuances. Explain business requirements, assumptions, requirements, patches, and workarounds. Talk to your workmate through comments. However, there are no rules without exceptions - do not abuse the comments. Do not write "now an error occurs" before generating an exception - the throw command speaks for itself; better explain why in this case nothing can be fixed.
In conclusion, I note that some code must be made unreadable. Yes, your eyes do not deceive you. Code that is built on hacks, bad practices or temporary solutions should be read poorly. My best example is type casting. Often, instances of objects need to be cast to certain types — this is standard practice, but not exactly recommended. Writing the conversion in a slightly less readable form is a good way to make the reader stop and make sure that this is not a mistake. This is somewhat similar to the text in italics, which is harder to read and which stands out in this way. Likewise, temporary solutions and hacks should be highlighted. TODO and FIXME markers are a good start; in fact, if there should be a banner comment somewhere in the code, then it’s here that the known problems can be pulled out of the code.
(The main thing - do not forget to fix them! - Prim.per.)Like most of these articles, this one is mainly about the principles of readability of the code, and not specific requirements. They are not rigidly set and are by no means complete. All languages, ecosystems, frameworks and libraries have their own nuances, things that are obvious in some languages may be ambiguous and undesirable in others. An important conclusion that needs to be made is the need to write code for the reader, avoid obscure styles, follow the context and evaluate when to write a comment and when to refactor.