Code Writing Principles

    After reading another bad advice about the standards for code design ( one , two , thousands of them ), I could not resist, so as not to share my thoughts on this topic. For many years, I nurtured in my subconscious mind the feeling "something is wrong here." And now, it's time to decide what is wrong and why. At the risk of being thrown by rotten bananas, I still write this article here, and not on my personal blog, because it 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 appropriateness is poorly substantiated
    2. They declare specific rules, instead of principles.
    3. These rules are poorly justified and are often built on conflicting principles.

    In the mentioned article, the whole rationale for the need for standardization is:
    A good guide on code design will help achieve the following:
    1. Establishment of a code quality standard for all sources;
    2. Ensuring consistency between source codes;
    3. Adherence to standards by all developers;
    4. Increased productivity.

    1. [here is a picture about another standard] "A standard is needed so that there is a standard" - does not justify its existence.
    2. In any more or less large project, there will always be a bunch of code that does not correspond to the current trends in the design style: changes to the style guide over time, lay down code, code from third-party libraries, auto-generated code. This is inevitable and not so bad as it might seem at first glance.
    3. The same as the first paragraph.
    4. It’s already warmer, but again, it doesn’t justify why productivity from 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 one style, which makes any code written “not according to the rules” cause irritation, anxiety, anger and a desire to impose your habits on others. It is much more useful to learn to perceive the source, ignoring the design. And for this, the presence of miscellaneous code is even useful. I'm not saying 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 “a standard is needed so that everything is standard”.

    How to write a competent standard:
    1. Defined with goals
    2. Formulate principles and validate them for compliance with goals
    3. Formulate a minimum of rules for the implementation of these principles

    So let's try


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

    What is the support:
    1. writing a new code
    2. changing the existing one, including the automatic one
    3. searching for the necessary part of the code
    4. analyzing the logic of the code
    5. searching for the source of incorrect behavior
    6. comparing different versions of the same code
    7. transferring the code between branches

    What principles will help to achieve your goal:

    1. The lines of the file should be as independent as possible.

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

    Despite the fact that this simple idea skips in one of the rules mentioned at the beginning of the article, in another 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 can be beautiful, but not practical at all for the following reasons:
    1. Adding a line with a longer name (for example, icon - person-premium) will change all the lines in the group.
    2. Automatic renaming in most cases will override alignment (for example, when changing icon - person to icon - user in most tools).
    3. Sometimes spaces become unreasonably long, which makes it more difficult to perceive code.

    You can also notice an extra seven-colon (semicolon, semicolon). The only reason for his appearance in this place is the blind following of the rules of the style guide, not understanding its principles.
    In multi-line rules, it is really necessary that the lines added to the end do not lead to a change in existing ones. For instance:

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

    	.icon {
    		display: inline-block;
    		width: 16px;
    		height: 16px; /* добавили семиколон */
    		background-image: url(/img/sprite.svg) /* полезное изменение */
    	}
    

    If you write in javascript and can afford to give up ie8, then you can use tail punctuation in literals too:

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

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

    Another aspect of this principle is to arrange on separate lines those entities that usually change independently. That is why individual css properties should not be placed on one line. Moreover, do not get carried away with complex properties.

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

    	.icon {
    		background: url(/img/sprite.svg) 10px 0; /* смещения в спрайте жестко связаны с самим спрайтом */
    		background-color: black; /* фоновый цвет меняется независимо от картинки */
    	}
    

    Another striking example of a 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 us to add / remove / change links without touching adjacent lines, but there is still a strong connection between them because of which we cannot, for example, write like this:

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

    To add such logic, you have to break the chain into two and fix their beginning:

    	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) into one basket (file / directory).

    If it seems to you that the code lacks the so-called “sections”, then most likely you have reached the upper threshold of perception, when it is already difficult to find the necessary sections 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 a directory. By arranging the code in a file system hierarchy, you can pretty well organize all the arriving number of entities. You probably do about the same thing in runtime, placing the code in a hierarchy of namespaces, so there is no reason to have different namespaces for one entity: in runtime and in the file system. Simply put, the names and hierarchy of directories must match the names and hierarchy of namespaces.

    Often you can see 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, partially the same, but with inevitable differences. Think about it: is the file type important? Namespaces are much more important. So why do we need these typed baskets? Isn't it better to store all the files of one module side by side, in the same directory, whatever their types are? Moreover, types can change. 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, when developing the next form, you don’t have to rush between directories, laying out files in different places. In case of removal of the component, you will not forget to delete the pictures. Yes, and transferring components between projects becomes much easier.

    3. The programming language is fundamentally not a natural language.

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

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

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

    JS partially understands the two-dimensionality of the code, so in it the seven-colons at the end of the lines are tautologies:

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

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

    But CSS does not understand, therefore, you cannot 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 auto-completion, 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 postfix "s" (which in most cases gives a plural form of a word) for the purpose of uniformity gives words that are illiterate 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 requirement that each programmer have a good knowledge of all English word forms.

    5. Full and identical names of one entity in different places

    Searching by name is a fairly common operation when working with unfamiliar code, so it is important to write code so that the name can easily find the place where it is defined. For example, you open the page and find the class “b-user__compact” there. You need to find out how he appeared there. A search on the string “b-user__compact” returns nothing, because the name of this class is not found anywhere else - it is glued from pieces. And all because someone decided to reduce copy-paste at the cost of complicating the debug:

    	//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 so. If you glue the name from the pieces, then make sure that these pieces contain the full namespace of the module where it is introduced 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'
    

    From the resulting “my-user__my-block-compact” class, you can immediately see that it is glued from two pieces: one is defined in the “my / block” module, and the other in “my / user” and both are easily found on 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 even more so:

    	/*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 this is probably the end. All projects are different: somewhere you need quick prototyping, and somewhere long support; somewhere statically typed languages ​​are used, and somewhere dynamically. One thing is important here - before declaring any rules as the only true ones, formulate goals, principles, and make sure that the rules really correspond to them and discard anything superfluous. There is no need to bind yourself hand and foot, being afraid to stumble if it is enough to put the railing in the right places.

    Also popular now: