Control the consistency of the code in Go

Control the consistency of the code in Go 3r3644. 3r3661.  
If you consider the consistency of an important part of a quality code - this article is for you. 3r3644. 3r3661.  
Waiting for you:
Different ways of doing the same thing in Go (equivalent operations) 3r3658.  
Less obvious factors affecting the uniformity of your
Ways to increase the consistency of your project
3r3660. gocritic . 3r3644. 3r3661.  
Check for entry into the range
In math (and some programming languages, but not in Go) you could describe the range as 3r3668. low < x < high . Source code that will express this restriction cannot be written like this. At the same time there are at least two popular ways to issue a check for entering the range:
3r33595. //(A) left side alignment
x> low && x < high
//(B) center alignment
low < x && x < high
3r3608. 3r3661.  
Operator and not
You knew that in Go there is a binary operator & ^ ? It is called and-not 3r3669. and it performs the same operation as 3r3668. & applied to the result 3r3668. ^ from the right (second) operand. 3r3644. 3r3661.  
3r33595. //(A) using the & ^ operator (no space)
x & ^ y
//(B) use & c ^ (there is a space)
x & ^ y
3r3608. 3r3661.  

I conducted a survey to make sure that there may be different preferences at all. In the end, if the choice is in favor of 3r3668. & ^ would be unanimous, it would have to be a linter check, and not part of the choice in the matter of code consistency. To my surprise, supporters were found in both forms. 3r3644. 3r3661.  

Literals of real numbers


There are many ways to write a real literal, but one of the most frequently encountered features that can break consistency even within a single function is the style of writing the whole and real part (abbreviated or full). 3r3644. 3r3661.  
3r33595. //(A) explicit integer and real parts of 3r3683. ???r3r3681. ???r3r3681.
//(B) implicit whole and real parts (short record)
.0 3r3683. 1. 3r3669. 3r3608. 3r3661.  
3r33333. LABEL or label? 3r33333. 3r3661.  

Unfortunately, there are no established conventions for label naming. All that remains is to choose one of the possible ways and stick to it. 3r3644. 3r3661.  
3r33595. //(A) all uppercase
//(B) upper camel case
goto TryTo
//(C) lower camel case
goto beConsistent
3r3608. 3r3661.  

is also possible. snake_case , but nowhere but Go assembler, I have not seen such tags. Most likely, this option should not be followed. 3r3644. 3r3661.  
3r33333. Specifying the type for untyped numeric literal 3r3661.  
3r33595. //(A) type is to the left of "="
var x int32 = 10
const y float32 = ???r3r3681.
//(B) type is to the right of "="
var x = int32 (10)
const y = float32 (1.6)
3r3608. 3r3661.  
3r33333. Transferring the closing bracket of the function being called 3r33333. 3r3661.  

In the case of the simplest calls that fit on one line of code, there can be no problems with parentheses. When, for one reason or another, a function or method call sprawls out into several lines, several degrees of freedom appear, for example, you will need to decide where to put the closing bracket argument. 3r3644. 3r3661.  
3r33595. //(A) a closing bracket on the same line with the last argument
multiLineCall (
//(B) the closing bracket is transferred to the new line
multiLineCall (
3r3608. 3r3661.  
3r33386. 3r33232. go-consistent 3r32424. 3r3661.  

Above we have listed the list of equivalent operations. 3r3644. 3r3661.  

How to determine which one to use? The most simple answer: the one that has a higher frequency of use in the considered part of the project (as a special case, in the whole project). 3r3644. 3r3661.  

Program 3r3632. go-consistent analyzes the specified files and packages, counting the number of uses of one or another alternative, suggesting to replace less frequent forms with idiomatic ones within the analyzed part of the project, those with the maximum frequency. 3r3644. 3r3661.  
3r3406. 3r3407. A straightforward calculation [/b] 3r3409. 3r33434. 3r3661.  

At the moment, the weight of each entry is equal to one. Sometimes this leads to the fact that one file dictates the style of the entire package just because of the fact that this operation is more often used in it. This is especially noticeable in relation to rare operations, such as creating an empty 3r36668. map

. 3r3644. 3r3661.  

How this is the optimal strategy is not yet clear. This part of the algorithm will be easy to modify or allow users to choose one of several suggested ones. 3r3644. 3r3661.  
3r33434. 3r3777. 3r3777. 3r3661.  

If 3r3668. $ (go env GOPATH) /bin located in the system 3r3668. PATH , then the next command will install go-consistent :

3r33595. go get -v
go-consistent --help # To verify installation of
3r3608. 3r3661.  

Returning to the boundaries of consistency, here’s how to check each one of them:

  • Check the consistency within a single file, you can run 3r3668. go-consistent on this file  
  • The consistency inside the package is calculated at startup with one package argument (or with all the files in this package indicated) 3r3658.  
  • Calculating global consistency will require passing all packets as arguments to 3r3658.  
    3r3660. 3r3661.  

    3r3668. go-consistent it is designed in such a way that it can give an answer even for huge repositories, where it is quite difficult to load all packages into memory at the same time (at least on a personal machine without a huge amount of RAM). 3r3644. 3r3661.  

    Another important feature is the zero configuration. Run 3r3668. go-consistent without any flags and configuration files - this is what works for 99% of cases. 3r3644. 3r3661.  
    3r3663. 3r3664. Warning : the first run on the project can give a large number of warnings. This does not mean that the code is written poorly; it is rather difficult to control consistency at such a micro level and it is not worth the effort if the control is performed exclusively by hand. 3r3670. 3r3661.  

    3r3634. go-namecheck 3r32424. 3r3661.  

    Reducing the consistency of the code can inconsistent naming function parameters or local variables. 3r3644. 3r3661.  

    Most Go programmers obviously 3r3668. erro less successful name for the error than 3r3668. err . And how about s against 3r3668. str ? 3r3644. 3r3661.  

    The task of checking the consistency of variable names is solved by methods go-consistent can not. It is difficult to do without the manifesto of local conventions. 3r3644. 3r3661.  

    3r3634. go-namecheck determines the format of this manifest and allows its validation, simplifying the following of the entity naming standards defined in the draft. 3r3644. 3r3661.  

    For example, you can specify that for function parameters 3r36365. having type 3r36464. string It is worth using the identifier s instead 3r3668. str . 3r3644. 3r3661.  

    This rule is expressed in the following way:

    3r33595. 3r3668. {"string": {"param": {"str": "s"}}} 3r3608. 3r3661.  
    • 3r3668. string - a regular expression that captures the type of interest 3r33658.  
    • 3r3668. param 3r3669. - the scope of applicability of the rules of replacement (scope). There may be several  
    • A pair of 3r3668. "str": "s" indicates replacement with 3r3668. str on 3r3668. s . There may be several  
      3r3660. 3r3661.  

    Instead of replacing 1-to-? you can use a regular expression that captures more than one identifier. Here, for example, the rule that requires replacing the prefix re for variables of type 3r3r6666. * regexp.Regexp to suffix RE . In other words, instead of reFile the rule would require the use of 3r3668. fileRE . 3r3644. 3r3661.  
    3r33595. 3r3668. {
    "regexp .Regexp": {
    "local + global": {"^ re[A-Z] w * $": "use RE suffix prefix"}
    } 3r3608. 3r3661.  

    All types are considered ignoring pointers. Any level of indirection will be removed, so there is no need to define separate rules for pointers to the type and the type itself. 3r3644. 3r3661.  

    A file that describes both rules would look like this:

    3r33595. 3r3668. {
    "string": {
    "param": {
    "str": "s",
    "strval": "s"
    }, 3r3681.}, 3r3681. "regexp .Regexp": {
    "local + global": {"^ re[A-Z] w * $": "use RE suffix prefix"}
    } 3r3608. 3r3661.  

    It is assumed that the project starts with an empty file. Then, at a certain point, for code review, a request is made to rename a variable or field in the structure. Natural response may be requested to consolidate these previously informal requirements in the form of a verified rule in the naming conventions file. The next time the problem can be found automatically. 3r3644. 3r3661.  

    Installed and used go-namecheck similar to 3r3668. go-consistent , except that in order to get the correct result, you do not need to run a check on the whole set of packages and files. 3r3644. 3r3661.  

    Conclusion 3r32424. 3r3661.  

    The features discussed above are not critical separately, but affect the overall consistency in the aggregate. We considered code homogeneity at the micro level, which does not depend on the architecture or other features of the application, since these aspects are most easily validated with an almost zero number of false positives. 3r3644. 3r3661.  

    If according to descriptions above 3r33232. go-consistent or 3r3634. go-namecheck you like it, try running them on your projects. Feedback is a really valuable gift for me. 3r3644. 3r3661.  

    3r3664. It is important 3r36565. : if you have any idea or addition, please tell about it! 3r3661.  
    There are several ways:

    • Write in the comments to this article  
    • 3r3653. Create an issue go-consistent 3r3658.  
    • Implement your utility and let the world know about it  
      3r3660. 3r3661.  
      3r3663. 3r3664. Warning : add
    go-consistent and /or 3r3668. go-namecheck in CI can be too radical an action. Running once a month, followed by editing all inconsistencies may be a better solution. 3r3670. 3r3777.
    3r3674. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//"""_mediator") () (); 3r3675.
+ 0 -

Add comment