diff --git a/site/404.html b/site/404.html index 9a42a23..37a9737 100644 --- a/site/404.html +++ b/site/404.html @@ -11,7 +11,7 @@ - + @@ -82,7 +82,7 @@ - + @@ -237,7 +237,7 @@ - + 100 Go Mistakes @@ -420,9 +420,19 @@ - Made with - - Material for MkDocs + + + + + + + + + + + + + diff --git a/site/chinese/presentation/index.html b/site/chinese/presentation/index.html index cdcb839..4540434 100644 --- a/site/chinese/presentation/index.html +++ b/site/chinese/presentation/index.html @@ -13,7 +13,7 @@ - + @@ -84,7 +84,7 @@ - + @@ -239,7 +239,7 @@ - + 100 Go Mistakes @@ -445,9 +445,19 @@ - Made with - - Material for MkDocs + + + + + + + + + + + + + diff --git a/site/index.html b/site/index.html index c55c22d..1df8a6c 100644 --- a/site/index.html +++ b/site/index.html @@ -13,7 +13,7 @@ - + @@ -89,7 +89,7 @@ - + @@ -244,7 +244,7 @@ - + 100 Go Mistakes @@ -689,9 +689,19 @@ - Made with - - Material for MkDocs + + + + + + + + + + + + + diff --git a/site/jobs/index.html b/site/jobs/index.html index dba84e3..ccd8f9d 100644 --- a/site/jobs/index.html +++ b/site/jobs/index.html @@ -15,7 +15,7 @@ - + @@ -91,7 +91,7 @@ - + @@ -246,7 +246,7 @@ - + 100 Go Mistakes @@ -431,7 +431,7 @@ ❤️ Go Jobs -Is your company hiring? Sponsor this repository and let Go developers know your opportunities in this section (traffic: +400 unique visitor per week). +Is your company hiring? Sponsor this repository and let Go developers know your opportunities in this section (traffic: +400 unique visitor per week). For example: [Company] Remote (USA) - $96,000 to $120,000 a year Job Description @@ -459,9 +459,19 @@ - Made with - - Material for MkDocs + + + + + + + + + + + + + diff --git a/site/mistakes/index.html b/site/mistakes/index.html index 167808a..36f9ab8 100644 --- a/site/mistakes/index.html +++ b/site/mistakes/index.html @@ -15,7 +15,7 @@ - + @@ -91,7 +91,7 @@ - + @@ -246,7 +246,7 @@ - + 100 Go Mistakes @@ -355,13 +355,6 @@ - - - ❤️ Sponsor this Repository - - - - Code and Project Organization @@ -488,11 +481,11 @@ - - Data Types (PDF chapter) + + Data Types - + @@ -1307,13 +1300,6 @@ - - - ❤️ Sponsor this Repository - - - - Code and Project Organization @@ -1440,11 +1426,11 @@ - - Data Types (PDF chapter) + + Data Types - + @@ -2184,23 +2170,6 @@ 💡 Go Mistakes This section contains a summary of the 100 mistakes in the book. Meanwhile, it's also a section open to the community. If you believe that a mistake should be added, please create a community mistake issue. -❤️ Sponsor this Repository -Is your company hiring? Sponsor this repository and let Go developers know your opportunities in this section (traffic: +400 unique visitor per week). -For example: - - -[Company] Remote (USA) - $96,000 to $120,000 a year - -### Job Description - -... - -### Qualifications - -... - - - Code and Project Organization Unintended variable shadowing (#1) @@ -2235,7 +2204,7 @@ To help clients and maintainers understand your code’s purpose, document exported elements. Not using linters (#16) To improve code quality and consistency, use linters and formatters. -Data Types (PDF chapter) +Data Types Creating confusion with octal literals (#17) When reading existing code, bear in mind that integer literals starting with 0 are octal numbers. Also, to improve readability, make octal integers explicit by prefixing them with 0o. Neglecting integer overflows (#18) @@ -2529,9 +2498,19 @@ - Made with - - Material for MkDocs + + + + + + + + + + + + + diff --git a/site/search/search_index.json b/site/search/search_index.json index a585d3a..471727b 100644 --- a/site/search/search_index.json +++ b/site/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"\ud83d\udcd6 Book Presentation","text":"Source code and community space of \ud83d\udcd6 100 Go Mistakes and How to Avoid Them, published by Manning in August 2022."},{"location":"#book-description","title":"Book Description","text":"If you're a Go developer looking to improve your skills, this book is for you. With a focus on practical examples, 100 Go Mistakes and How to Avoid Them covers a wide range of topics from concurrency and error handling to testing and code organization. You'll learn to write more idiomatic, efficient, and maintainable code and become a proficient Go developer. "},{"location":"#quotes","title":"Quotes","text":"This should be the required reading for all Golang developers before they touch code in Production... It's the Golang equivalent of the legendary 'Effective Java' by Joshua Bloch. \u2013 Neeraj Shah Not having this will be the 101st mistake a Go programmer could make. \u2013 Anupam Sengupta"},{"location":"#where-to-buy","title":"Where to Buy?","text":"100 Go Mistakes and How to Avoid Them (\ud83c\uddec\ud83c\udde7 edition: paper, digital, or audiobook): Manning O\u2019Reilly Amazon: .com, .co.uk, .de, .fr, .in, .co.jp, .es, .it, .com.br Go\u8a00\u8a9e100Tips \u958b\u767a\u8005\u306b\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u3078\u306e\u5bfe\u51e6\u6cd5 (\ud83c\uddef\ud83c\uddf5 edition): Amazon: .co.jp "},{"location":"#about-the-author","title":"About the Author","text":"Teiva Harsanyi is a senior software engineer at Google. He has worked in various domains, including insurance, transportation, and safety-critical industries like air traffic management. He is passionate about Go and how to design and implement reliable systems."},{"location":"#external-resources","title":"External Resources","text":""},{"location":"#english","title":"English","text":" Book Review: 100 Go Mistakes and How to Avoid Them: Post, YouTube How to make mistakes in Go - Go Time #190: Episode, Spotify 8LU - 100% Test Coverage Some Tips I learned from 100 Mistakes in Go What can be summarized from 100 Go Mistakes? "},{"location":"#chinese","title":"Chinese","text":" \u6df1\u5ea6\u9605\u8bfb\u4e4b\u300a100 Go Mistakes and How to Avoid Them 100 Go Mistakes \u968f\u8bb0 \u6211\u4e3a\u4ec0\u4e48\u653e\u5f03Go\u8bed\u8a00\uff1f "},{"location":"#japanese","title":"Japanese","text":" \u6700\u8fd1\u8aad\u3093\u3060Go\u8a00\u8a9e\u306e\u672c\u306e\u7d39\u4ecb\uff1a100 Go Mistakes and How to Avoid Them \u300e100 Go Mistakes and How to Avoid Them\u300f\u3092\u8aad\u3080 100 Go Mistakes \u968f\u8bb0 - 01 Code and project organization "},{"location":"#portuguese","title":"Portuguese","text":" Um \u00d3TIMO livro para programadores Go "},{"location":"jobs/","title":"\u2764\ufe0f Go Jobs","text":"Is your company hiring? Sponsor this repository and let Go developers know your opportunities in this section (traffic: +400 unique visitor per week). For example:"},{"location":"jobs/#company-remote-usa-96000-to-120000-a-year","title":"[Company] Remote (USA) - $96,000 to $120,000 a year","text":""},{"location":"jobs/#job-description","title":"Job Description","text":"..."},{"location":"jobs/#qualifications","title":"Qualifications","text":"..."},{"location":"mistakes/","title":"\ud83d\udca1 Go Mistakes","text":"This section contains a summary of the 100 mistakes in the book. Meanwhile, it's also a section open to the community. If you believe that a mistake should be added, please create a community mistake issue."},{"location":"mistakes/#sponsor-this-repository","title":"\u2764\ufe0f Sponsor this Repository","text":"Is your company hiring? Sponsor this repository and let Go developers know your opportunities in this section (traffic: +400 unique visitor per week). For example: [Company] Remote (USA) - $96,000 to $120,000 a year ### Job Description ... ### Qualifications ... "},{"location":"mistakes/#code-and-project-organization","title":"Code and Project Organization","text":""},{"location":"mistakes/#unintended-variable-shadowing-1","title":"Unintended variable shadowing (#1)","text":"Avoiding shadowed variables can help prevent mistakes like referencing the wrong variable or confusing readers."},{"location":"mistakes/#unnecessary-nested-code-2","title":"Unnecessary nested code (#2)","text":"Avoiding nested levels and keeping the happy path aligned on the left makes building a mental code model easier."},{"location":"mistakes/#misusing-init-functions-3","title":"Misusing init functions (#3)","text":"When initializing variables, remember that init functions have limited error handling and make state handling and testing more complex. In most cases, initializations should be handled as specific functions."},{"location":"mistakes/#overusing-getters-and-setters-4","title":"Overusing getters and setters (#4)","text":"Forcing the use of getters and setters isn\u2019t idiomatic in Go. Being pragmatic and finding the right balance between efficiency and blindly following certain idioms should be the way to go."},{"location":"mistakes/#interface-pollution-5","title":"Interface pollution (#5)","text":"Abstractions should be discovered, not created. To prevent unnecessary complexity, create an interface when you need it and not when you foresee needing it, or if you can at least prove the abstraction to be a valid one."},{"location":"mistakes/#interface-on-the-producer-side-6","title":"Interface on the producer side (#6)","text":"Keeping interfaces on the client side avoids unnecessary abstractions."},{"location":"mistakes/#returning-interfaces-7","title":"Returning interfaces (#7)","text":"To prevent being restricted in terms of flexibility, a function shouldn\u2019t return interfaces but concrete implementations in most cases. Conversely, a function should accept interfaces whenever possible."},{"location":"mistakes/#any-says-nothing-8","title":"any says nothing (#8)","text":"Only use any if you need to accept or return any possible type, such as json.Marshal. Otherwise, any doesn\u2019t provide meaningful information and can lead to compile-time issues by allowing a caller to call methods with any data type."},{"location":"mistakes/#being-confused-about-when-to-use-generics-9","title":"Being confused about when to use generics (#9)","text":"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."},{"location":"mistakes/#not-being-aware-of-the-possible-problems-with-type-embedding-10","title":"Not being aware of the possible problems with type embedding (#10)","text":"Using type embedding can also help avoid boilerplate code; however, ensure that doing so doesn\u2019t lead to visibility issues where some fields should have remained hidden."},{"location":"mistakes/#not-using-the-functional-options-pattern-11","title":"Not using the functional options pattern (#11)","text":"To handle options conveniently and in an API-friendly manner, use the functional options pattern."},{"location":"mistakes/#project-misorganization-project-structure-and-package-organization-12","title":"Project misorganization (project structure and package organization) (#12)","text":"Following a layout such as project-layout can be a good way to start structuring Go projects, especially if you are looking for existing conventions to standardize a new project."},{"location":"mistakes/#creating-utility-packages-13","title":"Creating utility packages (#13)","text":"Naming is a critical piece of application design. Creating packages such as common, util, and shared doesn\u2019t bring much value for the reader. Refactor such packages into meaningful and specific package names."},{"location":"mistakes/#ignoring-package-name-collisions-14","title":"Ignoring package name collisions (#14)","text":"To avoid naming collisions between variables and packages, leading to confusion or perhaps even bugs, use unique names for each one. If this isn\u2019t feasible, use an import alias to change the qualifier to differentiate the package name from the variable name, or think of a better name."},{"location":"mistakes/#missing-code-documentation-15","title":"Missing code documentation (#15)","text":"To help clients and maintainers understand your code\u2019s purpose, document exported elements."},{"location":"mistakes/#not-using-linters-16","title":"Not using linters (#16)","text":"To improve code quality and consistency, use linters and formatters."},{"location":"mistakes/#data-types-pdf-chapter","title":"Data Types (PDF chapter)","text":""},{"location":"mistakes/#creating-confusion-with-octal-literals-17","title":"Creating confusion with octal literals (#17)","text":"When reading existing code, bear in mind that integer literals starting with 0 are octal numbers. Also, to improve readability, make octal integers explicit by prefixing them with 0o."},{"location":"mistakes/#neglecting-integer-overflows-18","title":"Neglecting integer overflows (#18)","text":"Because integer overflows and underflows are handled silently in Go, you can implement your own functions to catch them."},{"location":"mistakes/#not-understanding-floating-points-19","title":"Not understanding floating-points (#19)","text":"Making floating-point comparisons within a given delta can ensure that your code is portable. When performing addition or subtraction, group the operations with a similar order of magnitude to favor accuracy. Also, perform multiplication and division before addition and subtraction."},{"location":"mistakes/#not-understanding-slice-length-and-capacity-20","title":"Not understanding slice length and capacity (#20)","text":"Understanding the difference between slice length and capacity should be part of a Go developer\u2019s 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."},{"location":"mistakes/#inefficient-slice-initialization-21","title":"Inefficient slice initialization (#21)","text":"When creating a slice, initialize it with a given length or capacity if its length is already known. This reduces the number of allocations and improves performance. The same logic goes for maps, and you need to initialize their size."},{"location":"mistakes/#being-confused-about-nil-vs-empty-slice-22","title":"Being confused about nil vs. empty slice (#22)","text":"To prevent common confusions such as when using the encoding/json or the reflect package, you need to understand the difference between nil and empty slices. Both are zero-length, zero-capacity slices, but only a nil slice doesn\u2019t require allocation."},{"location":"mistakes/#not-properly-checking-if-a-slice-is-empty-23","title":"Not properly checking if a slice is empty (#23)","text":"To check if a slice doesn\u2019t contain any element, check its length. This check works regardless of whether the slice is nil or empty. The same goes for maps. To design unambiguous APIs, you shouldn\u2019t distinguish between nil and empty slices."},{"location":"mistakes/#not-making-slice-copies-correctly-24","title":"Not making slice copies correctly (#24)","text":"To copy one slice to another using the copy built-in function, remember that the number of copied elements corresponds to the minimum between the two slice\u2019s lengths."},{"location":"mistakes/#unexpected-side-effects-using-slice-append-25","title":"Unexpected side effects using slice append (#25)","text":"Using copy or the full slice expression is a way to prevent append from creating conflicts if two different functions use slices backed by the same array. However, only a slice copy prevents memory leaks if you want to shrink a large slice."},{"location":"mistakes/#slice-and-memory-leaks-26","title":"Slice and memory leaks (#26)","text":"Working with a slice of pointers or structs with pointer fields, you can avoid memory leaks by marking as nil the elements excluded by a slicing operation."},{"location":"mistakes/#inefficient-map-initialization-27","title":"Inefficient map initialization (#27)","text":"See #21."},{"location":"mistakes/#map-and-memory-leaks-28","title":"Map and memory leaks (#28)","text":"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."},{"location":"mistakes/#comparing-values-incorrectly-29","title":"Comparing values incorrectly (#29)","text":"To compare types in Go, you can use the == and != operators if two types are comparable: Booleans, numerals, strings, pointers, channels, and structs are composed entirely of comparable types. Otherwise, you can either use reflect.DeepEqual and pay the price of reflection or use custom implementations and libraries."},{"location":"mistakes/#control-structures","title":"Control Structures","text":""},{"location":"mistakes/#ignoring-that-elements-are-copied-in-range-loops-30","title":"Ignoring that elements are copied in range loops (#30)","text":"The value element in a range loop is a copy. Therefore, to mutate a struct, for example, access it via its index or via a classic for loop (unless the element or the field you want to modify is a pointer)."},{"location":"mistakes/#ignoring-how-arguments-are-evaluated-in-range-loops-channels-and-arrays-31","title":"Ignoring how arguments are evaluated in range loops (channels and arrays) (#31)","text":"Understanding that the expression passed to the range operator is evaluated only once before the beginning of the loop can help you avoid common mistakes such as inefficient assignment in channel or slice iteration."},{"location":"mistakes/#ignoring-the-impacts-of-using-pointer-elements-in-range-loops-32","title":"Ignoring the impacts of using pointer elements in range loops (#32)","text":"Using a local variable or accessing an element using an index, you can prevent mistakes while copying pointers inside a loop."},{"location":"mistakes/#making-wrong-assumptions-during-map-iterations-ordering-and-map-insert-during-iteration-33","title":"Making wrong assumptions during map iterations (ordering and map insert during iteration) (#33)","text":"To ensure predictable outputs when using maps, remember that a map data structure: * Doesn\u2019t order the data by keys * Doesn\u2019t preserve the insertion order * Doesn\u2019t have a deterministic iteration order * Doesn\u2019t guarantee that an element added during an iteration will be produced during this iteration"},{"location":"mistakes/#ignoring-how-the-break-statement-works-34","title":"Ignoring how the break statement works (#34)","text":"Using break or continue with a label enforces breaking a specific statement. This can be helpful with switch or select statements inside loops."},{"location":"mistakes/#using-defer-inside-a-loop-35","title":"Using defer inside a loop (#35)","text":"Extracting loop logic inside a function leads to executing a defer statement at the end of each iteration."},{"location":"mistakes/#strings","title":"Strings","text":""},{"location":"mistakes/#not-understanding-the-concept-of-rune-36","title":"Not understanding the concept of rune (#36)","text":"Understanding that a rune corresponds to the concept of a Unicode code point and that it can be composed of multiple bytes should be part of the Go developer\u2019s core knowledge to work accurately with strings."},{"location":"mistakes/#inaccurate-string-iteration-37","title":"Inaccurate string iteration (#37)","text":"Iterating on a string with the range operator iterates on the runes with the index corresponding to the starting index of the rune\u2019s byte sequence. To access a specific rune index (such as the third rune), convert the string into a []rune."},{"location":"mistakes/#misusing-trim-functions-38","title":"Misusing trim functions (#38)","text":"strings.TrimRight/strings.TrimLeft removes all the trailing/leading runes contained in a given set, whereas strings.TrimSuffix/strings.TrimPrefix returns a string without a provided suffix/prefix."},{"location":"mistakes/#under-optimized-strings-concatenation-39","title":"Under-optimized strings concatenation (#39)","text":"Concatenating a list of strings should be done with strings.Builder to prevent allocating a new string during each iteration."},{"location":"mistakes/#useless-string-conversions-40","title":"Useless string conversions (#40)","text":"Remembering that the bytes package offers the same operations as the strings package can help avoid extra byte/string conversions."},{"location":"mistakes/#substring-and-memory-leaks-41","title":"Substring and memory leaks (#41)","text":"Using copies instead of substrings can prevent memory leaks, as the string returned by a substring operation will be backed by the same byte array."},{"location":"mistakes/#functions-and-methods","title":"Functions and Methods","text":""},{"location":"mistakes/#not-knowing-which-type-of-receiver-to-use-42","title":"Not knowing which type of receiver to use (#42)","text":"The decision whether to use a value or a pointer receiver should be made based on factors such as the type, whether it has to be mutated, whether it contains a field that can\u2019t be copied, and how large the object is. When in doubt, use a pointer receiver."},{"location":"mistakes/#never-using-named-result-parameters-43","title":"Never using named result parameters (#43)","text":"Using named result parameters can be an efficient way to improve the readability of a function/method, especially if multiple result parameters have the same type. In some cases, this approach can also be convenient because named result parameters are initialized to their zero value. But be cautious about potential side effects."},{"location":"mistakes/#unintended-side-effects-with-named-result-parameters-44","title":"Unintended side effects with named result parameters (#44)","text":"See #43."},{"location":"mistakes/#returning-a-nil-receiver-45","title":"Returning a nil receiver (#45)","text":"When returning an interface, be cautious about returning not a nil pointer but an explicit nil value. Otherwise, unintended consequences may result because the caller will receive a non-nil value."},{"location":"mistakes/#using-a-filename-as-a-function-input-46","title":"Using a filename as a function input (#46)","text":"Designing functions to receive io.Reader types instead of filenames improves the reusability of a function and makes testing easier."},{"location":"mistakes/#ignoring-how-defer-arguments-and-receivers-are-evaluated-argument-evaluation-pointer-and-value-receivers-47","title":"Ignoring how defer arguments and receivers are evaluated (argument evaluation, pointer, and value receivers) (#47)","text":"Passing a pointer to a defer function and wrapping a call inside a closure are two possible solutions to overcome the immediate evaluation of arguments and receivers."},{"location":"mistakes/#error-management","title":"Error Management","text":""},{"location":"mistakes/#panicking-48","title":"Panicking (#48)","text":"Using panic is an option to deal with errors in Go. However, it should only be used sparingly in unrecoverable conditions: for example, to signal a programmer error or when you fail to load a mandatory dependency."},{"location":"mistakes/#ignoring-when-to-wrap-an-error-49","title":"Ignoring when to wrap an error (#49)","text":"Wrapping an error allows you to mark an error and/or provide additional context. However, error wrapping creates potential coupling as it makes the source error available for the caller. If you want to prevent that, don\u2019t use error wrapping."},{"location":"mistakes/#comparing-an-error-type-inaccurately-50","title":"Comparing an error type inaccurately (#50)","text":"If you use Go 1.13 error wrapping with the %w directive and fmt.Errorf, comparing an error against a type or a value has to be done using errors.As or errors.Is, respectively. Otherwise, if the returned error you want to check is wrapped, it will fail the checks."},{"location":"mistakes/#comparing-an-error-value-inaccurately-51","title":"Comparing an error value inaccurately (#51)","text":"See #50. To convey an expected error, use error sentinels (error values). An unexpected error should be a specific error type."},{"location":"mistakes/#handling-an-error-twice-52","title":"Handling an error twice (#52)","text":"In most situations, an error should be handled only once. Logging an error is handling an error. Therefore, you have to choose between logging or returning an error. In many cases, error wrapping is the solution as it allows you to provide additional context to an error and return the source error."},{"location":"mistakes/#not-handling-an-error-53","title":"Not handling an error (#53)","text":"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."},{"location":"mistakes/#not-handling-defer-errors-54","title":"Not handling defer errors (#54)","text":"In many cases, you shouldn\u2019t ignore an error returned by a defer function. Either handle it directly or propagate it to the caller, depending on the context. If you want to ignore it, use the blank identifier."},{"location":"mistakes/#concurrency-foundations","title":"Concurrency: Foundations","text":""},{"location":"mistakes/#mixing-up-concurrency-and-parallelism-55","title":"Mixing up concurrency and parallelism (#55)","text":"Understanding the fundamental differences between concurrency and parallelism is a cornerstone of the Go developer\u2019s knowledge. Concurrency is about structure, whereas parallelism is about execution."},{"location":"mistakes/#thinking-concurrency-is-always-faster-56","title":"Thinking concurrency is always faster (#56)","text":"To be a proficient developer, you must acknowledge that concurrency isn\u2019t 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."},{"location":"mistakes/#being-puzzled-about-when-to-use-channels-or-mutexes-57","title":"Being puzzled about when to use channels or mutexes (#57)","text":"Being aware of goroutine interactions can also be helpful when deciding between channels and mutexes. In general, parallel goroutines require synchronization and hence mutexes. Conversely, concurrent goroutines generally require coordination and orchestration and hence channels."},{"location":"mistakes/#not-understanding-race-problems-data-races-vs-race-conditions-and-the-go-memory-model-58","title":"Not understanding race problems (data races vs. race conditions and the Go memory model) (#58)","text":"Being proficient in concurrency also means understanding that data races and race conditions are different concepts. Data races occur when multiple goroutines simultaneously access the same memory location and at least one of them is writing. Meanwhile, being data-race-free doesn\u2019t necessarily mean deterministic execution. When a behavior depends on the sequence or the timing of events that can\u2019t be controlled, this is a race condition. Understanding the Go memory model and the underlying guarantees in terms of ordering and synchronization is essential to prevent possible data races and/or race conditions."},{"location":"mistakes/#not-understanding-the-concurrency-impacts-of-a-workload-type-59","title":"Not understanding the concurrency impacts of a workload type (#59)","text":"When creating a certain number of goroutines, consider the workload type. Creating CPU-bound goroutines means bounding this number close to the GOMAXPROCS variable (based by default on the number of CPU cores on the host). Creating I/O-bound goroutines depends on other factors, such as the external system."},{"location":"mistakes/#misunderstanding-go-contexts-60","title":"Misunderstanding Go contexts (#60)","text":"Go contexts are also one of the cornerstones of concurrency in Go. A context allows you to carry a deadline, a cancellation signal, and/or a list of keys-values."},{"location":"mistakes/#concurrency-practice","title":"Concurrency: Practice","text":""},{"location":"mistakes/#propagating-an-inappropriate-context-61","title":"Propagating an inappropriate context (#61)","text":"Understanding the conditions when a context can be canceled should matter when propagating it: for example, an HTTP handler canceling the context when the response has been sent."},{"location":"mistakes/#starting-a-goroutine-without-knowing-when-to-stop-it-62","title":"Starting a goroutine without knowing when to stop it (#62)","text":"Avoiding leaks means being mindful that whenever a goroutine is started, you should have a plan to stop it eventually."},{"location":"mistakes/#not-being-careful-with-goroutines-and-loop-variables-63","title":"Not being careful with goroutines and loop variables (#63)","text":"To avoid bugs with goroutines and loop variables, create local variables or call functions instead of closures."},{"location":"mistakes/#expecting-a-deterministic-behavior-using-select-and-channels-64","title":"Expecting a deterministic behavior using select and channels (#64)","text":"Understanding that select with multiple channels chooses the case randomly if multiple options are possible prevents making wrong assumptions that can lead to subtle concurrency bugs."},{"location":"mistakes/#not-using-notification-channels-65","title":"Not using notification channels (#65)","text":"Send notifications using a chan struct{} type."},{"location":"mistakes/#not-using-nil-channels-66","title":"Not using nil channels (#66)","text":"Using nil channels should be part of your concurrency toolset because it allows you to remove cases from select statements, for example."},{"location":"mistakes/#being-puzzled-about-channel-size-67","title":"Being puzzled about channel size (#67)","text":"Carefully decide on the right channel type to use, given a problem. Only unbuffered channels provide strong synchronization guarantees. You should have a good reason to specify a channel size other than one for buffered channels."},{"location":"mistakes/#forgetting-about-possible-side-effects-with-string-formatting-etcd-data-race-example-and-deadlock-68","title":"Forgetting about possible side effects with string formatting (etcd data race example and deadlock) (#68)","text":"Being aware that string formatting may lead to calling existing functions means watching out for possible deadlocks and other data races."},{"location":"mistakes/#creating-data-races-with-append-69","title":"Creating data races with append (#69)","text":"Calling append isn\u2019t always data-race-free; hence, it shouldn\u2019t be used concurrently on a shared slice."},{"location":"mistakes/#using-mutexes-inaccurately-with-slices-and-maps-70","title":"Using mutexes inaccurately with slices and maps (#70)","text":"Remembering that slices and maps are pointers can prevent common data races."},{"location":"mistakes/#misusing-syncwaitgroup-71","title":"Misusing sync.WaitGroup (#71)","text":"To accurately use sync.WaitGroup, call the Add method before spinning up goroutines."},{"location":"mistakes/#forgetting-about-synccond-72","title":"Forgetting about sync.Cond (#72)","text":"You can send repeated notifications to multiple goroutines with sync.Cond."},{"location":"mistakes/#not-using-errgroup-73","title":"Not using errgroup (#73)","text":"You can synchronize a group of goroutines and handle errors and contexts with the errgroup package."},{"location":"mistakes/#copying-a-sync-type-74","title":"Copying a sync type (#74)","text":"sync types shouldn\u2019t be copied."},{"location":"mistakes/#standard-library","title":"Standard Library","text":""},{"location":"mistakes/#providing-a-wrong-time-duration-75","title":"Providing a wrong time duration (#75)","text":"Remain cautious with functions accepting a time.Duration. Even though passing an integer is allowed, strive to use the time API to prevent any possible confusion."},{"location":"mistakes/#timeafter-and-memory-leaks-76","title":"time.After and memory leaks (#76)","text":"Avoiding calls to time.After in repeated functions (such as loops or HTTP handlers) can avoid peak memory consumption. The resources created by time.After are released only when the timer expires."},{"location":"mistakes/#json-handling-common-mistakes-77","title":"JSON handling common mistakes (#77)","text":" Unexpected behavior because of type embedding 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. 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. 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."},{"location":"mistakes/#common-sql-mistakes-78","title":"Common SQL mistakes (#78)","text":" Forgetting that sql.Open doesn't necessarily establish connections to a database Call the Ping or PingContext method if you need to test your configuration and make sure a database is reachable. Forgetting about connections pooling Configure the database connection parameters for production-grade applications. Not using prepared statements Using SQL prepared statements makes queries more efficient and more secure. Mishandling null values Deal with nullable columns in tables using pointers or sql.NullXXX types. Not handling rows iteration errors Call the Err method of sql.Rows after row iterations to ensure that you haven\u2019t missed an error while preparing the next row."},{"location":"mistakes/#not-closing-transient-resources-http-body-sqlrows-and-osfile-79","title":"Not closing transient resources (HTTP body, sql.Rows, and os.File) (#79)","text":"Eventually close all structs implementing io.Closer to avoid possible leaks."},{"location":"mistakes/#forgetting-the-return-statement-after-replying-to-an-http-request-80","title":"Forgetting the return statement after replying to an HTTP request (#80)","text":"To avoid unexpected behaviors in HTTP handler implementations, make sure you don\u2019t miss the return statement if you want a handler to stop after http.Error."},{"location":"mistakes/#using-the-default-http-client-and-server-81","title":"Using the default HTTP client and server (#81)","text":"For production-grade applications, don\u2019t use the default HTTP client and server implementations. These implementations are missing timeouts and behaviors that should be mandatory in production."},{"location":"mistakes/#testing","title":"Testing","text":""},{"location":"mistakes/#not-categorizing-tests-build-tags-environment-variables-and-short-mode-82","title":"Not categorizing tests (build tags, environment variables, and short mode) (#82)","text":"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."},{"location":"mistakes/#not-enabling-the-race-flag-83","title":"Not enabling the race flag (#83)","text":"Enabling the -race flag is highly recommended when writing concurrent applications. Doing so allows you to catch potential data races that can lead to software bugs."},{"location":"mistakes/#not-using-test-execution-modes-parallel-and-shuffle-84","title":"Not using test execution modes (parallel and shuffle) (#84)","text":"Using the -parallel flag is an efficient way to speed up tests, especially long-running ones. Use the -shuffle flag to help ensure that a test suite doesn\u2019t rely on wrong assumptions that could hide bugs."},{"location":"mistakes/#not-using-table-driven-tests-85","title":"Not using table-driven tests (#85)","text":"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."},{"location":"mistakes/#sleeping-in-unit-tests-86","title":"Sleeping in unit tests (#86)","text":"Avoid sleeps using synchronization to make a test less flaky and more robust. If synchronization isn\u2019t possible, consider a retry approach."},{"location":"mistakes/#not-dealing-with-the-time-api-efficiently-87","title":"Not dealing with the time API efficiently (#87)","text":"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."},{"location":"mistakes/#not-using-testing-utility-packages-httptest-and-iotest-88","title":"Not using testing utility packages (httptest and iotest) (#88)","text":"The httptest package is helpful for dealing with HTTP applications. It provides a set of utilities to test both clients and servers. The iotest package helps write io.Reader and test that an application is tolerant to errors."},{"location":"mistakes/#writing-inaccurate-benchmarks-89","title":"Writing inaccurate benchmarks (#89)","text":" Not resetting or pausing the timer Use time methods to preserve the accuracy of a benchmark. Making wrong assumptions about micro-benchmarks Increasing benchtime or using tools such as benchstat can be helpful when dealing with micro-benchmarks. Be careful with the results of a micro-benchmark if the system that ends up running the application is different from the one running the micro-benchmark. Not being careful about compiler optimizations Make sure the function under test leads to a side effect, to prevent compiler optimizations from fooling you about the benchmark results. Being fooled by the observer effect To prevent the observer effect, force a benchmark to re-create the data used by a CPU-bound function."},{"location":"mistakes/#not-exploring-all-the-go-testing-features-90","title":"Not exploring all the Go testing features (#90)","text":" Code coverage Use code coverage with the -coverprofile flag to quickly see which part of the code needs more attention. Testing from a different package Place unit tests in a different package to enforce writing tests that focus on an exposed behavior, not internals. Utility functions Handling errors using the *testing.T variable instead of the classic if err != nil makes code shorter and easier to read. Setup and teardown You can use setup and teardown functions to configure a complex environment, such as in the case of integration tests."},{"location":"mistakes/#not-using-fuzzing-community-mistake","title":"Not using fuzzing (community mistake)","text":"Fuzzing is an efficient strategy to detect random, unexpected, or malformed inputs to complex functions and methods in order to discover vulnerabilities, bugs, or even potential crashes. Credits: @jeromedoucet"},{"location":"mistakes/#optimizations","title":"Optimizations","text":""},{"location":"mistakes/#not-understanding-cpu-caches-91","title":"Not understanding CPU caches (#91)","text":" CPU architecture Understanding how to use CPU caches is important for optimizing CPU-bound applications because the L1 cache is about 50 to 100 times faster than the main memory. Cache line Being conscious of the cache line concept is critical to understanding how to organize data in data-intensive applications. A CPU doesn\u2019t 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. Slice of structs vs. struct of slices 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) isn\u2019t predictable. Cache placement policy To avoid a critical stride, hence utilizing only a tiny portion of the cache, be aware that caches are partitioned."},{"location":"mistakes/#writing-concurrent-code-that-leads-to-false-sharing-92","title":"Writing concurrent code that leads to false sharing (#92)","text":"Knowing that lower levels of CPU caches aren\u2019t shared across all the cores helps avoid performance-degrading patterns such as false sharing while writing concurrency code. Sharing memory is an illusion."},{"location":"mistakes/#not-taking-into-account-instruction-level-parallelism-93","title":"Not taking into account instruction-level parallelism (#93)","text":"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."},{"location":"mistakes/#not-being-aware-of-data-alignment-94","title":"Not being aware of data alignment (#94)","text":"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)."},{"location":"mistakes/#not-understanding-stack-vs-heap-95","title":"Not understanding stack vs. heap (#95)","text":"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."},{"location":"mistakes/#not-knowing-how-to-reduce-allocations-api-change-compiler-optimizations-and-syncpool-96","title":"Not knowing how to reduce allocations (API change, compiler optimizations, and sync.Pool) (#96)","text":"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."},{"location":"mistakes/#not-relying-on-inlining-97","title":"Not relying on inlining (#97)","text":"Use the fast-path inlining technique to efficiently reduce the amortized time to call a function."},{"location":"mistakes/#not-using-go-diagnostics-tooling-98","title":"Not using Go diagnostics tooling (#98)","text":"Rely on profiling and the execution tracer to understand how an application performs and the parts to optimize."},{"location":"mistakes/#not-understanding-how-the-gc-works-99","title":"Not understanding how the GC works (#99)","text":"Understanding how to tune the GC can lead to multiple benefits such as handling sudden load increases more efficiently."},{"location":"mistakes/#not-understanding-the-impacts-of-running-go-in-docker-and-kubernetes-100","title":"Not understanding the impacts of running Go in Docker and Kubernetes (#100)","text":"To help avoid CPU throttling when deployed in Docker and Kubernetes, keep in mind that Go isn\u2019t CFS-aware."},{"location":"chinese/presentation/","title":"Presentation","text":"TODO"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"\ud83d\udcd6 Book Presentation","text":"Source code and community space of \ud83d\udcd6 100 Go Mistakes and How to Avoid Them, published by Manning in August 2022."},{"location":"#book-description","title":"Book Description","text":"If you're a Go developer looking to improve your skills, this book is for you. With a focus on practical examples, 100 Go Mistakes and How to Avoid Them covers a wide range of topics from concurrency and error handling to testing and code organization. You'll learn to write more idiomatic, efficient, and maintainable code and become a proficient Go developer. "},{"location":"#quotes","title":"Quotes","text":"This should be the required reading for all Golang developers before they touch code in Production... It's the Golang equivalent of the legendary 'Effective Java' by Joshua Bloch. \u2013 Neeraj Shah Not having this will be the 101st mistake a Go programmer could make. \u2013 Anupam Sengupta"},{"location":"#where-to-buy","title":"Where to Buy?","text":"100 Go Mistakes and How to Avoid Them (\ud83c\uddec\ud83c\udde7 edition: paper, digital, or audiobook): Manning O\u2019Reilly Amazon: .com, .co.uk, .de, .fr, .in, .co.jp, .es, .it, .com.br Go\u8a00\u8a9e100Tips \u958b\u767a\u8005\u306b\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u3078\u306e\u5bfe\u51e6\u6cd5 (\ud83c\uddef\ud83c\uddf5 edition): Amazon: .co.jp "},{"location":"#about-the-author","title":"About the Author","text":"Teiva Harsanyi is a senior software engineer at Google. He has worked in various domains, including insurance, transportation, and safety-critical industries like air traffic management. He is passionate about Go and how to design and implement reliable systems."},{"location":"#external-resources","title":"External Resources","text":""},{"location":"#english","title":"English","text":" Book Review: 100 Go Mistakes and How to Avoid Them: Post, YouTube How to make mistakes in Go - Go Time #190: Episode, Spotify 8LU - 100% Test Coverage Some Tips I learned from 100 Mistakes in Go What can be summarized from 100 Go Mistakes? "},{"location":"#chinese","title":"Chinese","text":" \u6df1\u5ea6\u9605\u8bfb\u4e4b\u300a100 Go Mistakes and How to Avoid Them 100 Go Mistakes \u968f\u8bb0 \u6211\u4e3a\u4ec0\u4e48\u653e\u5f03Go\u8bed\u8a00\uff1f "},{"location":"#japanese","title":"Japanese","text":" \u6700\u8fd1\u8aad\u3093\u3060Go\u8a00\u8a9e\u306e\u672c\u306e\u7d39\u4ecb\uff1a100 Go Mistakes and How to Avoid Them \u300e100 Go Mistakes and How to Avoid Them\u300f\u3092\u8aad\u3080 100 Go Mistakes \u968f\u8bb0 - 01 Code and project organization "},{"location":"#portuguese","title":"Portuguese","text":" Um \u00d3TIMO livro para programadores Go "},{"location":"jobs/","title":"\u2764\ufe0f Go Jobs","text":"Is your company hiring? Sponsor this repository and let Go developers know your opportunities in this section (traffic: +400 unique visitor per week). For example:"},{"location":"jobs/#company-remote-usa-96000-to-120000-a-year","title":"[Company] Remote (USA) - $96,000 to $120,000 a year","text":""},{"location":"jobs/#job-description","title":"Job Description","text":"..."},{"location":"jobs/#qualifications","title":"Qualifications","text":"..."},{"location":"mistakes/","title":"\ud83d\udca1 Go Mistakes","text":"This section contains a summary of the 100 mistakes in the book. Meanwhile, it's also a section open to the community. If you believe that a mistake should be added, please create a community mistake issue. "},{"location":"mistakes/#code-and-project-organization","title":"Code and Project Organization","text":""},{"location":"mistakes/#unintended-variable-shadowing-1","title":"Unintended variable shadowing (#1)","text":"Avoiding shadowed variables can help prevent mistakes like referencing the wrong variable or confusing readers."},{"location":"mistakes/#unnecessary-nested-code-2","title":"Unnecessary nested code (#2)","text":"Avoiding nested levels and keeping the happy path aligned on the left makes building a mental code model easier."},{"location":"mistakes/#misusing-init-functions-3","title":"Misusing init functions (#3)","text":"When initializing variables, remember that init functions have limited error handling and make state handling and testing more complex. In most cases, initializations should be handled as specific functions."},{"location":"mistakes/#overusing-getters-and-setters-4","title":"Overusing getters and setters (#4)","text":"Forcing the use of getters and setters isn\u2019t idiomatic in Go. Being pragmatic and finding the right balance between efficiency and blindly following certain idioms should be the way to go."},{"location":"mistakes/#interface-pollution-5","title":"Interface pollution (#5)","text":"Abstractions should be discovered, not created. To prevent unnecessary complexity, create an interface when you need it and not when you foresee needing it, or if you can at least prove the abstraction to be a valid one."},{"location":"mistakes/#interface-on-the-producer-side-6","title":"Interface on the producer side (#6)","text":"Keeping interfaces on the client side avoids unnecessary abstractions."},{"location":"mistakes/#returning-interfaces-7","title":"Returning interfaces (#7)","text":"To prevent being restricted in terms of flexibility, a function shouldn\u2019t return interfaces but concrete implementations in most cases. Conversely, a function should accept interfaces whenever possible."},{"location":"mistakes/#any-says-nothing-8","title":"any says nothing (#8)","text":"Only use any if you need to accept or return any possible type, such as json.Marshal. Otherwise, any doesn\u2019t provide meaningful information and can lead to compile-time issues by allowing a caller to call methods with any data type."},{"location":"mistakes/#being-confused-about-when-to-use-generics-9","title":"Being confused about when to use generics (#9)","text":"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."},{"location":"mistakes/#not-being-aware-of-the-possible-problems-with-type-embedding-10","title":"Not being aware of the possible problems with type embedding (#10)","text":"Using type embedding can also help avoid boilerplate code; however, ensure that doing so doesn\u2019t lead to visibility issues where some fields should have remained hidden."},{"location":"mistakes/#not-using-the-functional-options-pattern-11","title":"Not using the functional options pattern (#11)","text":"To handle options conveniently and in an API-friendly manner, use the functional options pattern."},{"location":"mistakes/#project-misorganization-project-structure-and-package-organization-12","title":"Project misorganization (project structure and package organization) (#12)","text":"Following a layout such as project-layout can be a good way to start structuring Go projects, especially if you are looking for existing conventions to standardize a new project."},{"location":"mistakes/#creating-utility-packages-13","title":"Creating utility packages (#13)","text":"Naming is a critical piece of application design. Creating packages such as common, util, and shared doesn\u2019t bring much value for the reader. Refactor such packages into meaningful and specific package names."},{"location":"mistakes/#ignoring-package-name-collisions-14","title":"Ignoring package name collisions (#14)","text":"To avoid naming collisions between variables and packages, leading to confusion or perhaps even bugs, use unique names for each one. If this isn\u2019t feasible, use an import alias to change the qualifier to differentiate the package name from the variable name, or think of a better name."},{"location":"mistakes/#missing-code-documentation-15","title":"Missing code documentation (#15)","text":"To help clients and maintainers understand your code\u2019s purpose, document exported elements."},{"location":"mistakes/#not-using-linters-16","title":"Not using linters (#16)","text":"To improve code quality and consistency, use linters and formatters."},{"location":"mistakes/#data-types","title":"Data Types","text":""},{"location":"mistakes/#creating-confusion-with-octal-literals-17","title":"Creating confusion with octal literals (#17)","text":"When reading existing code, bear in mind that integer literals starting with 0 are octal numbers. Also, to improve readability, make octal integers explicit by prefixing them with 0o."},{"location":"mistakes/#neglecting-integer-overflows-18","title":"Neglecting integer overflows (#18)","text":"Because integer overflows and underflows are handled silently in Go, you can implement your own functions to catch them."},{"location":"mistakes/#not-understanding-floating-points-19","title":"Not understanding floating-points (#19)","text":"Making floating-point comparisons within a given delta can ensure that your code is portable. When performing addition or subtraction, group the operations with a similar order of magnitude to favor accuracy. Also, perform multiplication and division before addition and subtraction."},{"location":"mistakes/#not-understanding-slice-length-and-capacity-20","title":"Not understanding slice length and capacity (#20)","text":"Understanding the difference between slice length and capacity should be part of a Go developer\u2019s 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."},{"location":"mistakes/#inefficient-slice-initialization-21","title":"Inefficient slice initialization (#21)","text":"When creating a slice, initialize it with a given length or capacity if its length is already known. This reduces the number of allocations and improves performance. The same logic goes for maps, and you need to initialize their size."},{"location":"mistakes/#being-confused-about-nil-vs-empty-slice-22","title":"Being confused about nil vs. empty slice (#22)","text":"To prevent common confusions such as when using the encoding/json or the reflect package, you need to understand the difference between nil and empty slices. Both are zero-length, zero-capacity slices, but only a nil slice doesn\u2019t require allocation."},{"location":"mistakes/#not-properly-checking-if-a-slice-is-empty-23","title":"Not properly checking if a slice is empty (#23)","text":"To check if a slice doesn\u2019t contain any element, check its length. This check works regardless of whether the slice is nil or empty. The same goes for maps. To design unambiguous APIs, you shouldn\u2019t distinguish between nil and empty slices."},{"location":"mistakes/#not-making-slice-copies-correctly-24","title":"Not making slice copies correctly (#24)","text":"To copy one slice to another using the copy built-in function, remember that the number of copied elements corresponds to the minimum between the two slice\u2019s lengths."},{"location":"mistakes/#unexpected-side-effects-using-slice-append-25","title":"Unexpected side effects using slice append (#25)","text":"Using copy or the full slice expression is a way to prevent append from creating conflicts if two different functions use slices backed by the same array. However, only a slice copy prevents memory leaks if you want to shrink a large slice."},{"location":"mistakes/#slice-and-memory-leaks-26","title":"Slice and memory leaks (#26)","text":"Working with a slice of pointers or structs with pointer fields, you can avoid memory leaks by marking as nil the elements excluded by a slicing operation."},{"location":"mistakes/#inefficient-map-initialization-27","title":"Inefficient map initialization (#27)","text":"See #21."},{"location":"mistakes/#map-and-memory-leaks-28","title":"Map and memory leaks (#28)","text":"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."},{"location":"mistakes/#comparing-values-incorrectly-29","title":"Comparing values incorrectly (#29)","text":"To compare types in Go, you can use the == and != operators if two types are comparable: Booleans, numerals, strings, pointers, channels, and structs are composed entirely of comparable types. Otherwise, you can either use reflect.DeepEqual and pay the price of reflection or use custom implementations and libraries."},{"location":"mistakes/#control-structures","title":"Control Structures","text":""},{"location":"mistakes/#ignoring-that-elements-are-copied-in-range-loops-30","title":"Ignoring that elements are copied in range loops (#30)","text":"The value element in a range loop is a copy. Therefore, to mutate a struct, for example, access it via its index or via a classic for loop (unless the element or the field you want to modify is a pointer)."},{"location":"mistakes/#ignoring-how-arguments-are-evaluated-in-range-loops-channels-and-arrays-31","title":"Ignoring how arguments are evaluated in range loops (channels and arrays) (#31)","text":"Understanding that the expression passed to the range operator is evaluated only once before the beginning of the loop can help you avoid common mistakes such as inefficient assignment in channel or slice iteration."},{"location":"mistakes/#ignoring-the-impacts-of-using-pointer-elements-in-range-loops-32","title":"Ignoring the impacts of using pointer elements in range loops (#32)","text":"Using a local variable or accessing an element using an index, you can prevent mistakes while copying pointers inside a loop."},{"location":"mistakes/#making-wrong-assumptions-during-map-iterations-ordering-and-map-insert-during-iteration-33","title":"Making wrong assumptions during map iterations (ordering and map insert during iteration) (#33)","text":"To ensure predictable outputs when using maps, remember that a map data structure: * Doesn\u2019t order the data by keys * Doesn\u2019t preserve the insertion order * Doesn\u2019t have a deterministic iteration order * Doesn\u2019t guarantee that an element added during an iteration will be produced during this iteration"},{"location":"mistakes/#ignoring-how-the-break-statement-works-34","title":"Ignoring how the break statement works (#34)","text":"Using break or continue with a label enforces breaking a specific statement. This can be helpful with switch or select statements inside loops."},{"location":"mistakes/#using-defer-inside-a-loop-35","title":"Using defer inside a loop (#35)","text":"Extracting loop logic inside a function leads to executing a defer statement at the end of each iteration."},{"location":"mistakes/#strings","title":"Strings","text":""},{"location":"mistakes/#not-understanding-the-concept-of-rune-36","title":"Not understanding the concept of rune (#36)","text":"Understanding that a rune corresponds to the concept of a Unicode code point and that it can be composed of multiple bytes should be part of the Go developer\u2019s core knowledge to work accurately with strings."},{"location":"mistakes/#inaccurate-string-iteration-37","title":"Inaccurate string iteration (#37)","text":"Iterating on a string with the range operator iterates on the runes with the index corresponding to the starting index of the rune\u2019s byte sequence. To access a specific rune index (such as the third rune), convert the string into a []rune."},{"location":"mistakes/#misusing-trim-functions-38","title":"Misusing trim functions (#38)","text":"strings.TrimRight/strings.TrimLeft removes all the trailing/leading runes contained in a given set, whereas strings.TrimSuffix/strings.TrimPrefix returns a string without a provided suffix/prefix."},{"location":"mistakes/#under-optimized-strings-concatenation-39","title":"Under-optimized strings concatenation (#39)","text":"Concatenating a list of strings should be done with strings.Builder to prevent allocating a new string during each iteration."},{"location":"mistakes/#useless-string-conversions-40","title":"Useless string conversions (#40)","text":"Remembering that the bytes package offers the same operations as the strings package can help avoid extra byte/string conversions."},{"location":"mistakes/#substring-and-memory-leaks-41","title":"Substring and memory leaks (#41)","text":"Using copies instead of substrings can prevent memory leaks, as the string returned by a substring operation will be backed by the same byte array."},{"location":"mistakes/#functions-and-methods","title":"Functions and Methods","text":""},{"location":"mistakes/#not-knowing-which-type-of-receiver-to-use-42","title":"Not knowing which type of receiver to use (#42)","text":"The decision whether to use a value or a pointer receiver should be made based on factors such as the type, whether it has to be mutated, whether it contains a field that can\u2019t be copied, and how large the object is. When in doubt, use a pointer receiver."},{"location":"mistakes/#never-using-named-result-parameters-43","title":"Never using named result parameters (#43)","text":"Using named result parameters can be an efficient way to improve the readability of a function/method, especially if multiple result parameters have the same type. In some cases, this approach can also be convenient because named result parameters are initialized to their zero value. But be cautious about potential side effects."},{"location":"mistakes/#unintended-side-effects-with-named-result-parameters-44","title":"Unintended side effects with named result parameters (#44)","text":"See #43."},{"location":"mistakes/#returning-a-nil-receiver-45","title":"Returning a nil receiver (#45)","text":"When returning an interface, be cautious about returning not a nil pointer but an explicit nil value. Otherwise, unintended consequences may result because the caller will receive a non-nil value."},{"location":"mistakes/#using-a-filename-as-a-function-input-46","title":"Using a filename as a function input (#46)","text":"Designing functions to receive io.Reader types instead of filenames improves the reusability of a function and makes testing easier."},{"location":"mistakes/#ignoring-how-defer-arguments-and-receivers-are-evaluated-argument-evaluation-pointer-and-value-receivers-47","title":"Ignoring how defer arguments and receivers are evaluated (argument evaluation, pointer, and value receivers) (#47)","text":"Passing a pointer to a defer function and wrapping a call inside a closure are two possible solutions to overcome the immediate evaluation of arguments and receivers."},{"location":"mistakes/#error-management","title":"Error Management","text":""},{"location":"mistakes/#panicking-48","title":"Panicking (#48)","text":"Using panic is an option to deal with errors in Go. However, it should only be used sparingly in unrecoverable conditions: for example, to signal a programmer error or when you fail to load a mandatory dependency."},{"location":"mistakes/#ignoring-when-to-wrap-an-error-49","title":"Ignoring when to wrap an error (#49)","text":"Wrapping an error allows you to mark an error and/or provide additional context. However, error wrapping creates potential coupling as it makes the source error available for the caller. If you want to prevent that, don\u2019t use error wrapping."},{"location":"mistakes/#comparing-an-error-type-inaccurately-50","title":"Comparing an error type inaccurately (#50)","text":"If you use Go 1.13 error wrapping with the %w directive and fmt.Errorf, comparing an error against a type or a value has to be done using errors.As or errors.Is, respectively. Otherwise, if the returned error you want to check is wrapped, it will fail the checks."},{"location":"mistakes/#comparing-an-error-value-inaccurately-51","title":"Comparing an error value inaccurately (#51)","text":"See #50. To convey an expected error, use error sentinels (error values). An unexpected error should be a specific error type."},{"location":"mistakes/#handling-an-error-twice-52","title":"Handling an error twice (#52)","text":"In most situations, an error should be handled only once. Logging an error is handling an error. Therefore, you have to choose between logging or returning an error. In many cases, error wrapping is the solution as it allows you to provide additional context to an error and return the source error."},{"location":"mistakes/#not-handling-an-error-53","title":"Not handling an error (#53)","text":"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."},{"location":"mistakes/#not-handling-defer-errors-54","title":"Not handling defer errors (#54)","text":"In many cases, you shouldn\u2019t ignore an error returned by a defer function. Either handle it directly or propagate it to the caller, depending on the context. If you want to ignore it, use the blank identifier."},{"location":"mistakes/#concurrency-foundations","title":"Concurrency: Foundations","text":""},{"location":"mistakes/#mixing-up-concurrency-and-parallelism-55","title":"Mixing up concurrency and parallelism (#55)","text":"Understanding the fundamental differences between concurrency and parallelism is a cornerstone of the Go developer\u2019s knowledge. Concurrency is about structure, whereas parallelism is about execution."},{"location":"mistakes/#thinking-concurrency-is-always-faster-56","title":"Thinking concurrency is always faster (#56)","text":"To be a proficient developer, you must acknowledge that concurrency isn\u2019t 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."},{"location":"mistakes/#being-puzzled-about-when-to-use-channels-or-mutexes-57","title":"Being puzzled about when to use channels or mutexes (#57)","text":"Being aware of goroutine interactions can also be helpful when deciding between channels and mutexes. In general, parallel goroutines require synchronization and hence mutexes. Conversely, concurrent goroutines generally require coordination and orchestration and hence channels."},{"location":"mistakes/#not-understanding-race-problems-data-races-vs-race-conditions-and-the-go-memory-model-58","title":"Not understanding race problems (data races vs. race conditions and the Go memory model) (#58)","text":"Being proficient in concurrency also means understanding that data races and race conditions are different concepts. Data races occur when multiple goroutines simultaneously access the same memory location and at least one of them is writing. Meanwhile, being data-race-free doesn\u2019t necessarily mean deterministic execution. When a behavior depends on the sequence or the timing of events that can\u2019t be controlled, this is a race condition. Understanding the Go memory model and the underlying guarantees in terms of ordering and synchronization is essential to prevent possible data races and/or race conditions."},{"location":"mistakes/#not-understanding-the-concurrency-impacts-of-a-workload-type-59","title":"Not understanding the concurrency impacts of a workload type (#59)","text":"When creating a certain number of goroutines, consider the workload type. Creating CPU-bound goroutines means bounding this number close to the GOMAXPROCS variable (based by default on the number of CPU cores on the host). Creating I/O-bound goroutines depends on other factors, such as the external system."},{"location":"mistakes/#misunderstanding-go-contexts-60","title":"Misunderstanding Go contexts (#60)","text":"Go contexts are also one of the cornerstones of concurrency in Go. A context allows you to carry a deadline, a cancellation signal, and/or a list of keys-values."},{"location":"mistakes/#concurrency-practice","title":"Concurrency: Practice","text":""},{"location":"mistakes/#propagating-an-inappropriate-context-61","title":"Propagating an inappropriate context (#61)","text":"Understanding the conditions when a context can be canceled should matter when propagating it: for example, an HTTP handler canceling the context when the response has been sent."},{"location":"mistakes/#starting-a-goroutine-without-knowing-when-to-stop-it-62","title":"Starting a goroutine without knowing when to stop it (#62)","text":"Avoiding leaks means being mindful that whenever a goroutine is started, you should have a plan to stop it eventually."},{"location":"mistakes/#not-being-careful-with-goroutines-and-loop-variables-63","title":"Not being careful with goroutines and loop variables (#63)","text":"To avoid bugs with goroutines and loop variables, create local variables or call functions instead of closures."},{"location":"mistakes/#expecting-a-deterministic-behavior-using-select-and-channels-64","title":"Expecting a deterministic behavior using select and channels (#64)","text":"Understanding that select with multiple channels chooses the case randomly if multiple options are possible prevents making wrong assumptions that can lead to subtle concurrency bugs."},{"location":"mistakes/#not-using-notification-channels-65","title":"Not using notification channels (#65)","text":"Send notifications using a chan struct{} type."},{"location":"mistakes/#not-using-nil-channels-66","title":"Not using nil channels (#66)","text":"Using nil channels should be part of your concurrency toolset because it allows you to remove cases from select statements, for example."},{"location":"mistakes/#being-puzzled-about-channel-size-67","title":"Being puzzled about channel size (#67)","text":"Carefully decide on the right channel type to use, given a problem. Only unbuffered channels provide strong synchronization guarantees. You should have a good reason to specify a channel size other than one for buffered channels."},{"location":"mistakes/#forgetting-about-possible-side-effects-with-string-formatting-etcd-data-race-example-and-deadlock-68","title":"Forgetting about possible side effects with string formatting (etcd data race example and deadlock) (#68)","text":"Being aware that string formatting may lead to calling existing functions means watching out for possible deadlocks and other data races."},{"location":"mistakes/#creating-data-races-with-append-69","title":"Creating data races with append (#69)","text":"Calling append isn\u2019t always data-race-free; hence, it shouldn\u2019t be used concurrently on a shared slice."},{"location":"mistakes/#using-mutexes-inaccurately-with-slices-and-maps-70","title":"Using mutexes inaccurately with slices and maps (#70)","text":"Remembering that slices and maps are pointers can prevent common data races."},{"location":"mistakes/#misusing-syncwaitgroup-71","title":"Misusing sync.WaitGroup (#71)","text":"To accurately use sync.WaitGroup, call the Add method before spinning up goroutines."},{"location":"mistakes/#forgetting-about-synccond-72","title":"Forgetting about sync.Cond (#72)","text":"You can send repeated notifications to multiple goroutines with sync.Cond."},{"location":"mistakes/#not-using-errgroup-73","title":"Not using errgroup (#73)","text":"You can synchronize a group of goroutines and handle errors and contexts with the errgroup package."},{"location":"mistakes/#copying-a-sync-type-74","title":"Copying a sync type (#74)","text":"sync types shouldn\u2019t be copied."},{"location":"mistakes/#standard-library","title":"Standard Library","text":""},{"location":"mistakes/#providing-a-wrong-time-duration-75","title":"Providing a wrong time duration (#75)","text":"Remain cautious with functions accepting a time.Duration. Even though passing an integer is allowed, strive to use the time API to prevent any possible confusion."},{"location":"mistakes/#timeafter-and-memory-leaks-76","title":"time.After and memory leaks (#76)","text":"Avoiding calls to time.After in repeated functions (such as loops or HTTP handlers) can avoid peak memory consumption. The resources created by time.After are released only when the timer expires."},{"location":"mistakes/#json-handling-common-mistakes-77","title":"JSON handling common mistakes (#77)","text":" Unexpected behavior because of type embedding 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. 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. 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."},{"location":"mistakes/#common-sql-mistakes-78","title":"Common SQL mistakes (#78)","text":" Forgetting that sql.Open doesn't necessarily establish connections to a database Call the Ping or PingContext method if you need to test your configuration and make sure a database is reachable. Forgetting about connections pooling Configure the database connection parameters for production-grade applications. Not using prepared statements Using SQL prepared statements makes queries more efficient and more secure. Mishandling null values Deal with nullable columns in tables using pointers or sql.NullXXX types. Not handling rows iteration errors Call the Err method of sql.Rows after row iterations to ensure that you haven\u2019t missed an error while preparing the next row."},{"location":"mistakes/#not-closing-transient-resources-http-body-sqlrows-and-osfile-79","title":"Not closing transient resources (HTTP body, sql.Rows, and os.File) (#79)","text":"Eventually close all structs implementing io.Closer to avoid possible leaks."},{"location":"mistakes/#forgetting-the-return-statement-after-replying-to-an-http-request-80","title":"Forgetting the return statement after replying to an HTTP request (#80)","text":"To avoid unexpected behaviors in HTTP handler implementations, make sure you don\u2019t miss the return statement if you want a handler to stop after http.Error."},{"location":"mistakes/#using-the-default-http-client-and-server-81","title":"Using the default HTTP client and server (#81)","text":"For production-grade applications, don\u2019t use the default HTTP client and server implementations. These implementations are missing timeouts and behaviors that should be mandatory in production."},{"location":"mistakes/#testing","title":"Testing","text":""},{"location":"mistakes/#not-categorizing-tests-build-tags-environment-variables-and-short-mode-82","title":"Not categorizing tests (build tags, environment variables, and short mode) (#82)","text":"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."},{"location":"mistakes/#not-enabling-the-race-flag-83","title":"Not enabling the race flag (#83)","text":"Enabling the -race flag is highly recommended when writing concurrent applications. Doing so allows you to catch potential data races that can lead to software bugs."},{"location":"mistakes/#not-using-test-execution-modes-parallel-and-shuffle-84","title":"Not using test execution modes (parallel and shuffle) (#84)","text":"Using the -parallel flag is an efficient way to speed up tests, especially long-running ones. Use the -shuffle flag to help ensure that a test suite doesn\u2019t rely on wrong assumptions that could hide bugs."},{"location":"mistakes/#not-using-table-driven-tests-85","title":"Not using table-driven tests (#85)","text":"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."},{"location":"mistakes/#sleeping-in-unit-tests-86","title":"Sleeping in unit tests (#86)","text":"Avoid sleeps using synchronization to make a test less flaky and more robust. If synchronization isn\u2019t possible, consider a retry approach."},{"location":"mistakes/#not-dealing-with-the-time-api-efficiently-87","title":"Not dealing with the time API efficiently (#87)","text":"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."},{"location":"mistakes/#not-using-testing-utility-packages-httptest-and-iotest-88","title":"Not using testing utility packages (httptest and iotest) (#88)","text":"The httptest package is helpful for dealing with HTTP applications. It provides a set of utilities to test both clients and servers. The iotest package helps write io.Reader and test that an application is tolerant to errors."},{"location":"mistakes/#writing-inaccurate-benchmarks-89","title":"Writing inaccurate benchmarks (#89)","text":" Not resetting or pausing the timer Use time methods to preserve the accuracy of a benchmark. Making wrong assumptions about micro-benchmarks Increasing benchtime or using tools such as benchstat can be helpful when dealing with micro-benchmarks. Be careful with the results of a micro-benchmark if the system that ends up running the application is different from the one running the micro-benchmark. Not being careful about compiler optimizations Make sure the function under test leads to a side effect, to prevent compiler optimizations from fooling you about the benchmark results. Being fooled by the observer effect To prevent the observer effect, force a benchmark to re-create the data used by a CPU-bound function."},{"location":"mistakes/#not-exploring-all-the-go-testing-features-90","title":"Not exploring all the Go testing features (#90)","text":" Code coverage Use code coverage with the -coverprofile flag to quickly see which part of the code needs more attention. Testing from a different package Place unit tests in a different package to enforce writing tests that focus on an exposed behavior, not internals. Utility functions Handling errors using the *testing.T variable instead of the classic if err != nil makes code shorter and easier to read. Setup and teardown You can use setup and teardown functions to configure a complex environment, such as in the case of integration tests."},{"location":"mistakes/#not-using-fuzzing-community-mistake","title":"Not using fuzzing (community mistake)","text":"Fuzzing is an efficient strategy to detect random, unexpected, or malformed inputs to complex functions and methods in order to discover vulnerabilities, bugs, or even potential crashes. Credits: @jeromedoucet"},{"location":"mistakes/#optimizations","title":"Optimizations","text":""},{"location":"mistakes/#not-understanding-cpu-caches-91","title":"Not understanding CPU caches (#91)","text":" CPU architecture Understanding how to use CPU caches is important for optimizing CPU-bound applications because the L1 cache is about 50 to 100 times faster than the main memory. Cache line Being conscious of the cache line concept is critical to understanding how to organize data in data-intensive applications. A CPU doesn\u2019t 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. Slice of structs vs. struct of slices 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) isn\u2019t predictable. Cache placement policy To avoid a critical stride, hence utilizing only a tiny portion of the cache, be aware that caches are partitioned."},{"location":"mistakes/#writing-concurrent-code-that-leads-to-false-sharing-92","title":"Writing concurrent code that leads to false sharing (#92)","text":"Knowing that lower levels of CPU caches aren\u2019t shared across all the cores helps avoid performance-degrading patterns such as false sharing while writing concurrency code. Sharing memory is an illusion."},{"location":"mistakes/#not-taking-into-account-instruction-level-parallelism-93","title":"Not taking into account instruction-level parallelism (#93)","text":"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."},{"location":"mistakes/#not-being-aware-of-data-alignment-94","title":"Not being aware of data alignment (#94)","text":"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)."},{"location":"mistakes/#not-understanding-stack-vs-heap-95","title":"Not understanding stack vs. heap (#95)","text":"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."},{"location":"mistakes/#not-knowing-how-to-reduce-allocations-api-change-compiler-optimizations-and-syncpool-96","title":"Not knowing how to reduce allocations (API change, compiler optimizations, and sync.Pool) (#96)","text":"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."},{"location":"mistakes/#not-relying-on-inlining-97","title":"Not relying on inlining (#97)","text":"Use the fast-path inlining technique to efficiently reduce the amortized time to call a function."},{"location":"mistakes/#not-using-go-diagnostics-tooling-98","title":"Not using Go diagnostics tooling (#98)","text":"Rely on profiling and the execution tracer to understand how an application performs and the parts to optimize."},{"location":"mistakes/#not-understanding-how-the-gc-works-99","title":"Not understanding how the GC works (#99)","text":"Understanding how to tune the GC can lead to multiple benefits such as handling sudden load increases more efficiently."},{"location":"mistakes/#not-understanding-the-impacts-of-running-go-in-docker-and-kubernetes-100","title":"Not understanding the impacts of running Go in Docker and Kubernetes (#100)","text":"To help avoid CPU throttling when deployed in Docker and Kubernetes, keep in mind that Go isn\u2019t CFS-aware."},{"location":"chinese/presentation/","title":"Presentation","text":"TODO"}]} \ No newline at end of file diff --git a/site/sitemap.xml.gz b/site/sitemap.xml.gz index 63f34fe..e4eb7f9 100644 Binary files a/site/sitemap.xml.gz and b/site/sitemap.xml.gz differ
Is your company hiring? Sponsor this repository and let Go developers know your opportunities in this section (traffic: +400 unique visitor per week).
For example:
This section contains a summary of the 100 mistakes in the book. Meanwhile, it's also a section open to the community. If you believe that a mistake should be added, please create a community mistake issue.
To help clients and maintainers understand your code’s purpose, document exported elements.
To improve code quality and consistency, use linters and formatters.
When reading existing code, bear in mind that integer literals starting with 0 are octal numbers. Also, to improve readability, make octal integers explicit by prefixing them with 0o.
0o
Source code and community space of \ud83d\udcd6 100 Go Mistakes and How to Avoid Them, published by Manning in August 2022.
If you're a Go developer looking to improve your skills, this book is for you. With a focus on practical examples, 100 Go Mistakes and How to Avoid Them covers a wide range of topics from concurrency and error handling to testing and code organization. You'll learn to write more idiomatic, efficient, and maintainable code and become a proficient Go developer.
This should be the required reading for all Golang developers before they touch code in Production... It's the Golang equivalent of the legendary 'Effective Java' by Joshua Bloch.
\u2013 Neeraj Shah
Not having this will be the 101st mistake a Go programmer could make.
\u2013 Anupam Sengupta
100 Go Mistakes and How to Avoid Them (\ud83c\uddec\ud83c\udde7 edition: paper, digital, or audiobook):
Go\u8a00\u8a9e100Tips \u958b\u767a\u8005\u306b\u3042\u308a\u304c\u3061\u306a\u9593\u9055\u3044\u3078\u306e\u5bfe\u51e6\u6cd5 (\ud83c\uddef\ud83c\uddf5 edition):
Teiva Harsanyi is a senior software engineer at Google. He has worked in various domains, including insurance, transportation, and safety-critical industries like air traffic management. He is passionate about Go and how to design and implement reliable systems.
...
Avoiding shadowed variables can help prevent mistakes like referencing the wrong variable or confusing readers.
Avoiding nested levels and keeping the happy path aligned on the left makes building a mental code model easier.
When initializing variables, remember that init functions have limited error handling and make state handling and testing more complex. In most cases, initializations should be handled as specific functions.
Forcing the use of getters and setters isn\u2019t idiomatic in Go. Being pragmatic and finding the right balance between efficiency and blindly following certain idioms should be the way to go.
Abstractions should be discovered, not created. To prevent unnecessary complexity, create an interface when you need it and not when you foresee needing it, or if you can at least prove the abstraction to be a valid one.
Keeping interfaces on the client side avoids unnecessary abstractions.
To prevent being restricted in terms of flexibility, a function shouldn\u2019t return interfaces but concrete implementations in most cases. Conversely, a function should accept interfaces whenever possible.
any
Only use any if you need to accept or return any possible type, such as json.Marshal. Otherwise, any doesn\u2019t provide meaningful information and can lead to compile-time issues by allowing a caller to call methods with any data type.
json.Marshal
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.
Using type embedding can also help avoid boilerplate code; however, ensure that doing so doesn\u2019t lead to visibility issues where some fields should have remained hidden.
To handle options conveniently and in an API-friendly manner, use the functional options pattern.
Following a layout such as project-layout can be a good way to start structuring Go projects, especially if you are looking for existing conventions to standardize a new project.
Naming is a critical piece of application design. Creating packages such as common, util, and shared doesn\u2019t bring much value for the reader. Refactor such packages into meaningful and specific package names.
common
util
shared
To avoid naming collisions between variables and packages, leading to confusion or perhaps even bugs, use unique names for each one. If this isn\u2019t feasible, use an import alias to change the qualifier to differentiate the package name from the variable name, or think of a better name.
To help clients and maintainers understand your code\u2019s purpose, document exported elements.
Because integer overflows and underflows are handled silently in Go, you can implement your own functions to catch them.
Making floating-point comparisons within a given delta can ensure that your code is portable.
When performing addition or subtraction, group the operations with a similar order of magnitude to favor accuracy. Also, perform multiplication and division before addition and subtraction.
Understanding the difference between slice length and capacity should be part of a Go developer\u2019s 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.
When creating a slice, initialize it with a given length or capacity if its length is already known. This reduces the number of allocations and improves performance. The same logic goes for maps, and you need to initialize their size.
To prevent common confusions such as when using the encoding/json or the reflect package, you need to understand the difference between nil and empty slices. Both are zero-length, zero-capacity slices, but only a nil slice doesn\u2019t require allocation.
encoding/json
reflect
To check if a slice doesn\u2019t contain any element, check its length. This check works regardless of whether the slice is nil or empty. The same goes for maps.
nil
To design unambiguous APIs, you shouldn\u2019t distinguish between nil and empty slices.
To copy one slice to another using the copy built-in function, remember that the number of copied elements corresponds to the minimum between the two slice\u2019s lengths.
copy
Using copy or the full slice expression is a way to prevent append from creating conflicts if two different functions use slices backed by the same array. However, only a slice copy prevents memory leaks if you want to shrink a large slice.
append
Working with a slice of pointers or structs with pointer fields, you can avoid memory leaks by marking as nil the elements excluded by a slicing operation.
See #21.
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.
To compare types in Go, you can use the == and != operators if two types are comparable: Booleans, numerals, strings, pointers, channels, and structs are composed entirely of comparable types. Otherwise, you can either use reflect.DeepEqual and pay the price of reflection or use custom implementations and libraries.
reflect.DeepEqual
range
The value element in a range loop is a copy. Therefore, to mutate a struct, for example, access it via its index or via a classic for loop (unless the element or the field you want to modify is a pointer).
for
Understanding that the expression passed to the range operator is evaluated only once before the beginning of the loop can help you avoid common mistakes such as inefficient assignment in channel or slice iteration.
Using a local variable or accessing an element using an index, you can prevent mistakes while copying pointers inside a loop.
To ensure predictable outputs when using maps, remember that a map data structure: * Doesn\u2019t order the data by keys * Doesn\u2019t preserve the insertion order * Doesn\u2019t have a deterministic iteration order * Doesn\u2019t guarantee that an element added during an iteration will be produced during this iteration
break
Using break or continue with a label enforces breaking a specific statement. This can be helpful with switch or select statements inside loops.
continue
switch
select
defer
Extracting loop logic inside a function leads to executing a defer statement at the end of each iteration.
Understanding that a rune corresponds to the concept of a Unicode code point and that it can be composed of multiple bytes should be part of the Go developer\u2019s core knowledge to work accurately with strings.
Iterating on a string with the range operator iterates on the runes with the index corresponding to the starting index of the rune\u2019s byte sequence. To access a specific rune index (such as the third rune), convert the string into a []rune.
[]rune
strings.TrimRight/strings.TrimLeft removes all the trailing/leading runes contained in a given set, whereas strings.TrimSuffix/strings.TrimPrefix returns a string without a provided suffix/prefix.
strings.TrimRight
strings.TrimLeft
strings.TrimSuffix
strings.TrimPrefix
Concatenating a list of strings should be done with strings.Builder to prevent allocating a new string during each iteration.
strings.Builder
Remembering that the bytes package offers the same operations as the strings package can help avoid extra byte/string conversions.
bytes
strings
Using copies instead of substrings can prevent memory leaks, as the string returned by a substring operation will be backed by the same byte array.
The decision whether to use a value or a pointer receiver should be made based on factors such as the type, whether it has to be mutated, whether it contains a field that can\u2019t be copied, and how large the object is. When in doubt, use a pointer receiver.
Using named result parameters can be an efficient way to improve the readability of a function/method, especially if multiple result parameters have the same type. In some cases, this approach can also be convenient because named result parameters are initialized to their zero value. But be cautious about potential side effects.
See #43.
When returning an interface, be cautious about returning not a nil pointer but an explicit nil value. Otherwise, unintended consequences may result because the caller will receive a non-nil value.
Designing functions to receive io.Reader types instead of filenames improves the reusability of a function and makes testing easier.
io.Reader
Passing a pointer to a defer function and wrapping a call inside a closure are two possible solutions to overcome the immediate evaluation of arguments and receivers.
Using panic is an option to deal with errors in Go. However, it should only be used sparingly in unrecoverable conditions: for example, to signal a programmer error or when you fail to load a mandatory dependency.
panic
Wrapping an error allows you to mark an error and/or provide additional context. However, error wrapping creates potential coupling as it makes the source error available for the caller. If you want to prevent that, don\u2019t use error wrapping.
If you use Go 1.13 error wrapping with the %w directive and fmt.Errorf, comparing an error against a type or a value has to be done using errors.As or errors.Is, respectively. Otherwise, if the returned error you want to check is wrapped, it will fail the checks.
%w
fmt.Errorf
errors.As
errors.Is
See #50.
To convey an expected error, use error sentinels (error values). An unexpected error should be a specific error type.
In most situations, an error should be handled only once. Logging an error is handling an error. Therefore, you have to choose between logging or returning an error. In many cases, error wrapping is the solution as it allows you to provide additional context to an error and return the source error.
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.
In many cases, you shouldn\u2019t ignore an error returned by a defer function. Either handle it directly or propagate it to the caller, depending on the context. If you want to ignore it, use the blank identifier.
Understanding the fundamental differences between concurrency and parallelism is a cornerstone of the Go developer\u2019s knowledge. Concurrency is about structure, whereas parallelism is about execution.
To be a proficient developer, you must acknowledge that concurrency isn\u2019t 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.
Being aware of goroutine interactions can also be helpful when deciding between channels and mutexes. In general, parallel goroutines require synchronization and hence mutexes. Conversely, concurrent goroutines generally require coordination and orchestration and hence channels.
Being proficient in concurrency also means understanding that data races and race conditions are different concepts. Data races occur when multiple goroutines simultaneously access the same memory location and at least one of them is writing. Meanwhile, being data-race-free doesn\u2019t necessarily mean deterministic execution. When a behavior depends on the sequence or the timing of events that can\u2019t be controlled, this is a race condition.
Understanding the Go memory model and the underlying guarantees in terms of ordering and synchronization is essential to prevent possible data races and/or race conditions.
When creating a certain number of goroutines, consider the workload type. Creating CPU-bound goroutines means bounding this number close to the GOMAXPROCS variable (based by default on the number of CPU cores on the host). Creating I/O-bound goroutines depends on other factors, such as the external system.
GOMAXPROCS
Go contexts are also one of the cornerstones of concurrency in Go. A context allows you to carry a deadline, a cancellation signal, and/or a list of keys-values.
Understanding the conditions when a context can be canceled should matter when propagating it: for example, an HTTP handler canceling the context when the response has been sent.
Avoiding leaks means being mindful that whenever a goroutine is started, you should have a plan to stop it eventually.
To avoid bugs with goroutines and loop variables, create local variables or call functions instead of closures.
Understanding that select with multiple channels chooses the case randomly if multiple options are possible prevents making wrong assumptions that can lead to subtle concurrency bugs.
Send notifications using a chan struct{} type.
chan struct{}
Using nil channels should be part of your concurrency toolset because it allows you to remove cases from select statements, for example.
Carefully decide on the right channel type to use, given a problem. Only unbuffered channels provide strong synchronization guarantees.
You should have a good reason to specify a channel size other than one for buffered channels.
Being aware that string formatting may lead to calling existing functions means watching out for possible deadlocks and other data races.
Calling append isn\u2019t always data-race-free; hence, it shouldn\u2019t be used concurrently on a shared slice.
Remembering that slices and maps are pointers can prevent common data races.
sync.WaitGroup
To accurately use sync.WaitGroup, call the Add method before spinning up goroutines.
Add
sync.Cond
You can send repeated notifications to multiple goroutines with sync.Cond.
errgroup
You can synchronize a group of goroutines and handle errors and contexts with the errgroup package.
sync
sync types shouldn\u2019t be copied.
Remain cautious with functions accepting a time.Duration. Even though passing an integer is allowed, strive to use the time API to prevent any possible confusion.
time.Duration
time.After
Avoiding calls to time.After in repeated functions (such as loops or HTTP handlers) can avoid peak memory consumption. The resources created by time.After are released only when the timer expires.
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.
json.Marshaler
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.
time.Time
To avoid wrong assumptions when you provide a map while unmarshaling JSON data, remember that numerics are converted to float64 by default.
float64
sql.Open
Call the Ping or PingContext method if you need to test your configuration and make sure a database is reachable.
Ping
PingContext
Configure the database connection parameters for production-grade applications.
Using SQL prepared statements makes queries more efficient and more secure.
Deal with nullable columns in tables using pointers or sql.NullXXX types.
sql.NullXXX
Call the Err method of sql.Rows after row iterations to ensure that you haven\u2019t missed an error while preparing the next row.
Err
sql.Rows
os.File
Eventually close all structs implementing io.Closer to avoid possible leaks.
io.Closer
To avoid unexpected behaviors in HTTP handler implementations, make sure you don\u2019t miss the return statement if you want a handler to stop after http.Error.
return
http.Error
For production-grade applications, don\u2019t use the default HTTP client and server implementations. These implementations are missing timeouts and behaviors that should be mandatory in production.
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.
Enabling the -race flag is highly recommended when writing concurrent applications. Doing so allows you to catch potential data races that can lead to software bugs.
-race
Using the -parallel flag is an efficient way to speed up tests, especially long-running ones.
-parallel
Use the -shuffle flag to help ensure that a test suite doesn\u2019t rely on wrong assumptions that could hide bugs.
-shuffle
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.
Avoid sleeps using synchronization to make a test less flaky and more robust. If synchronization isn\u2019t possible, consider a retry approach.
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.
httptest
iotest
The httptest package is helpful for dealing with HTTP applications. It provides a set of utilities to test both clients and servers.
The iotest package helps write io.Reader and test that an application is tolerant to errors.
Use time methods to preserve the accuracy of a benchmark.
Increasing benchtime or using tools such as benchstat can be helpful when dealing with micro-benchmarks.
benchtime
benchstat
Be careful with the results of a micro-benchmark if the system that ends up running the application is different from the one running the micro-benchmark.
Make sure the function under test leads to a side effect, to prevent compiler optimizations from fooling you about the benchmark results.
To prevent the observer effect, force a benchmark to re-create the data used by a CPU-bound function.
Use code coverage with the -coverprofile flag to quickly see which part of the code needs more attention.
-coverprofile
Place unit tests in a different package to enforce writing tests that focus on an exposed behavior, not internals.
Handling errors using the *testing.T variable instead of the classic if err != nil makes code shorter and easier to read.
*testing.T
if err != nil
You can use setup and teardown functions to configure a complex environment, such as in the case of integration tests.
Fuzzing is an efficient strategy to detect random, unexpected, or malformed inputs to complex functions and methods in order to discover vulnerabilities, bugs, or even potential crashes.
Credits: @jeromedoucet
Understanding how to use CPU caches is important for optimizing CPU-bound applications because the L1 cache is about 50 to 100 times faster than the main memory.
Being conscious of the cache line concept is critical to understanding how to organize data in data-intensive applications. A CPU doesn\u2019t 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.
Slice of structs vs. struct of slices
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) isn\u2019t predictable.
To avoid a critical stride, hence utilizing only a tiny portion of the cache, be aware that caches are partitioned.
Knowing that lower levels of CPU caches aren\u2019t shared across all the cores helps avoid performance-degrading patterns such as false sharing while writing concurrency code. Sharing memory is an illusion.
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.
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).
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.
sync.Pool
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.
Use the fast-path inlining technique to efficiently reduce the amortized time to call a function.
Rely on profiling and the execution tracer to understand how an application performs and the parts to optimize.
Understanding how to tune the GC can lead to multiple benefits such as handling sudden load increases more efficiently.
To help avoid CPU throttling when deployed in Docker and Kubernetes, keep in mind that Go isn\u2019t CFS-aware.
TODO