Rebuses in the code, and how to decipher them. Secret ID Power

    Clean code reads well-written prose.
    Grady Butch in the book "Clean Code"

    Rebus like code




    What is a rebus? This is an encrypted message. The author of the rebus takes an ordinary human text and encodes it with pictures, numbers and letters. And we are looking at this encryption and trying to read the source text.

    The rebus has two hypostases. On the one hand, the rebus is the source unencrypted text, and on the other, the cipher pattern. The text is the “what” of the rebus, its meaning, the message. Pictures are “how”: exactly how the message is encrypted, by what means. Guessing the rebus, we translate "how" to "what."

    Drawings are the language of the rebus, its arsenal of expressive means. Rebuster as it speaks to us with the help of these drawings, says something. He is not allowed to use normal human words.

    Here is how the puzzles are read:



    Code as a rebus


    The program code has something in common with the rebus: it also has its “what” and “how”. And he, too, sometimes has to decipher.

    “What” of the code is its purpose, meaning, the effect and the final result that we expect from it. What exactly is he doing .

    “How” of the code - in what concrete way will it perform its “what”, with what specific assignments, multiplications, comparisons; implementation of the algorithm, instructions processor. This is the permitted language of the code, its arsenal of expressive means.

    Martin Fowler talks about it this way ( “Length of function” , original ):
    Smalltalk in those years worked on black and white cars. If you needed to highlight text or graphics, you had to reverse the video. The class in Smalltalk, which is responsible for graphics, contained the 'highlight' method, and in its implementation there was only one line - the call to the 'reverse' method. The name of the method was longer than the implementation, but it did not matter, because there was a long distance between the intention and the implementation of this code.
    Here highlight is “what”. In Martin's terminology - intention : “highlight a fragment of the image”. The name expresses what this function does. Reverse is “how”, implementation . How exactly the highlighting is performed (using image inversion). That is the difference between "what" and "how."

    Despite the fact that the name of the method is longer than its implementation, there is a sense in the existence of such a method for a very simple reason. When we see a call to reverse in the code, we must understand or remember that the inversion of the image is applied in order to make this image more noticeable. When we see a highlight, we simply read: “make this fragment more noticeable.” In the first case, we spend a little mental effort to understand the mission entrusted to the code, in the second - no. In the first case, we see a rebus in front of us that needs decoding, in the second case - a story in plain language.

    A programmer, when writing a program, is like a reboiler. The programmer encrypts the human description of the algorithm using the available programming language tools (more primitive than the human language). Encrypts “what” with “how”. And later, he or his colleague reads the code, deciphering these rebuses the initial description of the algorithm. If, in the process of reading the code, we cannot immediately understand to what result the execution of each fragment will result, that is, what is the purpose, the meaning of the code, then this code is a rebus, and it must be rewritten in an understandable language.

    The problem of rebuses in the code is that they alwaysrequire mental effort. Even if we do not perform a full set of decryption operations in our mind, and we’ll just stupidly remember the meaning of a rebus, it will still create a load: first, to remember its meaning, and secondly, at the moment of record transformation rebus to this value.

    Deciphering the rebus in the course of reading the code - these are the very mental transformations that Tim Ottinger speaks about in the book “Clean Code”. True, there he argues about them in the context of assigning clear names to variables, but the problem is affected by exactly the same one. Tim's word:
    As a rule, programmers are very smart. And smart people sometimes love to show the power of the intellect, demonstrating their mental juggling abilities. After all, if you remember that the variable r contains a URL with a remote host and a schema converted to lowercase, this clearly indicates your mind.
    One of the differences between a smart and professional programmer is that the professional understands: clarity is above all. Professionals use their strength for the good and write code that is understandable to other people.
    Even a small load from each rebus can turn into a problem if there are many such rebuses. You must have seen the code, the reading of which was simply exhausting. Know: in your tiredness the rebuses in the code are to blame. Rebus exacerbates the fatigue even of its own author directly in the process of writing code. After all, in the process of writing code, the programmer also continuously re-reads what is written. Despite the fact that the author does not decipher his own puzzles, but simply remembers, they still create a load. The trap is that the author simply does not see the rebuses in his own code ! Try to imagine how much mental effort you can save in the evening if you start getting rid of the rebuses in your code in the morning!

    So, to reduce fatigue from writing and reading code, you need to avoid rebuses. But how to do that?

    Language code. ID Power


    I agree with Grady Butch’s statement that clean code is read like good prose. This is a necessary, though not sufficient condition. Most of us will intuitively understand what is at stake, but I would like to get at least some definition: what it is - good prose.

    I asked my fellow writers: how does good prose differ from bad prose? Everyone answered differently, but one way or another everyone emphasized the importance of language: it must be rich, it must create clear images in the mind and soul of the reader. When the reader easily has a clear picture that the author wanted to draw to him - we are dealing with good prose.

    The code tells the processor what to do. A good code at the same time tells the programmer - and, moreover, extremely truthfully! - what is the code doing? That is, he sets out his algorithm as close as possible to how the author himself would have done in natural language. Our code must do this very well, otherwise an unbridled maniac with a chainsaw or a shotgun can come to our home . The code should not be a rebus.

    What means does the code have to not be a rebus?

    The story on behalf of the code will be easily understood by the person if the code itself speaks in human language. This can be achieved only with the help of identifiers: the names of functions, classes, variables and constants - becauseonly in identifiers can we use any words of human language we need .

    Of course, the keywords of a programming language are human words too, but their vocabulary is too wretched. Something like the language of Ellochka-Cannibals - you can’t write good prose on it.

    Therefore, it is vital that the program code contains as many correctly chosen identifiers as possible. So that their whole set forms the very well-written prose.

    See how easy it is to read lines of code when the names of variables and methods are well chosen:

    pageData.hasAttribute("Test")
    dom_tree.to_html()
    emails_str.split(',')

    Looking at these short phrases, it is easy to understand what they are talking about. We know what result we get, because the identifiers tell us about it. Now imagine that at the place of each such call is its implementation - how much will the reading speed of such an “encrypted” code decrease?

    Many of the simplest methods of refactoring: named constants, selecting a method, replacing a variable with a method call, explaining a variable, splitting a temporary variable, etc. — all mean how to make the code speak human language, in other words, how to avoid puzzles .

    Rebuss method


    When I read "Clean Code", I occasionally visited the thought: "What the hell!".

    From the height of his 40 years of experience, Robert Martin gives us tips on how to make the code better. For example:
    The first rule: functions must be compact. The second rule: functions should be even more compact.
    And he immediately admits that he cannot scientifically substantiate his statement. Honestly, unscientific, he is also not doing well. The requirement of compactness of the function is already beginning to look like a dogma - that is why the debate about the length of the function has not subsided for so many decades.

    Bob also offers to write each function so that it performs only one operation. Moreover, this one operation is not very clear. We have to call for help the principle of a single level of abstraction, which further confuses the situation. All this is too vague.

    A more pragmatic approach to the question is Martin Fowler.

    It seems to me that the argument about the separation of intent and realization has more meaning. If, looking at the code snippet, you need to make an effort to understand what it is doing, then you need to put it into a function and give it a name in accordance with this “what”. Then the next time the purpose of the function will be immediately obvious, and in most cases you will not be worried about how the function performs its work.

    Original
    The argument that makes most sense to me, however, is the separation between intention and implementation. If you have to spend effort into looking at a fragment of code to figure out what it's doing, then you should extract it into a function and name the function after that “what”. That way when you read it again, the purpose of the function leaps right out at you, and most of the time you won't need to care about how the function fulfills its purpose — which is the body of the function.
    Already better. Do you understand now what Martin wanted to say in this passage? He meant: let's eliminate the puzzles. Let the code itself tell us what result will be obtained, and how - let it be hidden somewhere far away, in the definition of a function. Let all the puzzles be decoded. No rebuses - no effort.

    In no case should not blindly apply the methods of refactoring. This is so obvious, but how to understand when refactoring is really needed, and when - no? The rebus method says: if the rebus has not disappeared after refactoring, then refactoring is not needed .

    If you cannot find such a name for a new function that will clearly explain what is happening in it - this is a bell, that you are doing something wrong here. Try to allocate a slightly different code snippet to the function - to which you can quickly find a clear and short name.

    An example of decoding rebuses in code (not very successful)


    As such, I will cite a fragment I liked from the book “Clean Code”. Before refactoring, we see a code full of rebuses. Refactoring was done by the author of the book in accordance with the rules of good code promoted by him, and - this is a coincidence - the refactor code looks exactly like the code in which the rebuses are decoded.

    The author of refactoring has fully applied human-readable identifiers (class names, methods, and variables) to indicate what the code actually does. The only pity is that not everywhere it turned out well, and in some places new riddles have appeared instead of the previous puzzles.

    For example, the include method, which is most often used in this passage.

    privatevoidinclude(String pageName, String arg)throws Exception {	
    	WikiPage inheritedPage = findInheritedPage(pageName);
    	if (inheritedPage != null) {
    		String pagePathName = getPathNameForPage(inheritedPage);
    		buildIncludeDirective(pagePathName, arg);
    	}
    }

    The name does not reflect what is happening in the implementation. What include and where?

    Looking at the call to this method:

    include("TearDown", "-teardown");

    It is impossible to say what result the author of the code was going to achieve.

    Next: what does buildIncludeDirective do? Judging by the name, it should make some kind of inclusion directive, so what? Get her back? But no. He immediately adds it to the overall result.

    And here's another updatePageContent. What does updatePageContent tell us about what result we get after calling the method? Never mind. Some kind of page content will be replaced by what is not known at all. What was the purpose of refactoring here called method allocation? Did he get rid of the rebus? It did not help, but only tangled the code more. Here we have the very case when the body of the method is preferable. Design

    pageData.setContent(newPageContent.toString());

    much clearer mysterious updatePageContent ().

    As an entertainment, I suggest readers look for what else there are bad places in the refactored code .

    To justify Bob, I can say that in the current version of FitNesse this code is no longer there. Apparently, in turn, he, too, was once a factor.

    Conclusion


    The length of a function is too vague a criterion for determining the quality of a function. “Short functions” is not equal to “good functions”. The length of the function is not a criterion, everything, forget it.

    A good code should give answers to the questions of the programmer - why is he (the code) here, what the hell is he doing here is achieving results. These responses can only be given by identifiers.

    As an example of what answers the code should not give, I want to cite an excerpt from one fun book.
    “I am Ronan, Winner of Evil,” he said slowly. - And this is Tarl. We want
    to ask you some questions. If you lie, you die. Got it?
    “I, uncle, always,” he breathed. - You are welcome. All-all-all say.
    “That's nice,” Ronan continued. - Name?
    - Ronan, Winner of Evil.
    - Yes, not mine, idiot!
    “Ah, yes, yes, then Tarl,” the orc replied apologetically.
    - And not mine! - muttered Tarl. - Your name, cudgel! Name!
    “A name is a name that I use to distinguish myself from others,” said the orc.
    - Well, here come this name! - screamed Tarl.
    Orca suddenly dawned.
    - BUT! Acne!
    - So, Pimple, what are you doing here?
    “I’m putting my pants on,” came the truthful answer.
    Ronan wrinkled his nose in disgust.
    “No, I ask what your gang of orcs are doing here!”
    Eyes Pimples swirled rapidly, looking around the scene.
    “Most are lying around without heads,” he murmured.
    Tarl touched Ronan by the shoulder.
    “Let me try,” he said confidently, and turned to the frightened orc. “Tell me,
    Pimple,” he continued, “why are you here?”
    - Oh, uncle, and do not ask. Existential philosophy for me is just a dark forest.
    - Listen, you, dragon burp, - he growled dully. “Your orc gang had a special
    reason to come here.” What is there in the forest?
    - There are a lot of trees here.
    Ronan's eyes bulged, and Tarl turned away. Pimple, feeling that he gave the wrong answer, which was expected, began to mutter further.
    “And if you want to know about the reason, and not about the forest, it’s all because the man at the pub
    paid us to come here and kill you.”
    James Bibby, "Ronan the Barbarian"

    Also popular now: