📜 ⬆️ ⬇️

Principles of writing code

After reading the next bad advice about the standards for the design of the code ( one , two , thousands of them ), I could not resist not to share my fabrications on this topic. For many years I have been nurturing in my subconscious the feeling “something is wrong here”. And now, it's time to decide what is wrong, and why. At the risk of being banned with rotten bananas, I still write this article here, and not in my personal blog, because this is a very important topic and I want as many developers as possible to understand its essence and, perhaps, reconsider their views on life ... code.

Coding Standards


Typical problems of many such style guides:
1. Their expediency is poorly justified.
2. They declare specific rules, instead of principles.
3. These rules are poorly substantiated and often built on conflicting principles.

In the above article, the whole rationale for the need for standardization is:
A good guide to code design will allow you to achieve the following:
1. Establishing a code quality standard for all sources;
2. Ensuring consistency between the sources;
3. Following the standards by all developers;
4. Increase productivity.

1. [here is a picture about another standard] “A standard is needed so that there is a standard” - does not justify its presence.
2. In any more or less large project there will always be a bunch of code that does not correspond to the current trends of the design mode: changes in style guide with time, legacy code, code of third-party libraries, auto-generated code. This is inevitable and not so bad as it might seem at first glance.
3. Same as the first paragraph.
4. It is already warmer, but again, it does not justify why the productivity of this should grow, and most importantly - by how much.

From my own experience, I can say that the worst solution is to get used to writing and reading code in any one style, from which any code written “not by the rules” will cause irritation, anxiety, anger and a desire to impose one’s habits on others. Much more useful to learn to perceive the source, ignoring the design. And for this the presence of a differently shaped code is even useful. I do not say that you need to relax and write everything in one line, but in order not to do this, there are much more practical reasons than “the standard is needed so that everything is standard”.
')
How to write a literate standard:
1. Defined goals
2. Formulate principles and validate them for purpose
3. Formulate a minimum of rules to implement these principles.

So, let's try


Objective: reduce the cost of support by imposing restrictions on yourself and the team.

What is the support:
1. writing new code
2. change existing, including automatic
3. search for the necessary part of the code
4. analysis of the logic of the code
5. search for the source of misconduct
6. comparison of different versions of the same code
7. code transfer between branches

What principles will help to achieve the goal:

1. File strings should be as independent as possible.

The reason is simple: if changing one line requires changing the others, this increases the risk of conflicts when merging branches. Each conflict is an additional, sometimes significant, time to resolve it.

Despite the fact that this simple idea skips in one of the rules mentioned in the beginning of the article, in the other rule we see a clearly contradictory recommendation:

.icon--home { background-position: 0 0 ; } .icon--person { background-position: -16px 0 ; } .icon--files { background-position: 0 -16px; } .icon--settings { background-position: -16px -16px; } 

Vertical alignment - it can be beautiful, but it is not at all practical for the following reasons:
1. Adding a line with a longer name (for example, icon - person-premium) will change all lines in the group.
2. Automatic renaming in most cases will kill alignment (for example, when changing icon - person to icon - user in most tools).
3. Sometimes spaces become unnecessarily long, which makes it harder to perceive the code.

Also here you can see the extra semicolon (semicolon, semicolon). The only reason for his appearance in this place is blindly following the rules of the style guide, not understanding its principles.
In multi-line rules, it is really necessary that the additions to the end of the line do not lead to changes in existing ones. For example:

  .icon { display: inline-block; width: 16px; height: 16px } 

  .icon { display: inline-block; width: 16px; height: 16px; /*   */ background-image: url(/img/sprite.svg) /*   */ } 

If you are writing to javascript and can afford to refuse ie8, you can use tail punctuation in literals:

  var MainThroubles = [ 'fools', 'roads', 'fools on roads', ] 

  var GodEnum = { father : 0, son: 1, holySpirit : 2, } 

Another aspect of this principle is to place on separate lines those entities that change, as a rule, independently. That is why the individual css-properties should not be placed in one line. Moreover, you should not get involved in complex properties.

  .icon { background: url(/img/sprite.svg) 10px 0 black; } 

  .icon { background: url(/img/sprite.svg) 10px 0; /*         */ background-color: black; /*       */ } 

Another vivid example of the violation of this principle is the chain of method calls:

  messageProto .clone() .text( text ) .appendTo( document.body ) .fadeIn() 

Here we tried to place each link on a separate line, which allows you to add / delete / change links without touching the adjacent lines, but between them there is still a strong link due to which we cannot, for example, write like this:

  messageProto .clone() .text( text ) if( onTop ){ .appendTo( document.body ) } else { .prependTo( document.body ) } .fadeIn() 

To add this logic, you will have to break the chain into two and fix their beginnings:

  var message = messageProto .clone() .text( text ) if( onTop ){ message.appendTo( document.body ) } else { message.prependTo( document.body ) } message.fadeIn() 

But with such a record, we have complete freedom of action:

  var message = messageProto.clone() message.text( text ) message.appendTo( document.body ) message.fadeIn() 

  var message = messageProto.clone() message.text( text ) if( onTop ){ message.appendTo( document.body ) } else { message.prependTo( document.body ) } message.fadeIn() 

2. Do not dump all the eggs (code) in one basket (file / directory).

If it seems to you that the code lacks the so-called “sections”, then most likely you are getting to the upper threshold of perception, when it is already difficult to find the necessary parts of it. In this case, the natural desire is to create a table of contents. But the table of contents in the form of comments at the beginning of the file does not compare with the table of contents in the form of a list of files in the directory. Placing code in a file system hierarchy you can pretty well organize all the arriving number of entities. You probably do the same thing in runtime, putting the code in the hierarchy of neimspaces, so there is no reason to have different namespaces for the same entity: in runtime and in the file system. Simply put, the names and directory hierarchy must match the name and namespace hierarchy.

Often you can find the placement of files in several large baskets: “all pictures”, “all scripts”, “all styles”. And as the project grows, a hierarchy appears in each of them, partly the same, but with inevitable differences. Consider: is the file type important? Namespace is much more important. So why do we need these typed baskets? Isn't it better to keep all files of one module side by side, in one directory, whatever their types? Moreover, the types may vary. Compare:

  img/ header/ logo.jpg menu_normal.png menu_hover.png main/ title-bullet.png css/ header/ logo.css menu.css main/ typo.css title.css js/ menu.js spoiler.js tests/ menu.js spoiler.js 

  header/ logo/ header-logo.jpg header-logo.css menu/ menu.css menu.js menu.test.js menu_normal.png menu_hover.png main/ title/ title.css title-bullet.png spoiler/ spoiler.js spoiler.test.js 

In the second case, developing the next form, you do not have to rush between directories, laying out files in different places. In the case of the removal of components, you will not forget to remove the pictures. Yes, and transfer components between projects becomes much easier.

3. A programming language is not a natural language in principle.

Unlike written language, which is read strictly sequentially, the program code in modern programming languages ​​is a two-dimensional structure. They often do not have, for example, the need to put dots (semicolons) at the end of sentences:

  .icon--settings { background-position: -16px -16px; } 

  .icon--settings { background-position: -16px -16px } 

JS partially understands the two-dimensional nature of the code, so in it the seven-columns at the end of the lines are tautologies:

  function run() { setup(); test(); teardown(); } 

  function run() { setup() test() teardown() } 

But CSS does not understand, so in it, one can not do without them:

  .icon { display: inline-block; width: 16px; height: 16px; } 

To improve the perception of language tokens, spaces can be placed at all according to the rules of writing:

  say({message: concat("hello", worldName.get())}) 

  say({ message : concat( "hello" , worldName.get() ) }) 

  say( document.body , { message : concat( "hello" , worldName.get() ) } ) 

For more convenient work with autocompletion, words can change their order, lining up from more important and specific to less important and general:

  view.getTopOffset() 

  view.offsetTop_get() 

  view.offset.top.get() 

And the rule of naming collections with the prefix "s" (which in most cases gives the plural form of a word) for the sake of uniformity gives illiterate words from the point of view of the English language:

  for( man of mans ) man.say( 'Hello, Mary!' ) 

But this is a lesser evil compared to the demand from every programmer for a good knowledge of all English word forms.

5. Full and identical names of the same entity in different places.

Search by name is quite a frequent operation when working with unfamiliar code, so it is important to write code so that it is easy to find the place where it is defined by name. For example, you opened the page and found the class “b-user__compact” there. You need to find out how he appeared there. Searching for the string “b-user__compact” does not produce anything, because the name of this class is not found anywhere else - it is glued together from pieces. And all because someone decided to reduce copy-paste at the cost of complicating the debag:

  //my/block/block.js My.Block.prototype.getSkinModClass = function(){ return 'b-' + this.blockId + '__' + this.skinId } My.Block.prototype.skinId = 'compact' //my/user/user.js My.User.prototype.blockId = 'user' 

Do not do this. If you glue the name of the pieces, then make sure that these pieces contain the full namespace of the module where it is put into use:

  //my/block/block.js My.Block.prototype.getSkinModClass = function(){ return this.blockId + '__' + this.skinId } My.Block.prototype.skinId = 'my-block-compact' //my/user/user.js My.User.prototype.blockId = 'my-user' 

According to the received class “my-user__my-block-compact”, it is immediately apparent that it is glued together from two pieces: one is defined in the “my / block” module, and the other is in “my / user”, and both are easily located by the corresponding substrings. Similar logic is possible when using css-preprocessors, where we encounter the same problems:

  //my/block/block.styl .b-block { &__compact { zoom: .75; } } //my/user/user.styl .b-user { @extends .b-block; color: red; } 

  //my/block/block.styl .my-block { &__my-block-compact { zoom: .75; } } //my/user/user.styl .my-user { @extends .my-block; color: red; } 

If you do not use css-preprocessors, then all the more:

  /*my/block/block.css*/ .m-compact { zoom: .75; } /*my/user/user.css*/ .b-user { color: red; } 

  /*my/block/block.css*/ .my-block-compact { zoom: .75; } /*my/user/user.css*/ .my-user { color: red; } 

Summary


It would be possible to continue for a long time, but perhaps we will end there. All projects are different: somewhere you need rapid prototyping, and somewhere long support; somewhere statically typed languages ​​are used, and somewhere dynamically. The important thing here is that before declaring any rules as the only true ones, formulate goals, principles, and make sure that the rules really correspond to them and discard everything unnecessary. There is no need to tie yourself hand and foot, afraid to stumble, if you just put the railing in the right places.

Source: https://habr.com/ru/post/236785/


All Articles