Hello! Today I want to share tips on writing a
perfect clear code, taken from the book by Peter Goodleaf "Craft programmer // Practice of writing good code."
Of course, it would be nice to read this entertaining book to anyone who writes code, but for particularly lazy, but willing to stop torturing less and introduce colleagues to confusion (
have a conscience ), I imagine
10 principles of self-documenting code under the cut
:
1. Write a simple code with good formatting.
The presentation format has a huge impact on the ease of understanding the code. A sensible representation conveys the structure of the code: functions, loops, and conditional statements become clearer.
int fibonacci(int position) { if (position < 2) { return 1; } int previousButOne = 1; int previous = 1; int answer = 2; for (int n = 2; n < position; ++n) { previousButOne = previous; previous = answer; answer = previous + previousButOne; } return answer; }
2. Choose meaningful names
The names of all variables, types, files and functions should be meaningful and not misleading. The name must correctly describe what it is. If you can not find a meaningful name, then there is a doubt that you understand the work of your code.
The naming system should be consistent and not cause unpleasant surprises. Make sure that the variable is always used only for the purpose its name implies.
A good choice of names is probably the best way to avoid unnecessary comments. Names best allow you to bring the code closer to the expressiveness of natural languages.
3. Break the code into independent functions.
How you break the code into functions and what names you give them can make the code understandable or completely incomprehensible.
Minimize any unexpected
side effects, however helpful they may seem. They will require additional documentation.
Write short functions. They are easier to understand. You can orient yourself in a complex algorithm if it is broken into small fragments with meaningful names, but this cannot be done in a shapeless mass of code.
4. Choose meaningful type names
As far as possible, describe the constraints or behavior using the available language features. For example:
- Defining a value that will not change, assign a constant type for it (use const in C).
- If the variable should not take negative values, use the unsigned type (if available in the language).
- Use enumerations to describe the associated dataset.
- Choose the type of variables correctly. In C / C ++, write the size to variables of type size_t , and the results of arithmetic operations with pointers to variables of type ptrdiff_t .
5. Use named constants.
A code like
if (counter == 76) is puzzling. What is the magic value of the number 76? What is the meaning of this test? Practicing magical numbers is flawed. They obscure the meaning of the code. It is much better to write this:
const size_t bananas_per_cake = 76; ... if (count == bananas_per_cake) { // }
If the code often contains the constant 76 (excuse me,
bananas_per_cake ), an additional advantage is achieved: when you need to change the content of bananas in the cake, it is enough to modify the code in one place and not perform a global search / replace the number 76, which is fraught with errors.
This applies not only to numbers, but also to constant strings. Take a closer look at
any literals in your code, especially if they occur multiple times. Wouldn't it be better to use named constants instead?
6. Highlight important code snippets.
Try to highlight the important code on the background of the usual material. In the right place should attract the attention of the reader. For this there are a number of techniques. For example:
- Reasonably place your ads in the classroom. First, there should be information about open objects, because it is the class user who needs it. Closed implementation details should be placed at the end, as they are less interesting to most readers.
- If possible, hide all non-essential information. Do not leave unnecessary garbage in the global namespace. In C ++, there is a pimpl idiom that allows you to hide the details of a class implementation. (Meyers 97).
- Do not hide the important code. Do not write more than one operator in a line and make this operator simple. The language allows you to write very ingenious for statements in the for loop, in which all the logic fits into one line with the help of many commas, but it is difficult to read such statements. Avoid them.
- Limit the nesting depth of conditional statements. Otherwise, it is difficult to notice the handling of really important cases behind the jumble of if and parentheses.
7. Combine related data
All related information should be in one place. Otherwise, you will force the reader not only to jump through hoops, but also to look for these hoops using ESP. The API for each component must be represented by a single file. If there is too much interconnected information to represent it in one place, the code architecture should be revised.
If possible, combine objects using language constructs. In C ++ and C #, you can combine elements within the same
namespace . In Java, the package mechanism is a means of combining. Related constants can be defined in the enumeration.
8. File Headers
At the beginning of the file, place a block of comments describing the contents of the file and the project to which it belongs. This does not require much work, but brings great benefits. Those who will have to accompany this file will get a good idea of ​​what they are dealing with. This title may have a special meaning: most software companies for legal reasons require that each source file contain a copyright statement. Usually file headers look like this:
/********************************************************* * File: Foo.java * Purpose: Foo class implementation * Notice: (c) 1066 Foo industries. All rights reserved. ********************************************************/
9. Correctly handle errors
Place all error handling in the most appropriate context. If there is a read / write disk problem, it must be processed in the code that deals with disk access. To handle this error, you may need to generate another error (such as the exception "I can not download the file"), passing it to a higher level. This means that at each level of the program the error should be an exact description of the problem in
its context . It makes no sense to handle the error associated with a disk failure in the user interface code.
Self-documenting code helps the reader to understand where the error occurred, what it means and what its implications are for the program at the moment.
10. Write meaningful comments.
So, we tried to avoid writing comments using other indirect methods of documenting code. But after you have made every effort to write a clear code, everything else needs to be provided with comments. To make the code easy to understand, you need to add the
appropriate amount of comments. What exactly?
First try other tricks. For example, check whether the code can be made clearer by changing the name or creating a helper function, and thus avoid commenting.
I am sure that already after introducing several of these principles into the habit you will make one programmer happier. And you will be this happy programmer. When? At the time of returning to work on his code of six months ago.