Searching...
English
EnglishEnglish
EspañolSpanish
简体中文Chinese
FrançaisFrench
DeutschGerman
日本語Japanese
PortuguêsPortuguese
ItalianoItalian
한국어Korean
РусскийRussian
NederlandsDutch
العربيةArabic
PolskiPolish
हिन्दीHindi
Tiếng ViệtVietnamese
SvenskaSwedish
ΕλληνικάGreek
TürkçeTurkish
ไทยThai
ČeštinaCzech
RomânăRomanian
MagyarHungarian
УкраїнськаUkrainian
Bahasa IndonesiaIndonesian
DanskDanish
SuomiFinnish
БългарскиBulgarian
עבריתHebrew
NorskNorwegian
HrvatskiCroatian
CatalàCatalan
SlovenčinaSlovak
LietuviųLithuanian
SlovenščinaSlovenian
СрпскиSerbian
EestiEstonian
LatviešuLatvian
فارسیPersian
മലയാളംMalayalam
தமிழ்Tamil
اردوUrdu
Learning Go

Learning Go

An Idiomatic Approach to Real-World Go Programming
by Jon Bodner 2021 375 pages
4.45
100+ ratings
Listen
Listen to Summary
Try Full Access for 7 Days
Unlock listening & more!
Continue

Key Takeaways

1. Go's Core Philosophy: Simplicity, Readability, Maintainability

Go is intended for building programs that last, programs that are modified by dozens of developers over dozens of years.

Build for longevity. Go prioritizes long-term maintainability over short-term conciseness or cutting-edge features. Its small feature set, strict formatting rules enforced by go fmt, and explicit nature are designed to make code easy for large teams to read and understand over time, reducing the cognitive load for developers joining or maintaining a project years later. This focus on clarity helps prevent subtle bugs and makes refactoring less risky.

Boring is good. The language deliberately omits features common in other modern languages like inheritance, generics (until recently), exceptions, and function overloading. This perceived "boringness" is a strength, as it leads to straightforward, predictable code. The design choices, like the semicolon insertion rule or the lack of implicit type promotion, simplify the compiler and enable powerful tooling, further supporting maintainability.

Practicality over purity. While drawing inspiration from various paradigms (procedural, concurrent, functional-lite), Go is fundamentally a practical language. Its features are chosen to solve real-world software engineering problems efficiently, balancing performance, ease of development, and maintainability. The goal is to build reliable, scalable systems that can evolve gracefully.

2. Embrace Idiomatic Go: Explicit Over Implicit

Idiomatic Go values comprehensibility over conciseness.

Clarity is paramount. Go consistently favors explicit actions and declarations over implicit ones, even if it means writing a few more lines of code. This philosophy is evident in many language features, such as:

  • Explicit type conversions (int(floatVal)) instead of automatic type promotion.
  • Explicit error handling (if err != nil) instead of hidden exceptions.
  • Explicit variable declarations (var x int or x := 10) with clear scoping rules.
  • Explicit pointer dereferencing (*ptr) and address-of (&val) operators.

Avoid magic. Features that hide complexity or introduce non-obvious behavior are generally avoided. This makes the flow of data and control through a Go program very clear. You can usually understand what a piece of code does by reading it directly, without needing to infer hidden operations or side effects.

Intentional design. This focus on explicitness is a deliberate design choice to make code easier to reason about, especially in large codebases with many contributors. While it might feel verbose initially to developers from languages with more implicit behaviors, it quickly becomes a strength for debugging and collaboration.

3. Master Go's Built-in Data Structures: Slices, Maps, and Structs

Most of the time, when you want a data structure that holds a sequence of values, a slice is what you should use.

Foundation of data. Go provides powerful and efficient built-in composite types: arrays, slices, maps, and structs. Arrays are fixed-size and rarely used directly due to their size being part of their type. Slices, built on top of arrays, are the dynamic sequence type, offering flexibility with functions like append, len, and cap, and are the go-to for ordered collections.

Key-value power. Maps provide efficient key-value storage, ideal when element order doesn't matter. They are reference types, meaning copies share the underlying data. The comma ok idiom (value, ok := map[key]) is crucial for distinguishing between a zero value and a missing key. Maps are not comparable with ==.

Group related data. Structs allow grouping related data fields under a single type, providing structure and clarity. They are value types by default, meaning copies are independent unless pointers are used. Struct tags (json:"field_name") add metadata for external data mapping, processed by functions, not automatically.

4. Functions and Methods: Go's Approach to Behavior

Every time you pass a parameter to a function, Go always makes a copy of the value of the variable.

Call by value. Go is strictly call-by-value. When you pass a variable to a function, a copy of its value is made. For primitive types, structs, and arrays, this means the original variable cannot be modified by the function. For reference types like slices, maps, and channels, the pointer value is copied, allowing modifications to the underlying data but not resizing (for slices) or reassigning the original variable itself.

Multiple returns. A key feature is the ability to return multiple values, commonly used for returning a result and an error (result, err := someFunc()). Unused return values must be explicitly ignored with the underscore _. Named return values can improve readability but should be used carefully to avoid confusion or accidental blank returns.

Functions as values. Functions are first-class citizens and can be assigned to variables, passed as parameters, or returned from other functions. Anonymous functions (closures) capture variables from their surrounding scope, enabling powerful patterns like adapters, callbacks, and resource cleanup via defer.

5. Errors Are Values: Handle Them Explicitly

Error handling is what separates the professionals from the amateurs.

No exceptions. Go does not have exceptions. Errors are handled by returning an error type as the last return value from a function. A nil error indicates success. This forces developers to consider and handle potential failure paths explicitly using if err != nil checks, making control flow clear and preventing unhandled runtime crashes common with exceptions.

The error interface. The built-in error type is a simple interface with a single method, Error() string. You can create basic errors with errors.New or fmt.Errorf. For more complex errors, define custom types that implement the error interface, allowing them to carry additional context or data.

Wrapping and unwrapping. Errors can be wrapped using fmt.Errorf with %w to create a chain of errors, preserving the original error while adding context. The errors.Is and errors.As functions are used to check if an error chain contains a specific sentinel error instance or a specific custom error type, respectively, providing robust error inspection.

6. Concurrency: Communicate By Sharing, Not Sharing By Communicating

“Share memory by communicating; do not communicate by sharing memory.”

Goroutines and channels. Go's primary concurrency model is based on Communicating Sequential Processes (CSP), using lightweight goroutines and typed channels. Goroutines are functions running concurrently, managed by the Go runtime scheduler. Channels are the pipes used for safe, synchronized communication and data transfer between goroutines, avoiding the complexities of shared memory and locks in many cases.

Select for coordination. The select statement is used with channels to wait on multiple communication operations simultaneously. It randomly picks one case that is ready, preventing starvation and simplifying coordination logic compared to complex locking schemes. select is often used in for-select loops to process channel data until a cancellation signal is received.

Avoid shared state. While mutexes (sync.Mutex, sync.RWMutex) exist for protecting shared memory, the idiomatic approach is to structure concurrent code so that data is passed between goroutines via channels, ensuring that only one goroutine has access to a piece of data at a time. Mutexes are best reserved for protecting access to shared resources like struct fields or maps where direct data transfer isn't the primary goal.

7. Interfaces: Implicit, Flexible Abstraction

If the method set for a concrete type contains all of the methods in the method set for an interface, the concrete type implements the interface.

Implicit implementation. Go interfaces are abstract types that define a method set. A concrete type implicitly implements an interface if it provides all the methods in the interface's method set. There is no explicit declaration (implements keyword) required. This allows for flexible decoupling; a type can satisfy an interface without being aware of it.

Type-safe duck typing. This implicit nature enables a form of type-safe duck typing. Code that expects an interface can accept any concrete type that "walks and quacks" like the interface (has the required methods), while the compiler still enforces that the methods exist and have the correct signatures. This balances the flexibility of dynamic languages with the safety of static typing.

Accept interfaces, return structs. A common Go idiom is to design functions and methods to "accept interfaces, return structs." Accepting interfaces makes your code flexible and testable by depending on behavior, not specific implementations. Returning concrete structs provides stability for callers, allowing new fields or methods to be added without breaking existing code, unlike returning interfaces which are brittle to changes in their method set.

8. Packages and Modules: Organize and Manage Dependencies

Making your module available to other people is as simple as putting it in a version control system.

Code organization. Go code is organized into packages, which serve as namespaces. Package-level identifiers starting with an uppercase letter are exported (public), while those starting with lowercase or underscore are unexported (private). This simple capitalization rule governs API visibility. Packages reside in directories, and the package name usually matches the directory name.

Modules for dependencies. Modules are collections of packages versioned together, defined by a go.mod file. The go.mod file declares the module path (usually its repository URL) and its dependencies with semantic versioning (vX.Y.Z). The go.sum file records cryptographic hashes of dependencies for security and integrity.

Dependency management. The go command (go build, go run, go test, go get) automatically handles downloading and caching dependencies based on the go.mod file. Go uses minimum version selection to resolve dependency conflicts. Publishing a module is as simple as pushing tagged code to a repository; there's no central registry required for distribution.

9. Testing Is Built-in and Essential

Go makes it so easy to test your code, there’s no excuse to not do it.

First-class support. Testing is a core part of the Go ecosystem, integrated directly into the standard library (testing package) and tooling (go test). Test files live alongside the code they test (_test.go suffix) and can access unexported identifiers within the same package, simplifying unit testing.

Test functions. Tests are written as functions starting with Test that accept a *testing.T parameter. Methods on *testing.T like Error, Errorf, Fatal, and Fatalf are used to report failures. Fatal methods stop the current test function immediately, while Error methods allow it to continue.

Advanced testing. Go supports table-driven tests using t.Run for concise, data-driven testing. It includes built-in tools for:

  • Code coverage (go test -cover) to identify untested code paths.
  • Benchmarking (go test -bench) to measure performance.
  • Race detection (go test -race) to find concurrency issues.
  • Stubbing HTTP services (net/http/httptest) for easier integration testing.

10. Pointers: Understand When and Why to Use Them

The first rule of pointers is to not be afraid of them.

Memory addresses. Pointers (*Type) hold the memory address of a value. The & operator gets the address of a variable, and the * operator dereferences a pointer to access the value it points to. The zero value for a pointer is nil. Unlike C/C++, Go pointers do not support arithmetic (except via unsafe).

Mutability indication. Pointers are primarily used to indicate that a function or method might modify the value passed to it. Since Go is call-by-value, passing a non-pointer type copies the value, making the original immutable. Passing a pointer
[ERROR: Incomplete response]

Last updated:

Review Summary

4.45 out of 5
Average of 100+ ratings from Goodreads and Amazon.

Learning Go receives high praise from readers for its comprehensive and clear introduction to the Go programming language. Reviewers appreciate the book's focus on idiomatic Go code, practical examples, and explanations of language design decisions. Many find it valuable for both beginners and experienced programmers transitioning to Go. The book covers a wide range of topics, from basic syntax to advanced concepts like concurrency. Some readers note that certain sections, particularly on concurrency and generics, can be challenging. Overall, it's highly recommended for those seeking to learn Go effectively.

Your rating:
Be the first to rate!

About the Author

Jon Bodner is an experienced software developer and author known for his expertise in the Go programming language. His writing style is praised for its clarity and ability to explain complex concepts in an accessible manner. Bodner's approach in "Learning Go" emphasizes teaching readers how to write idiomatic Go code, focusing on best practices and the language's design philosophy. His experience in the field allows him to provide insights into why certain language features were implemented and how to use them effectively. Readers appreciate Bodner's ability to balance introductory material with more advanced topics, making the book suitable for a wide range of programmers.

Download EPUB

To read this Learning Go summary on your e-reader device or app, download the free EPUB. The .epub digital book format is ideal for reading ebooks on phones, tablets, and e-readers.
Download EPUB
File size: 3.00 MB     Pages: 12
0:00
-0:00
1x
Dan
Andrew
Michelle
Lauren
Select Speed
1.0×
+
200 words per minute
Home
Library
Get App
Create a free account to unlock:
Requests: Request new book summaries
Bookmarks: Save your favorite books
History: Revisit books later
Recommendations: Personalized for you
Ratings: Rate books & see your ratings
100,000+ readers
Try Full Access for 7 Days
Listen, bookmark, and more
Compare Features Free Pro
📖 Read Summaries
All summaries are free to read in 40 languages
🎧 Listen to Summaries
Listen to unlimited summaries in 40 languages
❤️ Unlimited Bookmarks
Free users are limited to 10
📜 Unlimited History
Free users are limited to 10
Risk-Free Timeline
Today: Get Instant Access
Listen to full summaries of 73,530 books. That's 12,000+ hours of audio!
Day 4: Trial Reminder
We'll send you a notification that your trial is ending soon.
Day 7: Your subscription begins
You'll be charged on May 13,
cancel anytime before.
Consume 2.8x More Books
2.8x more books Listening Reading
Our users love us
100,000+ readers
"...I can 10x the number of books I can read..."
"...exceptionally accurate, engaging, and beautifully presented..."
"...better than any amazon review when I'm making a book-buying decision..."
Save 62%
Yearly
$119.88 $44.99/year
$3.75/mo
Monthly
$9.99/mo
Try Free & Unlock
7 days free, then $44.99/year. Cancel anytime.
Scanner
Find a barcode to scan

Settings
General
Widget
Loading...
Black Friday Sale 🎉
$20 off Lifetime Access
$79.99 $59.99
Upgrade Now →