With such a loud headline, I thought to write an article first. No, in fact, it is quite possible that you are fine and this article is not about you. But very often, when people come from other languages, you can see how they are trying to “pull their ears” patterns from the language they are used to, and they often do not work well in Go.

In this article, I would like to collect a few typical mistakes that novice Go programmers make (including myself), and how to avoid these errors.
Packing
Packages in go have one feature that is not found in most other languages ​​— cyclic dependencies between packages are prohibited. Therefore, package partitioning suddenly becomes very important so that you can effectively develop large applications.
')
Very often, when broken into packages, people try to divide them into too small entities, and there are packages with abstract names like “common”, “domain”, “misc”, etc., which contain “common logic” between packages, in order to avoid creating loops. In principle, there is nothing wrong with this fact directly, but if you look at the standard library, then there are no such packages in it, although it operates with rather complex entities.
How does she do it? What is the difference between the standard library and your project? If you look closely, you can literally pick out a couple of basic points where things go different from other languages ​​in go:
1. Packages may be largeThe “net / http” package, for example, has more than 40 files, and more than 20 different public types are defined. They all belong to HTTP and it would be illogical to break them into several packages. Types like http.Header, http.Client, http.Server all look logical and there is no need to try, for example, to separate the client implementation from the server implementation just for the sake of getting smaller modules.
2. Packages can consist almost entirely of interfaces, global constants and variables.For example, there is the io package, which contains all the necessary interfaces and constants that relate to a very general concept of "input-output". But in this package there is generally no hint of implementation, because there are many implementations of these interfaces, and otherwise ring dependencies would have been obtained.
In short, the idea is as follows - break into packets based on the subject area to which the relevant entities relate. If some entities from different packages refer to each other, then perhaps you just need to shove them into one big package and you end up with a much more logical and understandable type hierarchy.
Use of interfaces
Interfaces must be defined in a separate package from the implementation. The implementation returns a specific type, not an interface. Almost always, interfaces must be defined in the package that
accepts the dependency , or separately. This allows you to simultaneously avoid circular dependencies, and makes your code more testable and flexible.
An example of the first approach (interface is defined in the receiver)You all know the fmt package. And they also probably wrote something like the following:
Pay attention to the String () method. In the fmt package, the fmt.Stringer interface is declared, and the implementation of this interface is accepted in the same package (albeit in this case implicitly).
The habr package, in turn, does not depend on the fmt package at all and the fmt package can freely import it if it wishes. This allows you to "gently" create cyclic dependencies, without the need to refactor code and rebuild the entire package structure.
A more detailed example (and rationale) can be seen at the following links (in English):
An example of the second approach (the allocation of interfaces in a separate package)If an interface (or some type) is needed more than in one place and it has value by itself, then it should be allocated in a separate package. This is how the io package appeared - it contains the most useful interfaces, constants and variables that somehow relate to I / O. In order not to introduce additional dependencies when importing this package, there is a separate package that contains convenient functions for working with interfaces from io - the ioutil package.
Interfaces from the io package turned out so successful that, as far as I know, Go is the only language in which the standard out-of-the-box library can print simultaneously to files, sockets, HTTP responses, byte buffers, etc., and this the functionality went to the standard library almost “free”, thanks to well thought-out abstractions.
General recommendations
In this (a little short and a little messy) article I mentioned 2 things that beginners often skip in recommendations for using go. I hope this article will help to achieve a better understanding of how not to feel pain when using this beautiful language.
Links
Most likely you have already read
Effective Go , but if not, I highly recommend it :). There are also two great articles that describe “good practices” when programming with go: