Section refinement

This commit is contained in:
Teiva Harsanyi 2024-02-13 23:19:07 +01:00
parent 61aa242a49
commit e7c810a143
6 changed files with 205 additions and 209 deletions

View file

@ -22,7 +22,7 @@ This page is a summary of all the mistakes in the 100 Go Mistakes book. Meanwhil
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 `err` 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/1-variable-shadowing/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/1-variable-shadowing/main.go)
### Unnecessary nested code (#2)
@ -75,7 +75,7 @@ if s == "" {
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 codes readability.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/2-nested-code/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/2-nested-code/main.go)
### Misusing init functions (#3)
@ -93,7 +93,7 @@ Init functions can lead to some issues:
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/3-init-functions/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/3-init-functions/)
### Overusing getters and setters (#4)
@ -128,7 +128,7 @@ We should be cautious when creating abstractions in our code (abstractions shoul
Lets not try to solve a problem abstractly but solve what has to be solved now. Last, but not least, if its unclear how an interface makes the code better, we should probably consider removing it to make our code simpler.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/5-interface-pollution/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/5-interface-pollution/)
### Interface on the producer side (#6)
@ -140,7 +140,7 @@ Interfaces are satisfied implicitly in Go, which tends to be a gamechanger compa
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/6-interface-producer/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/6-interface-producer/)
### Returning interfaces (#7)
@ -158,7 +158,7 @@ In most cases, we shouldnt return interfaces but concrete implementations. Ot
The `any` 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/8-any/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/8-any/main.go)
### Being confused about when to use generics (#9)
@ -168,7 +168,7 @@ The `any` type can be helpful if there is a genuine need for accepting or return
Read the full section [here](9-generics.md).
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/9-generics/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/9-generics/main.go)
### Not being aware of the possible problems with type embedding (#10)
@ -204,7 +204,7 @@ If we decide to use type embedding, we need to keep two main constraints in mind
Using type embedding consciously by keeping these constraints in mind can help avoid boilerplate code with additional forwarding methods. However, lets make sure we dont do it solely for cosmetics and not promote elements that should remain hidden.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/10-type-embedding/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/10-type-embedding/main.go)
### Not using the functional options pattern (#11)
@ -264,7 +264,7 @@ func NewServer(addr string, opts ...Option) ( *http.Server, error) { <1>
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/11-functional-options/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/11-functional-options/)
### Project misorganization (project structure and package organization) (#12)
@ -291,7 +291,7 @@ Organizing a project isnt straightforward, but following these rules should h
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/13-utility-packages/stringset.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/13-utility-packages/stringset.go)
### Ignoring package name collisions (#14)
@ -360,7 +360,7 @@ We should also note the other integer literal representations:
We can also use an underscore character (_) as a separator for readability. For example, we can write 1 billion this way: `1_000_000_000`. We can also use the underscore character with other representations (for example, `0b00_00_01`).
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/17-octal-literals/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/17-octal-literals/main.go)
### Neglecting integer overflows (#18)
@ -380,7 +380,7 @@ constant 2147483648 overflows int32
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).
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/18-integer-overflows)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/18-integer-overflows)
### Not understanding floating-points (#19)
@ -405,7 +405,7 @@ Because Gos `float32` and `float64` types are approximations, we have to bear
* When performing additions or subtractions, group operations with a similar order of magnitude for better accuracy.
* To favor accuracy, if a sequence of operations requires addition, subtraction, multiplication, or division, perform the multiplication and division operations first.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/19-floating-points/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/19-floating-points/main.go)
### Not understanding slice length and capacity (#20)
@ -415,7 +415,7 @@ Because Gos `float32` and `float64` types are approximations, we have to bear
Read the full section [here](20-slice.md).
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/20-slice-length-cap/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/20-slice-length-cap/main.go)
### Inefficient slice initialization (#21)
@ -427,7 +427,7 @@ While initializing a slice using `make`, we can provide a length and an optional
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/21-slice-init/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/21-slice-init/main.go)
### Being confused about nil vs. empty slice (#22)
@ -443,7 +443,7 @@ In Go, there is a distinction between nil and empty slices. A nil slice is equal
The last option, `[]string{}`, should be avoided if we initialize the slice without elements. Finally, lets check whether the libraries we use make the distinctions between nil and empty slices to prevent unexpected behaviors.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/22-nil-empty-slice/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/22-nil-empty-slice/)
### Not properly checking if a slice is empty (#23)
@ -455,7 +455,7 @@ To determine whether a slice has elements, we can either do it by checking if th
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 its nil.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/23-checking-slice-empty/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/23-checking-slice-empty/main.go)
### Not making slice copies correctly (#24)
@ -465,7 +465,7 @@ Meanwhile, when designing interfaces, we should avoid distinguishing nil and emp
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 shouldnt be surprised if we find them in a codebase.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/24-slice-copy/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/24-slice-copy/main.go)
### Unexpected side effects using slice append (#25)
@ -479,7 +479,7 @@ When using slicing, we must remember that we can face a situation leading to uni
`s[low:high:max]` (full slice expression): This statement creates a slice similar to the one created with `s[low:high]`, except that the resulting slices capacity is equal to `max - low`.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/25-slice-append/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/25-slice-append/main.go)
### Slices and memory leaks (#26)
@ -491,13 +491,13 @@ When using slicing, we must remember that we can face a situation leading to uni
Remember that slicing a large slice or array can lead to potential high memory consumption. The remaining space wont 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/capacity-leak)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/capacity-leak)
#### Slice and pointers
When we use the slicing operation with pointers or structs with pointer fields, we need to know that the GC wont 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 `nil`.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/slice-pointers)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/slice-pointers)
### Inefficient map initialization (#27)
@ -509,7 +509,7 @@ A map provides an unordered collection of key-value pairs in which all the keys
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/27-map-init/main_test.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/27-map-init/main_test.go)
### Maps and memory leaks (#28)
@ -519,7 +519,7 @@ If we know up front the number of elements a map will contain, we should create
Read the full section [here](28-maps-memory-leaks.md).
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/28-map-memory-leak/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/28-map-memory-leak/main.go)
### Comparing values incorrectly (#29)
@ -547,7 +547,7 @@ If performance is crucial at run time, implementing our custom method might be t
One additional note: we must remember that the standard library has some existing comparison methods. For example, we can use the optimized `bytes.Compare` function to compare two slices of bytes. Before implementing a custom method, we need to make sure we dont reinvent the wheel.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/29-comparing-values/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/29-comparing-values/main.go)
## Control Structures
@ -570,7 +570,7 @@ Compared to a classic for `loop`, a `range` loop is a convenient way to iterate
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/30-range-loop-element-copied/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/30-range-loop-element-copied/)
### Ignoring how arguments are evaluated in `range` loops (channels and arrays) (#31)
@ -592,7 +592,7 @@ for i, v := range a {
This code updates the last index to 10. However, if we run this code, it does not print 10; it prints 2.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/31-range-loop-arg-evaluation/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/31-range-loop-arg-evaluation/)
### Ignoring the impacts of using pointer elements in `range` loops (#32)
@ -613,7 +613,7 @@ This code updates the last index to 10. However, if we run this code, it does no
<!-- TODO -->
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/33-map-iteration/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/33-map-iteration/main.go)
### Ignoring how the `break` statement works (#34)
@ -656,7 +656,7 @@ loop:
Here, we associate the `loop` label with the `for` loop. Then, because we provide the `loop` label to the `break` statement, it breaks the loop, not the switch. Therefore, this new version will print `0 1 2`, as we expected.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/34-break/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/34-break/main.go)
### Using `defer` inside a loop (#35)
@ -713,7 +713,7 @@ func readFile(path string) error {
Another solution is to make the `readFile` function a closure but intrinsically, this remains the same solution: adding another surrounding function to execute the `defer` calls during each iteration.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/35-defer-loop/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/35-defer-loop/main.go)
## Strings
@ -732,7 +732,7 @@ As runes are everywhere in Go, it's important to understand the following:
* Using UTF-8, a Unicode code point can be encoded into 1 to 4 bytes.
* Using `len()` on a string in Go returns the number of bytes, not the number of runes.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/36-rune/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/36-rune/main.go)
### Inaccurate string iteration (#37)
@ -804,7 +804,7 @@ r := []rune(s)[4]
fmt.Printf("%c\n", r) // o
```
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/37-string-iteration/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/37-string-iteration/main.go)
### Misusing trim functions (#38)
@ -826,7 +826,7 @@ Conversely, `strings.TrimLeft` removes all the leading runes contained in a set.
On the other side, `strings.TrimSuffix` / `strings.TrimPrefix` returns a string without the provided trailing suffix / prefix.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/38-trim/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/38-trim/main.go)
### Under-optimized strings concatenation (#39)
@ -896,7 +896,7 @@ As we can see, the latest version is by far the most efficient: 99% faster than
`strings.Builder` 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 `strings.Builder` is not recommended as doing so will make the code a bit less readable than using the `+=` operator or `fmt.Sprintf`.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/39-string-concat/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/39-string-concat/)
### Useless string conversions (#40)
@ -908,7 +908,7 @@ When choosing to work with a string or a `[]byte`, most programmers tend to favo
When were wondering whether we should work with strings or `[]byte`, lets recall that working with `[]byte` isnt necessarily less convenient. Indeed, all the exported functions of the strings package also have alternatives in the `bytes` package: `Split`, `Count`, `Contains`, `Index`, and so on. Hence, whether were 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/40-string-conversion/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/40-string-conversion/main.go)
### Substring and memory leaks (#41)
@ -920,7 +920,7 @@ In mistake [#26, “Slices and memory leaks,”](#slice-and-memory-leaks--26-) w
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 `strings.Clone` from Go 1.18.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/41-substring-memory-leak/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/41-substring-memory-leak/main.go)
## Functions and Methods
@ -964,7 +964,7 @@ A receiver _should_ be a value
Of course, its impossible to be exhaustive, as there will always be edge cases, but this sections goal was to provide guidance to cover most cases. By default, we can choose to go with a value receiver unless theres a good reason not to do so. In doubt, we should use a pointer receiver.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/42-receiver/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/42-receiver/)
### Never using named result parameters (#43)
@ -987,7 +987,7 @@ In this example, we attach a name to the result parameter: `b`. When we call ret
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 theres a clear benefit.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/43-named-result-parameters/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/43-named-result-parameters/main.go)
### Unintended side effects with named result parameters (#44)
@ -1018,7 +1018,7 @@ The error might not be obvious at first glance. Here, the error returned in the
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 arent always straightforward to spot while reading code. Therefore, lets remain cautious when using named result parameters, to avoid potential side effects.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/44-side-effects-named-result-parameters/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/44-side-effects-named-result-parameters/main.go)
### Returning a nil receiver (#45)
@ -1028,7 +1028,7 @@ When using named result parameters, we must recall that each parameter is initia
<!-- TODO -->
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/45-nil-receiver/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/45-nil-receiver/main.go)
### Using a filename as a function input (#46)
@ -1038,7 +1038,7 @@ When using named result parameters, we must recall that each parameter is initia
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 `os.Open`). 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 `io.Reader` 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/46-function-input/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/46-function-input/)
### Ignoring how `defer` arguments and receivers are evaluated (argument evaluation, pointer, and value receivers) (#47)
@ -1111,7 +1111,7 @@ Here, we wrap the calls to both `notify` and `incrementCounter` within a closure
Let's also note this behavior applies with method receiver: the receiver is evaluated immediately.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/47-defer-evaluation/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/47-defer-evaluation/)
## Error Management
@ -1144,7 +1144,7 @@ main.main()
Panicking in Go should be used sparingly. There are two prominent cases, one to signal a programmer error (e.g., [`sql.Register`](https://cs.opensource.google/go/go/+/refs/tags/go1.20.7:src/database/sql/sql.go;l=44) that panics if the driver is `nil` 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/48-panic/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/48-panic/main.go)
### Ignoring when to wrap an error (#49)
@ -1159,7 +1159,7 @@ Since Go 1.13, the %w directive allows us to wrap errors conveniently. Error wra
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 doesnt 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 shouldnt use error wrapping but error transformation, for example, using fmt.Errorf with the %v directive.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/49-error-wrapping/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/49-error-wrapping/main.go)
### Comparing an error type inaccurately (#50)
@ -1169,7 +1169,7 @@ When handling an error, we can decide to wrap it. Wrapping is about adding addit
<!-- TODO -->
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/50-compare-error-type/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/50-compare-error-type/main.go)
### Comparing an error value inaccurately (#51)
@ -1192,7 +1192,7 @@ In general, the convention is to start with `Err` followed by the error type: he
If we use error wrapping in our application with the `%w` directive and `fmt.Errorf`, checking an error against a specific value should be done using `errors.Is` instead of `==`. Thus, even if the sentinel error is wrapped, `errors.Is` can recursively unwrap it and compare each error in the chain against the provided value.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/51-comparing-error-value/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/51-comparing-error-value/main.go)
### Handling an error twice (#52)
@ -1204,7 +1204,7 @@ Handling an error multiple times is a mistake made frequently by developers, not
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/52-handling-error-twice/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/52-handling-error-twice/main.go)
### Not handling an error (#53)
@ -1212,7 +1212,7 @@ Let's remind us that handling an error should be done only once. Logging an erro
Ignoring an error, whether during a function call or in a `defer` function, should be done explicitly using the blank identifier. Otherwise, future readers may be confused about whether it was intentional or a miss.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/53-not-handling-error/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/53-not-handling-error/main.go)
### Not handling `defer` errors (#54)
@ -1249,7 +1249,7 @@ In terms of compilation and run time, this approach doesnt change anything co
_ = notify()
```
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/54-defer-errors/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/54-defer-errors/main.go)
## Concurrency: Foundations
@ -1276,7 +1276,7 @@ In summary, concurrency provides a structure to solve a problem with parts that
Read the full section [here](56-concurrency-faster.md).
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/56-faster/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/56-faster/)
### Being puzzled about when to use channels or mutexes (#57)
@ -1329,7 +1329,7 @@ A race condition occurs when the behavior depends on the sequence or the timing
In summary, when we work in concurrent applications, its 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 doesnt 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/58-races/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/58-races/)
### Not understanding the concurrency impacts of a workload type (#59)
@ -1349,7 +1349,7 @@ In programming, the execution time of a workload is limited by one of the follow
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 `runtime.GOMAXPROCS`). Knowing the workload type (I/O or CPU) is crucial when designing concurrent applications.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/59-workload-type/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/59-workload-type/main.go)
### Misunderstanding Go contexts (#60)
@ -1391,7 +1391,7 @@ One thing to note is that the internal channel should be closed when a context i
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/60-contexts/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/60-contexts/main.go)
## Concurrency: Practice
@ -1441,7 +1441,7 @@ In the latter case, calling publish will return an error because we returned the
In summary, propagating a context should be done cautiously.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/61-inappropriate-context/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/61-inappropriate-context/main.go)
### Starting a goroutine without knowing when to stop it (#62)
@ -1511,7 +1511,7 @@ Instead of signaling `watcher` that its time to close its resources, we now c
In summary, lets 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, its probably safer to wait for this goroutine to complete before exiting the application. This way, we can ensure that the resources can be freed.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/62-starting-goroutine/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/62-starting-goroutine/)
### Not being careful with goroutines and loop variables (#63)
@ -1572,7 +1572,7 @@ This behavior might look odd at first, but theres a good reason for it: to pr
When using `select` 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 theres 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/64-select-behavior/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/64-select-behavior/main.go)
### Not using notification channels (#65)
@ -1648,7 +1648,7 @@ This elegant solution relies on nil channels to somehow _remove_ one case from t
Lets keep this idea in mind: nil channels are useful in some conditions and should be part of the Go developers toolset when dealing with concurrent code.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/66-nil-channels/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/66-nil-channels/main.go)
### Being puzzled about channel size (#67)
@ -1664,7 +1664,7 @@ You should have a good reason to specify a channel size other than one for buffe
Being aware that string formatting may lead to calling existing functions means watching out for possible deadlocks and other data races.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/68-string-formatting/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/68-string-formatting/main.go)
### Creating data races with append (#69)
@ -1672,7 +1672,7 @@ You should have a good reason to specify a channel size other than one for buffe
Calling `append` isnt always data-race-free; hence, it shouldnt be used concurrently on a shared slice.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/69-data-race-append/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/69-data-race-append/main.go)
### Using mutexes inaccurately with slices and maps (#70)
@ -1680,7 +1680,7 @@ You should have a good reason to specify a channel size other than one for buffe
Remembering that slices and maps are pointers can prevent common data races.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/70-mutex-slices-maps/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/70-mutex-slices-maps/main.go)
### Misusing `sync.WaitGroup` (#71)
@ -1688,7 +1688,7 @@ You should have a good reason to specify a channel size other than one for buffe
To accurately use `sync.WaitGroup`, call the `Add` method before spinning up goroutines.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/71-wait-group/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/71-wait-group/main.go)
### Forgetting about `sync.Cond` (#72)
@ -1696,7 +1696,7 @@ You should have a good reason to specify a channel size other than one for buffe
You can send repeated notifications to multiple goroutines with `sync.Cond`.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/72-cond/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/72-cond/main.go)
### Not using `errgroup` (#73)
@ -1704,7 +1704,7 @@ You should have a good reason to specify a channel size other than one for buffe
You can synchronize a group of goroutines and handle errors and contexts with the `errgroup` package.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/73-errgroup/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/73-errgroup/main.go)
### Copying a `sync` type (#74)
@ -1712,7 +1712,7 @@ You should have a good reason to specify a channel size other than one for buffe
`sync` types shouldnt be copied.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/74-copying-sync/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/74-copying-sync/main.go)
## Standard Library
@ -1745,7 +1745,7 @@ ticker = time.NewTicker(time.Microsecond)
ticker = time.NewTicker(1000 * time.Nanosecond)
```
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/75-wrong-time-duration/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/75-wrong-time-duration/main.go)
### `time.After` and memory leaks (#76)
@ -1816,7 +1816,7 @@ func consumer(ch <-chan Event) {
}
```
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/76-time-after/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/76-time-after/main.go)
### JSON handling common mistakes (#77)
@ -1824,19 +1824,19 @@ func consumer(ch <-chan Event) {
Be careful about using embedded fields in Go structs. Doing so may lead to sneaky bugs like an embedded time.Time field implementing the `json.Marshaler` interface, hence overriding the default marshaling behavior.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/type-embedding/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/type-embedding/main.go)
* JSON and the monotonic clock
When comparing two `time.Time` structs, recall that `time.Time` contains both a wall clock and a monotonic clock, and the comparison using the == operator is done on both clocks.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/monotonic-clock/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/monotonic-clock/main.go)
* Map of `any`
To avoid wrong assumptions when you provide a map while unmarshaling JSON data, remember that numerics are converted to `float64` by default.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/map-any/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/map-any/main.go)
### Common SQL mistakes (#78)
@ -1844,7 +1844,7 @@ func consumer(ch <-chan Event) {
Call the `Ping` or `PingContext` method if you need to test your configuration and make sure a database is reachable.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/sql-open)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/sql-open)
* Forgetting about connections pooling
@ -1854,19 +1854,19 @@ func consumer(ch <-chan Event) {
Using SQL prepared statements makes queries more efficient and more secure.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/prepared-statements)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/prepared-statements)
* Mishandling null values
Deal with nullable columns in tables using pointers or `sql.NullXXX` types.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/null-values/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/null-values/main.go)
* Not handling rows iteration errors
Call the `Err` method of `sql.Rows` after row iterations to ensure that you havent missed an error while preparing the next row.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/rows-iterations-errors)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/rows-iterations-errors)
### Not closing transient resources (HTTP body, `sql.Rows`, and `os.File`) (#79)
@ -1874,7 +1874,7 @@ func consumer(ch <-chan Event) {
Eventually close all structs implementing `io.Closer` to avoid possible leaks.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/79-closing-resources/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/79-closing-resources/)
### Forgetting the return statement after replying to an HTTP request (#80)
@ -1924,7 +1924,7 @@ func handler(w http.ResponseWriter, req *http.Request) {
}
```
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/80-http-return/main.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/80-http-return/main.go)
### Using the default HTTP client and server (#81)
@ -1932,7 +1932,7 @@ func handler(w http.ResponseWriter, req *http.Request) {
For production-grade applications, dont use the default HTTP client and server implementations. These implementations are missing timeouts and behaviors that should be mandatory in production.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/81-default-http-client-server/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/81-default-http-client-server/)
## Testing
@ -1942,7 +1942,7 @@ func handler(w http.ResponseWriter, req *http.Request) {
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/82-categorizing-tests/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/82-categorizing-tests/)
### Not enabling the race flag (#83)
@ -1962,7 +1962,7 @@ func handler(w http.ResponseWriter, req *http.Request) {
Table-driven tests are an efficient way to group a set of similar tests to prevent code duplication and make future updates easier to handle.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/85-table-driven-tests/main_test.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/85-table-driven-tests/main_test.go)
### Sleeping in unit tests (#86)
@ -1970,7 +1970,7 @@ func handler(w http.ResponseWriter, req *http.Request) {
Avoid sleeps using synchronization to make a test less flaky and more robust. If synchronization isnt possible, consider a retry approach.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/86-sleeping/main_test.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/86-sleeping/main_test.go)
### Not dealing with the time API efficiently (#87)
@ -1978,17 +1978,17 @@ func handler(w http.ResponseWriter, req *http.Request) {
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/87-time-api/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/87-time-api/)
### Not using testing utility packages (`httptest` and `iotest`) (#88)
* The `httptest` package is helpful for dealing with HTTP applications. It provides a set of utilities to test both clients and servers.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/httptest/main_test.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/httptest/main_test.go)
* The `iotest` package helps write io.Reader and test that an application is tolerant to errors.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/iotest/main_test.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/iotest/main_test.go)
### Writing inaccurate benchmarks (#89)
@ -2004,7 +2004,7 @@ func handler(w http.ResponseWriter, req *http.Request) {
Read the full section [here](89-benchmarks.md).
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/89-benchmark/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/89-benchmark/)
### Not exploring all the Go testing features (#90)
@ -2016,19 +2016,19 @@ Read the full section [here](89-benchmarks.md).
Place unit tests in a different package to enforce writing tests that focus on an exposed behavior, not internals.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/different-package/main_test.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/different-package/main_test.go)
* Utility functions
Handling errors using the `*testing.T` variable instead of the classic `if err != nil` makes code shorter and easier to read.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/utility-function/main_test.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/utility-function/main_test.go)
* Setup and teardown
You can use setup and teardown functions to configure a complex environment, such as in the case of integration tests.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/setup-teardown/main_test.go)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/setup-teardown/main_test.go)
### Not using fuzzing (community mistake)
@ -2050,19 +2050,19 @@ Credits: [@jeromedoucet](https://github.com/jeromedoucet)
Being conscious of the cache line concept is critical to understanding how to organize data in data-intensive applications. A CPU doesnt 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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/cache-line/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/cache-line/)
* Slice of structs vs. struct of slices
<!-- TODO -->
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/slice-structs/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/slice-structs/)
* Predictability
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) isnt predictable.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/predictability/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/predictability/)
* Cache placement policy
@ -2074,7 +2074,7 @@ Credits: [@jeromedoucet](https://github.com/jeromedoucet)
Knowing that lower levels of CPU caches arent shared across all the cores helps avoid performance-degrading patterns such as false sharing while writing concurrency code. Sharing memory is an illusion.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/92-false-sharing/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/92-false-sharing/)
### Not taking into account instruction-level parallelism (#93)
@ -2082,7 +2082,7 @@ Credits: [@jeromedoucet](https://github.com/jeromedoucet)
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/93-instruction-level-parallelism/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/93-instruction-level-parallelism/)
### Not being aware of data alignment (#94)
@ -2090,7 +2090,7 @@ Credits: [@jeromedoucet](https://github.com/jeromedoucet)
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).
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/94-data-alignment/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/94-data-alignment/)
### Not understanding stack vs. heap (#95)
@ -2098,7 +2098,7 @@ Credits: [@jeromedoucet](https://github.com/jeromedoucet)
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.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/95-stack-heap/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/95-stack-heap/)
### Not knowing how to reduce allocations (API change, compiler optimizations, and `sync.Pool`) (#96)
@ -2106,7 +2106,7 @@ Credits: [@jeromedoucet](https://github.com/jeromedoucet)
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 `sync.Pool`.
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/96-reduce-allocations/)
[Source code](https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/96-reduce-allocations/)
### Not relying on inlining (#97)

View file

@ -61,11 +61,11 @@ extra:
extra_css:
- stylesheets/extra.css
nav:
- Book:
- 📖 Book:
- book.md
- chapter-1.md
- external.md
- Go Mistakes:
- Go Mistakes:
- index.md
- Full Sections:
- 9-generics.md
@ -74,8 +74,6 @@ nav:
- 56-concurrency-faster.md
- 89-benchmarks.md
- 98-profiling-execution-tracing.md
- ❤️ Go Jobs:
- jobs.md
markdown_extensions:
- admonition
- pymdownx.details

View file

@ -2738,7 +2738,7 @@
<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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/1-variable-shadowing/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/1-variable-shadowing/main.go">Source code</a></p>
<h3 id="unnecessary-nested-code-2">Unnecessary nested code (#2)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -2779,7 +2779,7 @@
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="c1">// ...</span>
</span></code></pre></div>
<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 codes readability.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/2-nested-code/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/2-nested-code/main.go">Source code</a></p>
<h3 id="misusing-init-functions-3">Misusing init functions (#3)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -2793,7 +2793,7 @@
<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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/3-init-functions/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/3-init-functions/">Source code</a></p>
<h3 id="overusing-getters-and-setters-4">Overusing getters and setters (#4)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -2817,7 +2817,7 @@ Whats the main problem if we overuse interfaces? The answer is that they make
<p>Dont design with interfaces, discover them.</p>
</div>
<p>Lets not try to solve a problem abstractly but solve what has to be solved now. Last, but not least, if its unclear how an interface makes the code better, we should probably consider removing it to make our code simpler.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/5-interface-pollution/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/5-interface-pollution/">Source code</a></p>
<h3 id="interface-on-the-producer-side-6">Interface on the producer side (#6)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -2825,7 +2825,7 @@ Whats 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 its not up to the producer to force a given abstraction for all the clients. Instead, its 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/6-interface-producer/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/6-interface-producer/">Source code</a></p>
<h3 id="returning-interfaces-7">Returning interfaces (#7)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -2838,14 +2838,14 @@ Whats 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> doesnt 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/8-any/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/8-any/main.go">Source code</a></p>
<h3 id="being-confused-about-when-to-use-generics-9">Being confused about when to use generics (#9)</h3>
<details class="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 <a href="9-generics/">here</a>.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/9-generics/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/9-generics/main.go">Source code</a></p>
<h3 id="not-being-aware-of-the-possible-problems-with-type-embedding-10">Not being aware of the possible problems with type embedding (#10)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -2871,7 +2871,7 @@ promoted to <code>Foo</code>. Therefore, Baz becomes available from Foo.</p>
<li>It shouldnt 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, lets make sure we dont do it solely for cosmetics and not promote elements that should remain hidden.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/10-type-embedding/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/10-type-embedding/main.go">Source code</a></p>
<h3 id="not-using-the-functional-options-pattern-11">Not using the functional options pattern (#11)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -2925,7 +2925,7 @@ promoted to <code>Foo</code>. Therefore, Baz becomes available from Foo.</p>
</span><span id="__span-5-40"><a id="__codelineno-5-40" name="__codelineno-5-40" href="#__codelineno-5-40"></a><span class="p">}</span>
</span></code></pre></div>
<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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/11-functional-options/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/11-functional-options/">Source code</a></p>
<h3 id="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, its 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.
@ -2943,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> doesnt 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/13-utility-packages/stringset.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/02-code-project-organization/13-utility-packages/stringset.go">Source code</a></p>
<h3 id="ignoring-package-name-collisions-14">Ignoring package name collisions (#14)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -2995,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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/17-octal-literals/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/17-octal-literals/main.go">Source code</a></p>
<h3 id="neglecting-integer-overflows-18">Neglecting integer overflows (#18)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3007,7 +3007,7 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
<div class="language-shell highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>constant<span class="w"> </span><span class="m">2147483648</span><span class="w"> </span>overflows<span class="w"> </span>int32
</span></code></pre></div>
<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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/18-integer-overflows">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/18-integer-overflows">Source code</a></p>
<h3 id="not-understanding-floating-points-19">Not understanding floating-points (#19)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3025,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>
</ul>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/19-floating-points/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/19-floating-points/main.go">Source code</a></p>
<h3 id="not-understanding-slice-length-and-capacity-20">Not understanding slice length and capacity (#20)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>Understanding the difference between slice length and capacity should be part of a Go developers 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 <a href="20-slice/">here</a>.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/20-slice-length-cap/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/20-slice-length-cap/main.go">Source code</a></p>
<h3 id="inefficient-slice-initialization-21">Inefficient slice initialization (#21)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3040,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, theres 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/21-slice-init/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/21-slice-init/main.go">Source code</a></p>
<h3 id="being-confused-about-nil-vs-empty-slice-22">Being confused about nil vs. empty slice (#22)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3053,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, lets check whether the libraries we use make the distinctions between nil and empty slices to prevent unexpected behaviors.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/22-nil-empty-slice/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/22-nil-empty-slice/">Source code</a></p>
<h3 id="not-properly-checking-if-a-slice-is-empty-23">Not properly checking if a slice is empty (#23)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3061,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 its nil.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/23-checking-slice-empty/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/23-checking-slice-empty/main.go">Source code</a></p>
<h3 id="not-making-slice-copies-correctly-24">Not making slice copies correctly (#24)</h3>
<details class="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 slices 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 shouldnt be surprised if we find them in a codebase.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/24-slice-copy/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/24-slice-copy/main.go">Source code</a></p>
<h3 id="unexpected-side-effects-using-slice-append-25">Unexpected side effects using slice append (#25)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3079,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 slices capacity is equal to <code>max - low</code>.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/25-slice-append/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/25-slice-append/main.go">Source code</a></p>
<h3 id="slices-and-memory-leaks-26">Slices and memory leaks (#26)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3087,10 +3087,10 @@ Meanwhile, we should also look at golangci-lint (<a href="https://github.com/gol
</details>
<h4 id="leaking-capacity">Leaking capacity</h4>
<p>Remember that slicing a large slice or array can lead to potential high memory consumption. The remaining space wont 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/capacity-leak">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/capacity-leak">Source code</a></p>
<h4 id="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 wont 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/slice-pointers">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/26-slice-memory-leak/slice-pointers">Source code</a></p>
<h3 id="inefficient-map-initialization-27">Inefficient map initialization (#27)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3098,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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/27-map-init/main_test.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/27-map-init/main_test.go">Source code</a></p>
<h3 id="maps-and-memory-leaks-28">Maps and memory leaks (#28)</h3>
<details class="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 <a href="28-maps-memory-leaks/">here</a>.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/28-map-memory-leak/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/28-map-memory-leak/main.go">Source code</a></p>
<h3 id="comparing-values-incorrectly-29">Comparing values incorrectly (#29)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3128,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 dont reinvent the wheel.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/29-comparing-values/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/03-data-types/29-comparing-values/main.go">Source code</a></p>
<h2 id="control-structures">Control Structures</h2>
<h3 id="ignoring-that-elements-are-copied-in-range-loops-30">Ignoring that elements are copied in <code>range</code> loops (#30)</h3>
<details class="info" open="open">
@ -3146,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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/30-range-loop-element-copied/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/30-range-loop-element-copied/">Source code</a></p>
<h3 id="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>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3162,14 +3162,12 @@ One additional note: we must remember that the standard library has some existin
</span><span id="__span-9-7"><a id="__codelineno-9-7" name="__codelineno-9-7" href="#__codelineno-9-7"></a><span class="p">}</span>
</span></code></pre></div>
<p>This code updates the last index to 10. However, if we run this code, it does not print 10; it prints 2.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/31-range-loop-arg-evaluation/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/31-range-loop-arg-evaluation/">Source code</a></p>
<h3 id="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>
<details class="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 class="warning" open="open">
<summary>Warning</summary>
<p>This mistake isn't relevant anymore from Go 1.22 (<a href="https://go.dev/blog/loopvar-preview">source</a>).</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 loops scope or creating a pointer referencing a slice element via its index. Both solutions are fine.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/32-range-loop-pointers/">Source code :simple-github:</a></p>
<h3 id="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>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3183,7 +3181,7 @@ One additional note: we must remember that the standard library has some existin
</ul>
<!-- TODO -->
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/33-map-iteration/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/33-map-iteration/main.go">Source code</a></p>
<h3 id="ignoring-how-the-break-statement-works-34">Ignoring how the <code>break</code> statement works (#34)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3215,7 +3213,7 @@ One additional note: we must remember that the standard library has some existin
</span><span id="__span-11-10"><a id="__codelineno-11-10" name="__codelineno-11-10" href="#__codelineno-11-10"></a><span class="w"> </span><span class="p">}</span>
</span></code></pre></div>
<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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/34-break/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/34-break/main.go">Source code</a></p>
<h3 id="using-defer-inside-a-loop-35">Using <code>defer</code> inside a loop (#35)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3261,7 +3259,7 @@ One additional note: we must remember that the standard library has some existin
</span><span id="__span-13-20"><a id="__codelineno-13-20" name="__codelineno-13-20" href="#__codelineno-13-20"></a><span class="p">}</span>
</span></code></pre></div>
<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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/35-defer-loop/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/04-control-structures/35-defer-loop/main.go">Source code</a></p>
<h2 id="strings">Strings</h2>
<h3 id="not-understanding-the-concept-of-rune-36">Not understanding the concept of rune (#36)</h3>
<details class="info" open="open">
@ -3277,7 +3275,7 @@ One additional note: we must remember that the standard library has some existin
<li>Using UTF-8, a Unicode code point can be encoded into 1 to 4 bytes.</li>
<li>Using <code>len()</code> on a string in Go returns the number of bytes, not the number of runes.</li>
</ul>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/36-rune/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/36-rune/main.go">Source code</a></p>
<h3 id="inaccurate-string-iteration-37">Inaccurate string iteration (#37)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3327,7 +3325,7 @@ One additional note: we must remember that the standard library has some existin
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a><span class="nx">r</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="nb">rune</span><span class="p">(</span><span class="nx">s</span><span class="p">)[</span><span class="mi">4</span><span class="p">]</span>
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%c\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="p">)</span><span class="w"> </span><span class="c1">// o</span>
</span></code></pre></div>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/37-string-iteration/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/37-string-iteration/main.go">Source code</a></p>
<h3 id="misusing-trim-functions-38">Misusing trim functions (#38)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3340,7 +3338,7 @@ One additional note: we must remember that the standard library has some existin
<p><a class="glightbox" href="img/trim.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="" src="img/trim.png" /></a></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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/38-trim/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/38-trim/main.go">Source code</a></p>
<h3 id="under-optimized-strings-concatenation-39">Under-optimized strings concatenation (#39)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3392,7 +3390,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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/39-string-concat/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/39-string-concat/">Source code</a></p>
<h3 id="useless-string-conversions-40">Useless string conversions (#40)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3400,7 +3398,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 were wondering whether we should work with strings or <code>[]byte</code>, lets recall that working with <code>[]byte</code> isnt 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 were 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/40-string-conversion/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/40-string-conversion/main.go">Source code</a></p>
<h3 id="substring-and-memory-leaks-41">Substring and memory leaks (#41)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3408,7 +3406,7 @@ One additional note: we must remember that the standard library has some existin
</details>
<p>In mistake <a href="#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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/41-substring-memory-leak/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/05-strings/41-substring-memory-leak/main.go">Source code</a></p>
<h2 id="functions-and-methods">Functions and Methods</h2>
<h3 id="not-knowing-which-type-of-receiver-to-use-42">Not knowing which type of receiver to use (#42)</h3>
<details class="info" open="open">
@ -3446,7 +3444,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, its impossible to be exhaustive, as there will always be edge cases, but this sections goal was to provide guidance to cover most cases. By default, we can choose to go with a value receiver unless theres a good reason not to do so. In doubt, we should use a pointer receiver.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/42-receiver/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/42-receiver/">Source code</a></p>
<h3 id="never-using-named-result-parameters-43">Never using named result parameters (#43)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3461,7 +3459,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 theres a clear benefit.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/43-named-result-parameters/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/43-named-result-parameters/main.go">Source code</a></p>
<h3 id="unintended-side-effects-with-named-result-parameters-44">Unintended side effects with named result parameters (#44)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3484,7 +3482,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 havent assigned any value to the <code>err</code> variable. Its 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 arent always straightforward to spot while reading code. Therefore, lets remain cautious when using named result parameters, to avoid potential side effects.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/44-side-effects-named-result-parameters/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/44-side-effects-named-result-parameters/main.go">Source code</a></p>
<h3 id="returning-a-nil-receiver-45">Returning a nil receiver (#45)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3492,14 +3490,14 @@ One additional note: we must remember that the standard library has some existin
</details>
<!-- TODO -->
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/45-nil-receiver/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/45-nil-receiver/main.go">Source code</a></p>
<h3 id="using-a-filename-as-a-function-input-46">Using a filename as a function input (#46)</h3>
<details class="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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/46-function-input/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/46-function-input/">Source code</a></p>
<h3 id="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>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3556,7 +3554,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 doesnt 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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/47-defer-evaluation/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/06-functions-methods/47-defer-evaluation/">Source code</a></p>
<h2 id="error-management">Error Management</h2>
<h3 id="panicking-48">Panicking (#48)</h3>
<details class="info" open="open">
@ -3579,7 +3577,7 @@ One additional note: we must remember that the standard library has some existin
</span><span id="__span-31-6"><a id="__codelineno-31-6" name="__codelineno-31-6" href="#__codelineno-31-6"></a> main.go:7 +0xb3
</span></code></pre></div>
<p>Panicking in Go should be used sparingly. There are two prominent cases, one to signal a programmer error (e.g., <a href="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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/48-panic/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/48-panic/main.go">Source code</a></p>
<h3 id="ignoring-when-to-wrap-an-error-49">Ignoring when to wrap an error (#49)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3591,7 +3589,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 doesnt 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 shouldnt use error wrapping but error transformation, for example, using fmt.Errorf with the %v directive.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/49-error-wrapping/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/49-error-wrapping/main.go">Source code</a></p>
<h3 id="comparing-an-error-type-inaccurately-50">Comparing an error type inaccurately (#50)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3599,7 +3597,7 @@ One additional note: we must remember that the standard library has some existin
</details>
<!-- TODO -->
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/50-compare-error-type/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/50-compare-error-type/main.go">Source code</a></p>
<h3 id="comparing-an-error-value-inaccurately-51">Comparing an error value inaccurately (#51)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3616,7 +3614,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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/51-comparing-error-value/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/51-comparing-error-value/main.go">Source code</a></p>
<h3 id="handling-an-error-twice-52">Handling an error twice (#52)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3624,13 +3622,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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/52-handling-error-twice/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/52-handling-error-twice/main.go">Source code</a></p>
<h3 id="not-handling-an-error-53">Not handling an error (#53)</h3>
<details class="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>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/53-not-handling-error/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/53-not-handling-error/main.go">Source code</a></p>
<h3 id="not-handling-defer-errors-54">Not handling <code>defer</code> errors (#54)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3655,7 +3653,7 @@ One additional note: we must remember that the standard library has some existin
</span><span id="__span-35-2"><a id="__codelineno-35-2" name="__codelineno-35-2" href="#__codelineno-35-2"></a><span class="c1">// Hence, it&#39;s accepted to miss some of them in case of errors.</span>
</span><span id="__span-35-3"><a id="__codelineno-35-3" name="__codelineno-35-3" href="#__codelineno-35-3"></a><span class="nx">_</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">notify</span><span class="p">()</span>
</span></code></pre></div>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/54-defer-errors/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/07-error-management/54-defer-errors/main.go">Source code</a></p>
<h2 id="concurrency-foundations">Concurrency: Foundations</h2>
<h3 id="mixing-up-concurrency-and-parallelism-55">Mixing up concurrency and parallelism (#55)</h3>
<details class="info" open="open">
@ -3676,7 +3674,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 isnt 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 <a href="56-concurrency-faster/">here</a>.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/56-faster/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/56-faster/">Source code</a></p>
<h3 id="being-puzzled-about-when-to-use-channels-or-mutexes-57">Being puzzled about when to use channels or mutexes (#57)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3714,7 +3712,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 cant be controlled. Here, the timing of events is the goroutines execution order.</p>
<p>In summary, when we work in concurrent applications, its 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 doesnt 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/58-races/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/58-races/">Source code</a></p>
<h3 id="not-understanding-the-concurrency-impacts-of-a-workload-type-59">Not understanding the concurrency impacts of a workload type (#59)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3731,7 +3729,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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/59-workload-type/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/59-workload-type/main.go">Source code</a></p>
<h3 id="misunderstanding-go-contexts-60">Misunderstanding Go contexts (#60)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3761,7 +3759,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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/60-contexts/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/08-concurrency-foundations/60-contexts/main.go">Source code</a></p>
<h2 id="concurrency-practice">Concurrency: Practice</h2>
<h3 id="propagating-an-inappropriate-context-61">Propagating an inappropriate context (#61)</h3>
<details class="info" open="open">
@ -3801,7 +3799,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. <a href="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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/61-inappropriate-context/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/61-inappropriate-context/main.go">Source code</a></p>
<h3 id="starting-a-goroutine-without-knowing-when-to-stop-it-62">Starting a goroutine without knowing when to stop it (#62)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3855,7 +3853,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 its 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, lets 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, its 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/62-starting-goroutine/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/62-starting-goroutine/">Source code</a></p>
<h3 id="not-being-careful-with-goroutines-and-loop-variables-63">Not being careful with goroutines and loop variables (#63)</h3>
<details class="warning" open="open">
<summary>Warning</summary>
@ -3902,7 +3900,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 theres 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 theres 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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/64-select-behavior/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/64-select-behavior/main.go">Source code</a></p>
<h3 id="not-using-notification-channels-65">Not using notification channels (#65)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3957,7 +3955,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>Lets keep this idea in mind: nil channels are useful in some conditions and should be part of the Go developers toolset when dealing with concurrent code.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/66-nil-channels/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/66-nil-channels/main.go">Source code</a></p>
<h3 id="being-puzzled-about-channel-size-67">Being puzzled about channel size (#67)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -3969,43 +3967,43 @@ the use case. However, we should see the two options as complementary. </p>
<summary>TL;DR</summary>
<p>Being aware that string formatting may lead to calling existing functions means watching out for possible deadlocks and other data races.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/68-string-formatting/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/68-string-formatting/main.go">Source code</a></p>
<h3 id="creating-data-races-with-append-69">Creating data races with append (#69)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>Calling <code>append</code> isnt always data-race-free; hence, it shouldnt be used concurrently on a shared slice.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/69-data-race-append/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/69-data-race-append/main.go">Source code</a></p>
<h3 id="using-mutexes-inaccurately-with-slices-and-maps-70">Using mutexes inaccurately with slices and maps (#70)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>Remembering that slices and maps are pointers can prevent common data races.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/70-mutex-slices-maps/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/70-mutex-slices-maps/main.go">Source code</a></p>
<h3 id="misusing-syncwaitgroup-71">Misusing <code>sync.WaitGroup</code> (#71)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>To accurately use <code>sync.WaitGroup</code>, call the <code>Add</code> method before spinning up goroutines.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/71-wait-group/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/71-wait-group/main.go">Source code</a></p>
<h3 id="forgetting-about-synccond-72">Forgetting about <code>sync.Cond</code> (#72)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>You can send repeated notifications to multiple goroutines with <code>sync.Cond</code>.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/72-cond/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/72-cond/main.go">Source code</a></p>
<h3 id="not-using-errgroup-73">Not using <code>errgroup</code> (#73)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>You can synchronize a group of goroutines and handle errors and contexts with the <code>errgroup</code> package.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/73-errgroup/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/73-errgroup/main.go">Source code</a></p>
<h3 id="copying-a-sync-type-74">Copying a <code>sync</code> type (#74)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p><code>sync</code> types shouldnt be copied.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/74-copying-sync/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/74-copying-sync/main.go">Source code</a></p>
<h2 id="standard-library">Standard Library</h2>
<h3 id="providing-a-wrong-time-duration-75">Providing a wrong time duration (#75)</h3>
<details class="info" open="open">
@ -4028,7 +4026,7 @@ the use case. However, we should see the two options as complementary. </p>
</span><span id="__span-47-2"><a id="__codelineno-47-2" name="__codelineno-47-2" href="#__codelineno-47-2"></a><span class="c1">// Or</span>
</span><span id="__span-47-3"><a id="__codelineno-47-3" name="__codelineno-47-3" href="#__codelineno-47-3"></a><span class="nx">ticker</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">NewTicker</span><span class="p">(</span><span class="mi">1000</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Nanosecond</span><span class="p">)</span>
</span></code></pre></div></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/75-wrong-time-duration/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/75-wrong-time-duration/main.go">Source code</a></p>
<h3 id="timeafter-and-memory-leaks-76"><code>time.After</code> and memory leaks (#76)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -4083,29 +4081,29 @@ the use case. However, we should see the two options as complementary. </p>
</span><span id="__span-51-13"><a id="__codelineno-51-13" name="__codelineno-51-13" href="#__codelineno-51-13"></a><span class="w"> </span><span class="p">}</span>
</span><span id="__span-51-14"><a id="__codelineno-51-14" name="__codelineno-51-14" href="#__codelineno-51-14"></a><span class="p">}</span>
</span></code></pre></div>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/76-time-after/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/76-time-after/main.go">Source code</a></p>
<h3 id="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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/type-embedding/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/type-embedding/main.go">Source code</a></p>
<ul>
<li>JSON and the monotonic clock</li>
</ul>
<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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/monotonic-clock/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/monotonic-clock/main.go">Source code</a></p>
<ul>
<li>Map of <code>any</code></li>
</ul>
<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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/map-any/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/77-json-handling/map-any/main.go">Source code</a></p>
<h3 id="common-sql-mistakes-78">Common SQL mistakes (#78)</h3>
<ul>
<li>Forgetting that <code>sql.Open</code> doesn't necessarily establish connections to a database</li>
</ul>
<p>Call the <code>Ping</code> or <code>PingContext</code> method if you need to test your configuration and make sure a database is reachable.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/sql-open">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/sql-open">Source code</a></p>
<ul>
<li>Forgetting about connections pooling</li>
</ul>
@ -4114,23 +4112,23 @@ the use case. However, we should see the two options as complementary. </p>
<li>Not using prepared statements</li>
</ul>
<p>Using SQL prepared statements makes queries more efficient and more secure.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/prepared-statements">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/prepared-statements">Source code</a></p>
<ul>
<li>Mishandling null values</li>
</ul>
<p>Deal with nullable columns in tables using pointers or <code>sql.NullXXX</code> types.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/null-values/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/null-values/main.go">Source code</a></p>
<ul>
<li>Not handling rows iteration errors</li>
</ul>
<p>Call the <code>Err</code> method of <code>sql.Rows</code> after row iterations to ensure that you havent missed an error while preparing the next row.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/rows-iterations-errors">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/78-sql/rows-iterations-errors">Source code</a></p>
<h3 id="not-closing-transient-resources-http-body-sqlrows-and-osfile-79">Not closing transient resources (HTTP body, <code>sql.Rows</code>, and <code>os.File</code>) (#79)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>Eventually close all structs implementing <code>io.Closer</code> to avoid possible leaks.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/79-closing-resources/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/79-closing-resources/">Source code</a></p>
<h3 id="forgetting-the-return-statement-after-replying-to-an-http-request-80">Forgetting the return statement after replying to an HTTP request (#80)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -4166,20 +4164,20 @@ the use case. However, we should see the two options as complementary. </p>
</span><span id="__span-55-9"><a id="__codelineno-55-9" name="__codelineno-55-9" href="#__codelineno-55-9"></a><span class="w"> </span><span class="nx">w</span><span class="p">.</span><span class="nx">WriteHeader</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusCreated</span><span class="p">)</span>
</span><span id="__span-55-10"><a id="__codelineno-55-10" name="__codelineno-55-10" href="#__codelineno-55-10"></a><span class="p">}</span>
</span></code></pre></div>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/80-http-return/main.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/80-http-return/main.go">Source code</a></p>
<h3 id="using-the-default-http-client-and-server-81">Using the default HTTP client and server (#81)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>For production-grade applications, dont use the default HTTP client and server implementations. These implementations are missing timeouts and behaviors that should be mandatory in production.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/81-default-http-client-server/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/81-default-http-client-server/">Source code</a></p>
<h2 id="testing">Testing</h2>
<h3 id="not-categorizing-tests-build-tags-environment-variables-and-short-mode-82">Not categorizing tests (build tags, environment variables, and short mode) (#82)</h3>
<details class="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>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/82-categorizing-tests/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/82-categorizing-tests/">Source code</a></p>
<h3 id="not-enabling-the-race-flag-83">Not enabling the race flag (#83)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -4195,28 +4193,28 @@ the use case. However, we should see the two options as complementary. </p>
<summary>TL;DR</summary>
<p>Table-driven tests are an efficient way to group a set of similar tests to prevent code duplication and make future updates easier to handle.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/85-table-driven-tests/main_test.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/85-table-driven-tests/main_test.go">Source code</a></p>
<h3 id="sleeping-in-unit-tests-86">Sleeping in unit tests (#86)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
<p>Avoid sleeps using synchronization to make a test less flaky and more robust. If synchronization isnt possible, consider a retry approach.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/86-sleeping/main_test.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/86-sleeping/main_test.go">Source code</a></p>
<h3 id="not-dealing-with-the-time-api-efficiently-87">Not dealing with the time API efficiently (#87)</h3>
<details class="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>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/87-time-api/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/87-time-api/">Source code</a></p>
<h3 id="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>
</ul>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/httptest/main_test.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/httptest/main_test.go">Source code</a></p>
<ul>
<li>The <code>iotest</code> package helps write io.Reader and test that an application is tolerant to errors.</li>
</ul>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/iotest/main_test.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/88-utility-package/iotest/main_test.go">Source code</a></p>
<h3 id="writing-inaccurate-benchmarks-89">Writing inaccurate benchmarks (#89)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -4230,7 +4228,7 @@ the use case. However, we should see the two options as complementary. </p>
</ul>
</details>
<p>Read the full section <a href="89-benchmarks/">here</a>.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/89-benchmark/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/89-benchmark/">Source code</a></p>
<h3 id="not-exploring-all-the-go-testing-features-90">Not exploring all the Go testing features (#90)</h3>
<ul>
<li>Code coverage</li>
@ -4240,17 +4238,17 @@ the use case. However, we should see the two options as complementary. </p>
<li>Testing from a different package</li>
</ul>
<p>Place unit tests in a different package to enforce writing tests that focus on an exposed behavior, not internals.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/different-package/main_test.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/different-package/main_test.go">Source code</a></p>
<ul>
<li>Utility functions</li>
</ul>
<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>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/utility-function/main_test.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/utility-function/main_test.go">Source code</a></p>
<ul>
<li>Setup and teardown</li>
</ul>
<p>You can use setup and teardown functions to configure a complex environment, such as in the case of integration tests.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/setup-teardown/main_test.go">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/11-testing/90-testing-features/setup-teardown/main_test.go">Source code</a></p>
<h3 id="not-using-fuzzing-community-mistake">Not using fuzzing (community mistake)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>
@ -4267,18 +4265,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 doesnt 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><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/cache-line/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/cache-line/">Source code</a></p>
<ul>
<li>Slice of structs vs. struct of slices</li>
</ul>
<!-- TODO -->
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/slice-structs/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/slice-structs/">Source code</a></p>
<ul>
<li>Predictability</li>
</ul>
<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) isnt predictable.</p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/predictability/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/91-cpu-caches/predictability/">Source code</a></p>
<ul>
<li>Cache placement policy</li>
</ul>
@ -4288,31 +4286,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 arent shared across all the cores helps avoid performance-degrading patterns such as false sharing while writing concurrency code. Sharing memory is an illusion.</p>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/92-false-sharing/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/92-false-sharing/">Source code</a></p>
<h3 id="not-taking-into-account-instruction-level-parallelism-93">Not taking into account instruction-level parallelism (#93)</h3>
<details class="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>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/93-instruction-level-parallelism/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/93-instruction-level-parallelism/">Source code</a></p>
<h3 id="not-being-aware-of-data-alignment-94">Not being aware of data alignment (#94)</h3>
<details class="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>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/94-data-alignment/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/94-data-alignment/">Source code</a></p>
<h3 id="not-understanding-stack-vs-heap-95">Not understanding stack vs. heap (#95)</h3>
<details class="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>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/95-stack-heap/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/95-stack-heap/">Source code</a></p>
<h3 id="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>
<details class="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>
</details>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/96-reduce-allocations/">Source code :simple-github:</a></p>
<p><a href="https://github.com/teivah/100-go-mistakes/tree/master/src/12-optimizations/96-reduce-allocations/">Source code</a></p>
<h3 id="not-relying-on-inlining-97">Not relying on inlining (#97)</h3>
<details class="info" open="open">
<summary>TL;DR</summary>

File diff suppressed because one or more lines are too long

View file

@ -2,67 +2,67 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://100go.co/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/20-slice/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/28-maps-memory-leaks/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/56-concurrency-faster/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/89-benchmarks/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/9-generics/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/98-profiling-execution-tracing/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/book/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/chapter-1/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/external/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/ja/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/jobs/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>https://100go.co/zh/</loc>
<lastmod>2024-02-03</lastmod>
<lastmod>2024-02-13</lastmod>
<changefreq>daily</changefreq>
</url>
</urlset>

Binary file not shown.