This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
</div>
</aside>
</div>
@ -975,7 +965,7 @@ This repo is open to <a href="https://hacktoberfest.com/">Hacktoberfest</a>!
</span></code></pre></div>
<p>When calling <code>foo</code>, we will pass a type argument of any type. Passing a type argument is called instantiation because the work is done at compile time which keeps type safety as part of the core language features and avoids runtime overheads.</p>
<p>Let’s get back to the <code>getKeys</code> function and use type parameters to write a generic version that would accept any kind of map:</p>
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
</div>
</aside>
</div>
@ -2748,7 +2738,7 @@ This repo is open to <a href="https://hacktoberfest.com/">Hacktoberfest</a>!
<p>Avoiding shadowed variables can help prevent mistakes like referencing the wrong variable or confusing readers.</p>
</details>
<p>Variable shadowing occurs when a variable name is redeclared in an inner block, but this practice is prone to mistakes. Imposing a rule to forbid shadowed variables depends on personal taste. For example, sometimes it can be convenient to reuse an existing variable name like <code>err</code> for errors. Yet, in general, we should remain cautious because we now know that we can face a scenario where the code compiles, but the variable that receives the value is not the one expected.</p>
<p>Writing readable code is an important challenge for every developer. Striving to reduce the number of nested blocks, aligning the happy path on the left, and returning as early as possible are concrete means to improve our code’s readability.</p>
@ -2803,7 +2793,7 @@ This repo is open to <a href="https://hacktoberfest.com/">Hacktoberfest</a>!
<li>If the initialization requires us to set a state, that has to be done through global variables.</li>
</ul>
<p>We should be cautious with init functions. They can be helpful in some situations, however, such as defining static configuration. Otherwise, and in most cases, we should handle initializations through ad hoc functions.</p>
<h3id="overusing-getters-and-setters-4">Overusing getters and setters (#4)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -2827,7 +2817,7 @@ What’s the main problem if we overuse interfaces? The answer is that they make
<p>Don’t design with interfaces, discover them.</p>
</div>
<p>Let’s not try to solve a problem abstractly but solve what has to be solved now. Last, but not least, if it’s unclear how an interface makes the code better, we should probably consider removing it to make our code simpler.</p>
<h3id="interface-on-the-producer-side-6">Interface on the producer side (#6)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -2835,7 +2825,7 @@ What’s the main problem if we overuse interfaces? The answer is that they make
</details>
<p>Interfaces are satisfied implicitly in Go, which tends to be a gamechanger compared to languages with an explicit implementation. In most cases, the approach to follow is similar to what we described in the previous section: <em>abstractions should be discovered, not created</em>. This means that it’s not up to the producer to force a given abstraction for all the clients. Instead, it’s up to the client to decide whether it needs some form of abstraction and then determine the best abstraction level for its needs.</p>
<p>An interface should live on the consumer side in most cases. However, in particular contexts (for example, when we know—not foresee—that an abstraction will be helpful for consumers), we may want to have it on the producer side. If we do, we should strive to keep it as minimal as possible, increasing its reusability potential and making it more easily composable.</p>
@ -2848,14 +2838,14 @@ What’s the main problem if we overuse interfaces? The answer is that they make
<p>Only use <code>any</code> if you need to accept or return any possible type, such as <code>json.Marshal</code>. Otherwise, <code>any</code> doesn’t provide meaningful information and can lead to compile-time issues by allowing a caller to call methods with any data type.</p>
</details>
<p>The <code>any</code> type can be helpful if there is a genuine need for accepting or returning any possible type (for instance, when it comes to marshaling or formatting). In general, we should avoid overgeneralizing the code we write at all costs. Perhaps a little bit of duplicated code might occasionally be better if it improves other aspects such as code expressiveness.</p>
<h3id="being-confused-about-when-to-use-generics-9">Being confused about when to use generics (#9)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Relying on generics and type parameters can prevent writing boilerplate code to factor out elements or behaviors. However, do not use type parameters prematurely, but only when you see a concrete need for them. Otherwise, they introduce unnecessary abstractions and complexity.</p>
</details>
<p>Read the full section <ahref="9-generics/">here</a>.</p>
<h3id="not-being-aware-of-the-possible-problems-with-type-embedding-10">Not being aware of the possible problems with type embedding (#10)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -2881,7 +2871,7 @@ promoted to <code>Foo</code>. Therefore, Baz becomes available from Foo.</p>
<li>It shouldn’t promote data (fields) or a behavior (methods) we want to hide from the outside: for example, if it allows clients to access a locking behavior that should remain private to the struct.</li>
</ul>
<p>Using type embedding consciously by keeping these constraints in mind can help avoid boilerplate code with additional forwarding methods. However, let’s make sure we don’t do it solely for cosmetics and not promote elements that should remain hidden.</p>
<h3id="not-using-the-functional-options-pattern-11">Not using the functional options pattern (#11)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -2890,7 +2880,7 @@ promoted to <code>Foo</code>. Therefore, Baz becomes available from Foo.</p>
<p>Although there are different implementations with minor variations, the main idea is as follows:</p>
<ul>
<li>An unexported struct holds the configuration: options.</li>
<li>Each option is a function that returns the same type: <code>type Option func(options *options)</code> error. For example, <code>WithPort</code> accepts an <code>int</code> argument that represents the port and returns an <code>Option</code> type that represents how to update the <code>options</code> struct.</li>
<li>Each option is a function that returns the same type: <code>type Option func(options *options) error</code>. For example, <code>WithPort</code> accepts an <code>int</code> argument that represents the port and returns an <code>Option</code> type that represents how to update the <code>options</code> struct.</li>
<p>The functional options pattern provides a handy and API-friendly way to handle options. Although the builder pattern can be a valid option, it has some minor downsides (having to pass a config struct that can be empty or a less handy way to handle error management) that tend to make the functional options pattern the idiomatic way to deal with these kind of problems in Go.</p>
<h3id="project-misorganization-project-structure-and-package-organization-12">Project misorganization (project structure and package organization) (#12)</h3>
<p>Regarding the overall organization, there are different schools of thought. For example, should we organize our application by context or by layer? It depends on our preferences. We may favor grouping code per context (such as the customer context, the contract context, etc.), or we may favor following hexagonal architecture principles and group per technical layer. If the decision we make fits our use case, it cannot be a wrong decision, as long as we remain consistent with it.</p>
<p>Regarding packages, there are multiple best practices that we should follow. First, we should avoid premature packaging because it might cause us to overcomplicate a project. Sometimes, it’s better to use a simple organization and have our project evolve when we understand what it contains rather than forcing ourselves to make the perfect structure up front.
@ -2953,7 +2943,7 @@ Granularity is another essential thing to consider. We should avoid having dozen
<p>Naming is a critical piece of application design. Creating packages such as <code>common</code>, <code>util</code>, and <code>shared</code> doesn’t bring much value for the reader. Refactor such packages into meaningful and specific package names.</p>
</details>
<p>Also, bear in mind that naming a package after what it provides and not what it contains can be an efficient way to increase its expressiveness.</p>
<h3id="ignoring-package-name-collisions-14">Ignoring package name collisions (#14)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3005,7 +2995,7 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
<li><em>Imaginary</em>—Uses an <code>i</code> suffix (for example, <code>3i</code>)</li>
</ul>
<p>We can also use an underscore character (_) as a separator for readability. For example, we can write 1 billion this way: <code>1_000_000_000</code>. We can also use the underscore character with other representations (for example, <code>0b00_00_01</code>).</p>
<p>However, at run time, an integer overflow or underflow is silent; this does not lead to an application panic. It is essential to keep this behavior in mind, because it can lead to sneaky bugs (for example, an integer increment or addition of positive integers that leads to a negative result).</p>
@ -3035,14 +3025,14 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
<li>When performing additions or subtractions, group operations with a similar order of magnitude for better accuracy.</li>
<li>To favor accuracy, if a sequence of operations requires addition, subtraction, multiplication, or division, perform the multiplication and division operations first.</li>
<h3id="not-understanding-slice-length-and-capacity-20">Not understanding slice length and capacity (#20)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Understanding the difference between slice length and capacity should be part of a Go developer’s core knowledge. The slice length is the number of available elements in the slice, whereas the slice capacity is the number of elements in the backing array.</p>
</details>
<p>Read the full section <ahref="20-slice/">here</a>.</p>
@ -3050,7 +3040,7 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
</details>
<p>While initializing a slice using <code>make</code>, we can provide a length and an optional capacity. Forgetting to pass an appropriate value for both of these parameters when it makes sense is a widespread mistake. Indeed, it can lead to multiple copies and additional effort for the GC to clean the temporary backing arrays. Performance-wise, there’s no good reason not to give the Go runtime a helping hand.</p>
<p>Our options are to allocate a slice with either a given capacity or a given length. Of these two solutions, we have seen that the second tends to be slightly faster. But using a given capacity and append can be easier to implement and read in some contexts.</p>
<h3id="being-confused-about-nil-vs-empty-slice-22">Being confused about nil vs. empty slice (#22)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3063,7 +3053,7 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
<li><code>make([]string, length)</code> if the future length is known</li>
</ul>
<p>The last option, <code>[]string{}</code>, should be avoided if we initialize the slice without elements. Finally, let’s check whether the libraries we use make the distinctions between nil and empty slices to prevent unexpected behaviors.</p>
<h3id="not-properly-checking-if-a-slice-is-empty-23">Not properly checking if a slice is empty (#23)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3071,14 +3061,14 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
</details>
<p>To determine whether a slice has elements, we can either do it by checking if the slice is nil or if its length is equal to 0. Checking the length is the best option to follow as it will cover both if the slice is empty or if the slice is nil.</p>
<p>Meanwhile, when designing interfaces, we should avoid distinguishing nil and empty slices, which leads to subtle programming errors. When returning slices, it should make neither a semantic nor a technical difference if we return a nil or empty slice. Both should mean the same thing for the callers. This principle is the same with maps. To check if a map is empty, check its length, not whether it’s nil.</p>
<h3id="not-making-slice-copies-correctly-24">Not making slice copies correctly (#24)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>To copy one slice to another using the <code>copy</code> built-in function, remember that the number of copied elements corresponds to the minimum between the two slice’s lengths.</p>
</details>
<p>Copying elements from one slice to another is a reasonably frequent operation. When using copy, we must recall that the number of elements copied to the destination corresponds to the minimum between the two slices’ lengths. Also bear in mind that other alternatives exist to copy a slice, so we shouldn’t be surprised if we find them in a codebase.</p>
<h3id="unexpected-side-effects-using-slice-append-25">Unexpected side effects using slice append (#25)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3089,7 +3079,7 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
<summary>Note</summary>
<p><code>s[low:high:max]</code> (full slice expression): This statement creates a slice similar to the one created with <code>s[low:high]</code>, except that the resulting slice’s capacity is equal to <code>max - low</code>.</p>
<h3id="slices-and-memory-leaks-26">Slices and memory leaks (#26)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3097,10 +3087,10 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
</details>
<h4id="leaking-capacity">Leaking capacity</h4>
<p>Remember that slicing a large slice or array can lead to potential high memory consumption. The remaining space won’t be reclaimed by the GC, and we can keep a large backing array despite using only a few elements. Using a slice copy is the solution to prevent such a case.</p>
<h4id="slice-and-pointers">Slice and pointers</h4>
<p>When we use the slicing operation with pointers or structs with pointer fields, we need to know that the GC won’t reclaim these elements. In that case, the two options are to either perform a copy or explicitly mark the remaining elements or their fields to <code>nil</code>.</p>
@ -3108,14 +3098,14 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
</details>
<p>A map provides an unordered collection of key-value pairs in which all the keys are distinct. In Go, a map is based on the hash table data structure. Internally, a hash table is an array of buckets, and each bucket is a pointer to an array of key-value pairs.</p>
<p>If we know up front the number of elements a map will contain, we should create it by providing an initial size. Doing this avoids potential map growth, which is quite heavy computation-wise because it requires reallocating enough space and rebalancing all the elements.</p>
<h3id="maps-and-memory-leaks-28">Maps and memory leaks (#28)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>A map can always grow in memory, but it never shrinks. Hence, if it leads to some memory issues, you can try different options, such as forcing Go to recreate the map or using pointers.</p>
</details>
<p>Read the full section <ahref="28-maps-memory-leaks/">here</a>.</p>
@ -3138,7 +3128,7 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
<p>If operands are not comparable (e.g., slices and maps), we have to use other options such as reflection. Reflection is a form of metaprogramming, and it refers to the ability of an application to introspect and modify its structure and behavior. For example, in Go, we can use <code>reflect.DeepEqual</code>. This function reports whether two elements are deeply equal by recursively traversing two values. The elements it accepts are basic types plus arrays, structs, slices, maps, pointers, interfaces, and functions. Yet, the main catch is the performance penalty.</p>
<p>If performance is crucial at run time, implementing our custom method might be the best solution.
One additional note: we must remember that the standard library has some existing comparison methods. For example, we can use the optimized <code>bytes.Compare</code> function to compare two slices of bytes. Before implementing a custom method, we need to make sure we don’t reinvent the wheel.</p>
<h3id="ignoring-that-elements-are-copied-in-range-loops-30">Ignoring that elements are copied in <code>range</code> loops (#30)</h3>
<detailsclass="info"open="open">
@ -3156,7 +3146,7 @@ One additional note: we must remember that the standard library has some existin
</ul>
<p>Compared to a classic for <code>loop</code>, a <code>range</code> loop is a convenient way to iterate over all the elements of one of these data structures, thanks to its concise syntax.</p>
<p>Yet, we should remember that the value element in a range loop is a copy. Therefore, if the value is a struct we need to mutate, we will only update the copy, not the element itself, unless the value or field we modify is a pointer. The favored options are to access the element via the index using a range loop or a classic for loop.</p>
<h3id="ignoring-how-arguments-are-evaluated-in-range-loops-channels-and-arrays-31">Ignoring how arguments are evaluated in <code>range</code> loops (channels and arrays) (#31)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3172,14 +3162,14 @@ One additional note: we must remember that the standard library has some existin
<h3id="ignoring-the-impacts-of-using-pointer-elements-in-range-loops-32">Ignoring the impacts of using pointer elements in <code>range</code> loops (#32)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Using a local variable or accessing an element using an index, you can prevent mistakes while copying pointers inside a loop.</p>
</details>
<p>When iterating over a data structure using a <code>range</code> loop, we must recall that all the values are assigned to a unique variable with a single unique address. Therefore, if we store a pointer referencing this variable during each iteration, we will end up in a situation where we store the same pointer referencing the same element: the latest one. We can overcome this issue by forcing the creation of a local variable in the loop’s scope or creating a pointer referencing a slice element via its index. Both solutions are fine.</p>
<h3id="making-wrong-assumptions-during-map-iterations-ordering-and-map-insert-during-iteration-33">Making wrong assumptions during map iterations (ordering and map insert during iteration) (#33)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3193,7 +3183,7 @@ One additional note: we must remember that the standard library has some existin
<p>Here, we associate the <code>loop</code> label with the <code>for</code> loop. Then, because we provide the <code>loop</code> label to the <code>break</code> statement, it breaks the loop, not the switch. Therefore, this new version will print <code>0 1 2</code>, as we expected.</p>
<p>Another solution is to make the <code>readFile</code> function a closure but intrinsically, this remains the same solution: adding another surrounding function to execute the <code>defer</code> calls during each iteration.</p>
<p>Conversely, <code>strings.TrimLeft</code> removes all the leading runes contained in a set.</p>
<p>On the other side, <code>strings.TrimSuffix</code> / <code>strings.TrimPrefix</code> returns a string without the provided trailing suffix / prefix.</p>
@ -3402,7 +3392,7 @@ One additional note: we must remember that the standard library has some existin
</span></code></pre></div>
<p>As we can see, the latest version is by far the most efficient: 99% faster than v1 and 78% faster than v2.</p>
<p><code>strings.Builder</code> is the recommended solution to concatenate a list of strings. Usually, this solution should be used within a loop. Indeed, if we just have to concatenate a few strings (such as a name and a surname), using <code>strings.Builder</code> is not recommended as doing so will make the code a bit less readable than using the <code>+=</code> operator or <code>fmt.Sprintf</code>.</p>
@ -3410,7 +3400,7 @@ One additional note: we must remember that the standard library has some existin
</details>
<p>When choosing to work with a string or a <code>[]byte</code>, most programmers tend to favor strings for convenience. But most I/O is actually done with <code>[]byte</code>. For example, <code>io.Reader</code>, <code>io.Writer</code>, and <code>io.ReadAll</code> work with <code>[]byte</code>, not strings.</p>
<p>When we’re wondering whether we should work with strings or <code>[]byte</code>, let’s recall that working with <code>[]byte</code> isn’t necessarily less convenient. Indeed, all the exported functions of the strings package also have alternatives in the <code>bytes</code> package: <code>Split</code>, <code>Count</code>, <code>Contains</code>, <code>Index</code>, and so on. Hence, whether we’re doing I/O or not, we should first check whether we could implement a whole workflow using bytes instead of strings and avoid the price of additional conversions.</p>
<h3id="substring-and-memory-leaks-41">Substring and memory leaks (#41)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3418,7 +3408,7 @@ One additional note: we must remember that the standard library has some existin
</details>
<p>In mistake <ahref="#slice-and-memory-leaks--26-">#26, “Slices and memory leaks,”</a> we saw how slicing a slice or array may lead to memory leak situations. This principle also applies to string and substring operations.</p>
<p>We need to keep two things in mind while using the substring operation in Go. First, the interval provided is based on the number of bytes, not the number of runes. Second, a substring operation may lead to a memory leak as the resulting substring will share the same backing array as the initial string. The solutions to prevent this case from happening are to perform a string copy manually or to use <code>strings.Clone</code> from Go 1.18.</p>
<h2id="functions-and-methods">Functions and Methods</h2>
<h3id="not-knowing-which-type-of-receiver-to-use-42">Not knowing which type of receiver to use (#42)</h3>
<detailsclass="info"open="open">
@ -3456,7 +3446,7 @@ One additional note: we must remember that the standard library has some existin
<li>If the receiver is a basic type such as <code>int</code>, <code>float64</code>, or <code>string</code>.</li>
</ul>
<p>Of course, it’s impossible to be exhaustive, as there will always be edge cases, but this section’s goal was to provide guidance to cover most cases. By default, we can choose to go with a value receiver unless there’s a good reason not to do so. In doubt, we should use a pointer receiver.</p>
<h3id="never-using-named-result-parameters-43">Never using named result parameters (#43)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3471,7 +3461,7 @@ One additional note: we must remember that the standard library has some existin
</span></code></pre></div>
<p>In this example, we attach a name to the result parameter: <code>b</code>. When we call return without arguments, it returns the current value of <code>b</code>.</p>
<p>In some cases, named result parameters can also increase readability: for example, if two parameters have the same type. In other cases, they can also be used for convenience. Therefore, we should use named result parameters sparingly when there’s a clear benefit.</p>
<h3id="unintended-side-effects-with-named-result-parameters-44">Unintended side effects with named result parameters (#44)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3494,7 +3484,7 @@ One additional note: we must remember that the standard library has some existin
</span></code></pre></div>
<p>The error might not be obvious at first glance. Here, the error returned in the <code>if ctx.Err() != nil</code> scope is <code>err</code>. But we haven’t assigned any value to the <code>err</code> variable. It’s still assigned to the zero value of an <code>error</code> type: <code>nil</code>. Hence, this code will always return a nil error.</p>
<p>When using named result parameters, we must recall that each parameter is initialized to its zero value. As we have seen in this section, this can lead to subtle bugs that aren’t always straightforward to spot while reading code. Therefore, let’s remain cautious when using named result parameters, to avoid potential side effects.</p>
<h3id="using-a-filename-as-a-function-input-46">Using a filename as a function input (#46)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Designing functions to receive <code>io.Reader</code> types instead of filenames improves the reusability of a function and makes testing easier.</p>
</details>
<p>Accepting a filename as a function input to read from a file should, in most cases, be considered a code smell (except in specific functions such as <code>os.Open</code>). Indeed, it makes unit tests more complex because we may have to create multiple files. It also reduces the reusability of a function (although not all functions are meant to be reused). Using the <code>io.Reader</code> interface abstracts the data source. Regardless of whether the input is a file, a string, an HTTP request, or a gRPC request, the implementation can be reused and easily tested.</p>
<h3id="ignoring-how-defer-arguments-and-receivers-are-evaluated-argument-evaluation-pointer-and-value-receivers-47">Ignoring how <code>defer</code> arguments and receivers are evaluated (argument evaluation, pointer, and value receivers) (#47)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3566,7 +3556,7 @@ One additional note: we must remember that the standard library has some existin
</span></code></pre></div>
<p>Here, we wrap the calls to both <code>notify</code> and <code>incrementCounter</code> within a closure. This closure references the status variable from outside its body. Therefore, <code>status</code> is evaluated once the closure is executed, not when we call <code>defer</code>. This solution also works and doesn’t require <code>notify</code> and <code>incrementCounter</code> to change their signature.</p>
<p>Let's also note this behavior applies with method receiver: the receiver is evaluated immediately.</p>
<p>Panicking in Go should be used sparingly. There are two prominent cases, one to signal a programmer error (e.g., <ahref="https://cs.opensource.google/go/go/+/refs/tags/go1.20.7:src/database/sql/sql.go;l=44"><code>sql.Register</code></a> that panics if the driver is <code>nil</code> or has already been register) and another where our application fails to create a mandatory dependency. Hence, exceptional conditions that lead us to stop the application. In most other cases, error management should be done with a function that returns a proper error type as the last return argument.</p>
<h3id="ignoring-when-to-wrap-an-error-49">Ignoring when to wrap an error (#49)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3601,7 +3591,7 @@ One additional note: we must remember that the standard library has some existin
<li>Marking an error as a specific error</li>
</ul>
<p>When handling an error, we can decide to wrap it. Wrapping is about adding additional context to an error and/or marking an error as a specific type. If we need to mark an error, we should create a custom error type. However, if we just want to add extra context, we should use fmt.Errorf with the %w directive as it doesn’t require creating a new error type. Yet, error wrapping creates potential coupling as it makes the source error available for the caller. If we want to prevent it, we shouldn’t use error wrapping but error transformation, for example, using fmt.Errorf with the %v directive.</p>
<h3id="comparing-an-error-value-inaccurately-51">Comparing an error value inaccurately (#51)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3626,7 +3616,7 @@ One additional note: we must remember that the standard library has some existin
<li>Unexpected errors should be designed as error types: <code>type BarError struct { ... }</code>, with <code>BarError</code> implementing the <code>error</code> interface.</li>
</ul>
<p>If we use error wrapping in our application with the <code>%w</code> directive and <code>fmt.Errorf</code>, checking an error against a specific value should be done using <code>errors.Is</code> instead of <code>==</code>. Thus, even if the sentinel error is wrapped, <code>errors.Is</code> can recursively unwrap it and compare each error in the chain against the provided value.</p>
<h3id="handling-an-error-twice-52">Handling an error twice (#52)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3634,13 +3624,13 @@ One additional note: we must remember that the standard library has some existin
</details>
<p>Handling an error multiple times is a mistake made frequently by developers, not specifically in Go. This can cause situations where the same error is logged multiple times make debugging harder.</p>
<p>Let's remind us that handling an error should be done only once. Logging an error is handling an error. Hence, we should either log or return an error. By doing this, we simplify our code and gain better insights into the error situation. Using error wrapping is the most convenient approach as it allows us to propagate the source error and add context to an error.</p>
<h3id="not-handling-an-error-53">Not handling an error (#53)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Ignoring an error, whether during a function call or in a <code>defer</code> function, should be done explicitly using the blank identifier. Otherwise, future readers may be confused about whether it was intentional or a miss.</p>
@ -3665,7 +3655,7 @@ One additional note: we must remember that the standard library has some existin
</span><spanid="__span-35-2"><aid="__codelineno-35-2"name="__codelineno-35-2"href="#__codelineno-35-2"></a><spanclass="c1">// Hence, it's accepted to miss some of them in case of errors.</span>
<h3id="mixing-up-concurrency-and-parallelism-55">Mixing up concurrency and parallelism (#55)</h3>
<detailsclass="info"open="open">
@ -3686,7 +3676,7 @@ One additional note: we must remember that the standard library has some existin
<p>To be a proficient developer, you must acknowledge that concurrency isn’t always faster. Solutions involving parallelization of minimal workloads may not necessarily be faster than a sequential implementation. Benchmarking sequential versus concurrent solutions should be the way to validate assumptions.</p>
</details>
<p>Read the full section <ahref="56-concurrency-faster/">here</a>.</p>
<h3id="being-puzzled-about-when-to-use-channels-or-mutexes-57">Being puzzled about when to use channels or mutexes (#57)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3724,7 +3714,7 @@ the use case. However, we should see the two options as complementary. </p>
<p>Depending on the operation we want to perform, does a data-race-free application necessarily mean a deterministic result? Not necessarily.</p>
<p>A race condition occurs when the behavior depends on the sequence or the timing of events that can’t be controlled. Here, the timing of events is the goroutines’ execution order.</p>
<p>In summary, when we work in concurrent applications, it’s essential to understand that a data race is different from a race condition. A data race occurs when multiple goroutines simultaneously access the same memory location and at least one of them is writing. A data race means unexpected behavior. However, a data-race-free application doesn’t necessarily mean deterministic results. An application can be free of data races but still have behavior that depends on uncontrolled events (such as goroutine execution, how fast a message is published to a channel, or how long a call to a database lasts); this is a race condition. Understanding both concepts is crucial to becoming proficient in designing concurrent applications.</p>
<h3id="not-understanding-the-concurrency-impacts-of-a-workload-type-59">Not understanding the concurrency impacts of a workload type (#59)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3741,7 +3731,7 @@ the use case. However, we should see the two options as complementary. </p>
<p>The last is the rarest nowadays, given that memory has become very cheap in recent decades. Hence, this section focuses on the two first workload types: CPU- and I/O-bound.</p>
</details>
<p>If the workload executed by the workers is I/O-bound, the value mainly depends on the external system. Conversely, if the workload is CPU-bound, the optimal number of goroutines is close to the number of available CPU cores (a best practice can be to use <code>runtime.GOMAXPROCS</code>). Knowing the workload type (I/O or CPU) is crucial when designing concurrent applications.</p>
<h3id="misunderstanding-go-contexts-60">Misunderstanding Go contexts (#60)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3771,7 +3761,7 @@ the use case. However, we should see the two options as complementary. </p>
</ul>
<p>One thing to note is that the internal channel should be closed when a context is canceled or has met a deadline, instead of when it receives a specific value, because the closure of a channel is the only channel action that all the consumer goroutines will receive. This way, all the consumers will be notified once a context is canceled or a deadline is reached.</p>
<p>In summary, to be a proficient Go developer, we have to understand what a context is and how to use it. In general, a function that users wait for should take a context, as doing so allows upstream callers to decide when calling this function should be aborted. </p>
<h3id="propagating-an-inappropriate-context-61">Propagating an inappropriate context (#61)</h3>
<detailsclass="info"open="open">
@ -3811,7 +3801,7 @@ the use case. However, we should see the two options as complementary. </p>
<p>From Go 1.21, there is a way to create a new context without cancel. <ahref="https://pkg.go.dev/context#WithoutCancel"><code>context.WithoutCancel</code></a> returns a copy of parent that is not canceled when parent is canceled.</p>
</details>
<p>In summary, propagating a context should be done cautiously.</p>
<h3id="starting-a-goroutine-without-knowing-when-to-stop-it-62">Starting a goroutine without knowing when to stop it (#62)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3865,7 +3855,7 @@ the use case. However, we should see the two options as complementary. </p>
</span></code></pre></div>
<p>Instead of signaling <code>watcher</code> that it’s time to close its resources, we now call this <code>close</code> method, using <code>defer</code> to guarantee that the resources are closed before the application exits.</p>
<p>In summary, let’s be mindful that a goroutine is a resource like any other that must eventually be closed to free memory or other resources. Starting a goroutine without knowing when to stop it is a design issue. Whenever a goroutine is started, we should have a clear plan about when it will stop. Last but not least, if a goroutine creates resources and its lifetime is bound to the lifetime of the application, it’s probably safer to wait for this goroutine to complete before exiting the application. This way, we can ensure that the resources can be freed.</p>
<h3id="not-being-careful-with-goroutines-and-loop-variables-63">Not being careful with goroutines and loop variables (#63)</h3>
<detailsclass="warning"open="open">
<summary>Warning</summary>
@ -3912,7 +3902,7 @@ the use case. However, we should see the two options as complementary. </p>
<p>Unlike a switch statement, where the first case with a match wins, the select statement selects randomly if multiple options are possible.</p>
<p>This behavior might look odd at first, but there’s a good reason for it: to prevent possible starvation. Suppose the first possible communication chosen is based on the source order. In that case, we may fall into a situation where, for example, we only receive from one channel because of a fast sender. To prevent this, the language designers decided to use a random selection.</p>
<p>When using <code>select</code> with multiple channels, we must remember that if multiple options are possible, the first case in the source order does not automatically win. Instead, Go selects randomly, so there’s no guarantee about which option will be chosen. To overcome this behavior, in the case of a single producer goroutine, we can use either unbuffered channels or a single channel.</p>
<h3id="not-using-notification-channels-65">Not using notification channels (#65)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -3967,7 +3957,7 @@ the use case. However, we should see the two options as complementary. </p>
</span></code></pre></div>
<p>This elegant solution relies on nil channels to somehow <em>remove</em> one case from the <code>select</code> statement.</p>
<p>Let’s keep this idea in mind: nil channels are useful in some conditions and should be part of the Go developer’s toolset when dealing with concurrent code.</p>
<h3id="timeafter-and-memory-leaks-76"><code>time.After</code> and memory leaks (#76)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Avoiding calls to <code>time.After</code> in repeated functions (such as loops or HTTP handlers) can avoid peak memory consumption. The resources created by <code>time.After</code> are released only when the timer expires.</p>
<p>Developers often use <code>time.After</code> in loops or HTTP handlers repeatedly to implement the timing function. But it can lead to unintended peak memory consumption due to the delayed release of resources, just like the following code:</p>
</span><spanid="__span-48-7"><aid="__codelineno-48-7"name="__codelineno-48-7"href="#__codelineno-48-7"></a><spanclass="w"></span><spanclass="nx">log</span><spanclass="p">.</span><spanclass="nx">Println</span><spanclass="p">(</span><spanclass="s">"warning: no messages received"</span><spanclass="p">)</span>
<p>As we see, it returns receive-only channel.</p>
<p>When <code>time.After</code> is used in a loop or repeated context, a new channel is created in each iteration. If these channels are not properly closed or if their associated timers are not stopped, they can accumulate and consume memory. The resources associated with each timer and channel are only released when the timer expires or the channel is closed.</p>
<p>To avoid this happening, We can use context's timeout setting instead of <code>time.After</code>, like below:</p>
</span><spanid="__span-50-9"><aid="__codelineno-50-9"name="__codelineno-50-9"href="#__codelineno-50-9"></a><spanclass="w"></span><spanclass="nx">log</span><spanclass="p">.</span><spanclass="nx">Println</span><spanclass="p">(</span><spanclass="s">"warning: no messages received"</span><spanclass="p">)</span>
</span><spanid="__span-51-11"><aid="__codelineno-51-11"name="__codelineno-51-11"href="#__codelineno-51-11"></a><spanclass="w"></span><spanclass="nx">log</span><spanclass="p">.</span><spanclass="nx">Println</span><spanclass="p">(</span><spanclass="s">"warning: no messages received"</span><spanclass="p">)</span>
<h3id="json-handling-common-mistakes-77">JSON handling common mistakes (#77)</h3>
<ul>
<li>Unexpected behavior because of type embedding</li>
</ul>
<p>Be careful about using embedded fields in Go structs. Doing so may lead to sneaky bugs like an embedded time.Time field implementing the <code>json.Marshaler</code> interface, hence overriding the default marshaling behavior.</p>
<p>When comparing two <code>time.Time</code> structs, recall that <code>time.Time</code> contains both a wall clock and a monotonic clock, and the comparison using the == operator is done on both clocks.</p>
<p>To avoid wrong assumptions when you provide a map while unmarshaling JSON data, remember that numerics are converted to <code>float64</code> by default.</p>
<p>Call the <code>Err</code> method of <code>sql.Rows</code> after row iterations to ensure that you haven’t missed an error while preparing the next row.</p>
<h3id="forgetting-the-return-statement-after-replying-to-an-http-request-80">Forgetting the return statement after replying to an HTTP request (#80)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>To avoid unexpected behaviors in HTTP handler implementations, make sure you don’t miss the <code>return</code> statement if you want a handler to stop after <code>http.Error</code>.</p>
</details>
<p>Consider the following HTTP handler that handles an error from <code>foo</code> using <code>http.Error</code>:</p>
</span><spanid="__span-53-2"><aid="__codelineno-53-2"name="__codelineno-53-2"href="#__codelineno-53-2"></a>all good
</span></code></pre></div>
<p>The response contains both the error and success messages, and also the first HTTP status code, 500. There would also be a warning log indicating that we attempted to write the status code multiple times:</p>
<divclass="language-text highlight"><pre><span></span><code><spanid="__span-50-1"><aid="__codelineno-50-1"name="__codelineno-50-1"href="#__codelineno-50-1"></a>2023/10/10 16:45:33 http: superfluous response.WriteHeader call from main.handler (main.go:20)
<divclass="language-text highlight"><pre><span></span><code><spanid="__span-54-1"><aid="__codelineno-54-1"name="__codelineno-54-1"href="#__codelineno-54-1"></a>2023/10/10 16:45:33 http: superfluous response.WriteHeader call from main.handler (main.go:20)
</span></code></pre></div>
<p>The mistake in this code is that <code>http.Error</code> does not stop the handler's execution, which means the success message and status code get written in addition to the error. Beyond an incorrect response, failing to return after writing an error can lead to the unwanted execution of code and unexpected side-effects. The following code adds the <code>return</code> statement following the <code>http.Error</code> and exhibits the desired behavior when ran:</p>
</span><spanid="__span-51-5"><aid="__codelineno-51-5"name="__codelineno-51-5"href="#__codelineno-51-5"></a><spanclass="w"></span><spanclass="k">return</span><spanclass="w"></span><spanclass="c1">// Adds the return statement</span>
</span><spanid="__span-55-5"><aid="__codelineno-55-5"name="__codelineno-55-5"href="#__codelineno-55-5"></a><spanclass="w"></span><spanclass="k">return</span><spanclass="w"></span><spanclass="c1">// Adds the return statement</span>
<h3id="using-the-default-http-client-and-server-81">Using the default HTTP client and server (#81)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>For production-grade applications, don’t use the default HTTP client and server implementations. These implementations are missing timeouts and behaviors that should be mandatory in production.</p>
<h3id="not-categorizing-tests-build-tags-environment-variables-and-short-mode-82">Not categorizing tests (build tags, environment variables, and short mode) (#82)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Categorizing tests using build flags, environment variables, or short mode makes the testing process more efficient. You can create test categories using build flags or environment variables (for example, unit versus integration tests) and differentiate short from long-running tests to decide which kinds of tests to execute.</p>
<h3id="not-dealing-with-the-time-api-efficiently-87">Not dealing with the time API efficiently (#87)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Understanding how to deal with functions using the time API is another way to make a test less flaky. You can use standard techniques such as handling the time as part of a hidden dependency or asking clients to provide it.</p>
<h3id="not-using-testing-utility-packages-httptest-and-iotest-88">Not using testing utility packages (<code>httptest</code> and <code>iotest</code>) (#88)</h3>
<ul>
<li>The <code>httptest</code> package is helpful for dealing with HTTP applications. It provides a set of utilities to test both clients and servers.</li>
<p>Handling errors using the <code>*testing.T</code> variable instead of the classic <code>if err != nil</code> makes code shorter and easier to read.</p>
<h3id="not-using-fuzzing-community-mistake">Not using fuzzing (community mistake)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
@ -4228,18 +4267,18 @@ the use case. However, we should see the two options as complementary. </p>
<li>Cache line</li>
</ul>
<p>Being conscious of the cache line concept is critical to understanding how to organize data in data-intensive applications. A CPU doesn’t fetch memory word by word; instead, it usually copies a memory block to a 64-byte cache line. To get the most out of each individual cache line, enforce spatial locality.</p>
<p>Making code predictable for the CPU can also be an efficient way to optimize certain functions. For example, a unit or constant stride is predictable for the CPU, but a non-unit stride (for example, a linked list) isn’t predictable.</p>
@ -4249,31 +4288,31 @@ the use case. However, we should see the two options as complementary. </p>
<summary>TL;DR</summary>
<p>Knowing that lower levels of CPU caches aren’t shared across all the cores helps avoid performance-degrading patterns such as false sharing while writing concurrency code. Sharing memory is an illusion.</p>
<h3id="not-taking-into-account-instruction-level-parallelism-93">Not taking into account instruction-level parallelism (#93)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Use instruction-level parallelism (ILP) to optimize specific parts of your code to allow a CPU to execute as many parallel instructions as possible. Identifying data hazards is one of the main steps.</p>
<h3id="not-being-aware-of-data-alignment-94">Not being aware of data alignment (#94)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>You can avoid common mistakes by remembering that in Go, basic types are aligned with their own size. For example, keep in mind that reorganizing the fields of a struct by size in descending order can lead to more compact structs (less memory allocation and potentially a better spatial locality).</p>
<h3id="not-understanding-stack-vs-heap-95">Not understanding stack vs. heap (#95)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Understanding the fundamental differences between heap and stack should also be part of your core knowledge when optimizing a Go application. Stack allocations are almost free, whereas heap allocations are slower and rely on the GC to clean the memory.</p>
<h3id="not-knowing-how-to-reduce-allocations-api-change-compiler-optimizations-and-syncpool-96">Not knowing how to reduce allocations (API change, compiler optimizations, and <code>sync.Pool</code>) (#96)</h3>
<detailsclass="info"open="open">
<summary>TL;DR</summary>
<p>Reducing allocations is also an essential aspect of optimizing a Go application. This can be done in different ways, such as designing the API carefully to prevent sharing up, understanding the common Go compiler optimizations, and using <code>sync.Pool</code>.</p>
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
</div>
</aside>
</div>
@ -1778,7 +1768,7 @@ This repo is open to <a href="https://hacktoberfest.com/">Hacktoberfest</a>!
<p>ただし、range ループ内の値要素はコピーであることを覚えておく必要があります。したがって、値を変更する必要がある構造体の場合、変更する値またはフィールドがポインタでない限り、要素自体ではなくコピーのみを更新します。range ループまたは従来の for ループを使用してインデックス経由で要素にアクセスすることが推奨されます。</p>
<p>ワーカーによって実行されるワークロードが I/O バウンドである場合、値は主に外部システムに依存します。逆に、ワークロードが CPU に依存している場合、ゴルーチンの最適な数は利用可能な CPU コアの数に近くなります(ベストプラクティスは <code>runtime.GOMAXPROCS</code> を使用することです)。並行処理のアプリケーションを設計する場合、ワークロードのタイプ( I/O あるいは CPU )を知ることが重要です。</p>
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.
This repo is open to <ahref="https://hacktoberfest.com/">Hacktoberfest</a>! 🎉 If you want to participate, please have a look at the <ahref="https://github.com/teivah/100-go-mistakes/issues">open issues</a>.