Registered users can read the full content for free
Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.
Page
1
(This page has no text content)
Page
2
Go by Example Programmer's guide to idiomatic and testable code Inanc Gumus To comment go to livebook. Manning Shelter Island For more information on this and other Manning titles go to manning.com.
Page
3
copyright For online information and ordering of this and other Manning books, please visit www.manning.com. The publisher offers discounts on this book when ordered in quantity. For more information, please contact Special Sales Department Manning Publications Co. 20 Baldwin Road PO Box 761 Shelter Island, NY 11964 Email: orders@manning.com © 2025 Manning Publications Co. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher. Many of the designations used by manufacturers and sellers to distinguish their
Page
4
products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps. Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid- free paper, and we exert our best efforts to that end. Recognizing also our responsibility to conserve the resources of our planet, Manning books are printed on paper that is at least 15 percent recycled and processed without the use of elemental chlorine. Manning Publications Co. 20 Baldwin Road PO Box 761 Shelter Island, NY 11964 The author and publisher have made every effort to ensure that the information in this book was correct at press time. The author and publisher do not assume and hereby disclaim any liability to any party for any loss, damage, or disruption caused by errors or omissions, whether such errors or omissions result from negligence, accident, or any other cause, or from any usage of the information herein. ISBN 9781617299896 Printed in the United States of America
Page
5
Development editor: Katie Sposato Technical development editor: Marion Newlevant Review editor: Dunja Nikitović Production editor: Kathy Rossland Copy editor: Keir Simpson Proofreader: Melody Dolab Technical proofreader: Tim van Deurzen Typesetter: Tamara Švelić Sabljić Cover designer: Marija Tudor
Page
6
dedication To my parents, who bought me my first computer. Rest among the stars, Dad.
Page
7
contents preface acknowledgments about this book about the author about the cover illustration 1 Getting started 1.1 Why should you read this book? 1.1.1 Learning by example 1.1.2 Crafting idiomatic code 1.1.3 Crafting testable code 1.2 Why Go? 1.3 Hello, gophers! 1.3.1 Statically and strongly typed language 1.3.2 Compilation and static binary 1.3.3 Go runtime 1.4 Concurrency 1.4.1 Goroutines 1.4.2 Go scheduler 1.4.3 Channels 1.5 Type system 1.5.1 Composition instead of inheritance
Page
8
1.5.2 Implicit interfaces 1.5.3 Testing with implicit interfaces 1.6 Standard library 1.7 Tooling 1.8 Outro 1.9 Source code and Go module 2 Idioms and testing 2.1 Groundwork 2.1.1 Overview 2.1.2 Implementation 2.2 Idioms 2.2.1 Names 2.2.2 Errors 2.2.3 Fields 2.2.4 Standard interfaces 2.3 Testing 2.3.1 Writing tests 2.3.2 Running tests 2.3.3 Writing a failing test 2.3.4 Writing descriptive test failure messages 2.3.5 Fixing the code 2.4 Table-driven tests 2.4.1 Creating a table of test cases 2.4.2 Writing a table-driven test 2.4.3 Running a specific test 2.4.4 Identifying problems with table-driven tests
Page
9
2.5 Subtests 2.5.1 Understanding subtests 2.5.2 Writing a table-driven test with subtests 2.5.3 Understanding the role of specific T pointers 2.5.4 Running subtests 2.5.5 Fixing the code 2.6 Example tests 2.6.1 Writing an example test 2.6.2 Running example tests 2.7 Other testing tools 3 Test coverage and optimization 3.1 Test coverage 3.1.1 Measuring test coverage 3.1.2 Perfecting test coverage 3.1.3 100% test coverage != bug-free code 3.2 Benchmarking and optimization 3.2.1 Writing and running benchmarks 3.2.2 Using sub-benchmarks 3.2.3 Profiling: Chasing out memory allocations 3.2.4 Optimizing code 3.2.5 Comparing benchmarks 3.3 Compiler optimizations 3.3.1 Inlining and dead-code elimination 3.3.2 The sink variable 3.3.3 A bright new future 3.4 Parallel testing
Page
10
3.4.1 Running tests in parallel 3.4.2 Running subtests in parallel 3.4.3 Detecting data races 3.5 Exercises 4 Command-line interfaces 4.1 Groundwork 4.1.1 Implementing the first version 4.1.2 Running the first version 4.2 Flag parsing 4.2.1 Overview 4.2.2 Higher-order value parsers 4.2.3 Implementing a flag parser 4.2.4 Integration and setting sensible defaults 4.3 The flag package 4.3.1 Integration 4.3.2 Demonstration 4.4 Value parsers 4.4.1 Value interface 4.4.2 Satisfying the Value interface 4.4.3 Using Var 4.5 Positional arguments 4.5.1 Flags vs. positional arguments 4.5.2 Customizing usage messages 4.5.3 Setting a positional argument 4.6 Validation 4.6.1 Writing a custom validator
Page
11
4.6.2 Validating flags with a custom validator 4.7 Exercises 5 Dependency injection 5.1 Challenges to testability 5.2 Testable programs 5.2.1 Overview 5.2.2 A place to store dependencies 5.2.3 io.Writer’s role 5.3 Decoupling 5.3.1 Decoupling from the environment 5.3.2 Decoupling the parser’s output 5.3.3 Preparing for the HIT client 5.3.4 Demonstration 5.4 CLI tests 5.4.1 Observing with a strings.Builder 5.4.2 Streamlining tests 5.4.3 Adding a helper 5.4.4 Writing CLI tests 5.5 Unit testing 5.5.1 A helper type 5.5.2 Unit-testing the parser 6 Synchronous APIs for concurrency 6.1 Overview 6.1.1 Package hit 6.1.2 Package API
Page
12
6.1.3 Directory structure 6.2 Foundations 6.2.1 Result 6.2.2 Send 6.3 Iterators 6.3.1 Push iterators 6.3.2 Producing values 6.3.3 Consuming values 6.3.4 Testing 6.4 Options 6.4.1 Providing options 6.4.2 Accepting options 6.5 Integration 6.5.1 Printing a summary 6.5.2 Integration 6.5.3 Demonstration 6.6 Concurrent pipeline pattern 6.6.1 Benefits of concurrent pipelines 6.6.2 Designing a concurrent pipeline 6.7 Producer stage 6.7.1 Implementation 6.7.2 Integration 6.8 Throttler stage 6.8.1 Implementation 6.8.2 Integration 6.9 Dispatcher stage
Page
13
6.9.1 Implementation 6.9.2 Integration 6.9.3 Demonstration 6.9.4 Outro 6.10 Exercises 7 Responsive and efficient programs 7.1 Revisiting the concurrent pipeline 7.2 Cancellation propagation 7.2.1 What is Context? 7.2.2 Context is like a tree 7.2.3 Context in practice 7.2.4 Deriving a new Context 7.2.5 Is Ctrl+C the end? 7.3 HTTP and efficient I/O operations 7.3.1 Round-tripping 7.3.2 Interface composition 7.3.3 ReadAll: Eat it all 7.3.4 Copy: Eat small, be small 7.3.5 Putting it all together 7.3.6 Demonstration 7.4 Optimization 7.4.1 Client and its RoundTripper 7.4.2 Tweaking the connection pool 7.4.3 Demonstration 7.5 Testing 7.5.1 Satisfying RoundTripper
Page
14
7.5.2 Testing with a RoundTripper 7.6 HTTP testing 7.6.1 Package httptest 7.6.2 Testing the client 7.7 Exercises 8 Structuring packages and services 8.1 Organizing and structuring packages 8.1.1 Avoiding import cycles 8.1.2 Structuring packages in practice 8.2 Core 8.2.1 Errors 8.2.2 Core 8.2.3 Service 8.2.4 Mutex 8.3 HTTP 8.3.1 Health check 8.3.2 Serving HTTP 8.3.3 HTTP server 8.4 HTTP handlers 8.4.1 Handler closures 8.4.2 Redirecting 8.4.3 HTTP status codes 8.5 Routing 8.5.1 ServeMux 8.5.2 Routes 8.5.3 Demonstration
Page
15
8.6 Timeouts 8.7 Testing 8.7.1 Response recording 8.7.2 Testing a handler 8.7.3 Test helpers 8.8 Simplicity 8.8.1 Up-front abstractions and indirections 8.8.2 Inherently testable types 8.9 Exercises 9 Composition patterns 9.1 Middleware pattern 9.1.1 What is HTTP middleware? 9.1.2 Example 9.1.3 Practice 9.1.4 Learning more about the slog package and the any interface 9.1.5 Integration 9.2 Logging responses 9.2.1 Measuring durations 9.2.2 Response recording 9.2.3 Integration 9.3 Interceptor pattern 9.3.1 Field embedding 9.3.2 Capturing and saving 9.3.3 Integration 9.3.4 Demonstration 9.4 Optional interface pattern
Page
16
9.4.1 Type asserting for optional functionality 9.4.2 Unwrapping all the way down 9.4.3 Unwrap 9.5 Context value propagation pattern 9.5.1 Generating, storing, and retrieving 9.5.2 Middleware 9.5.3 Wrapping slog handlers 9.5.4 Implementing a slog.Handler 9.5.5 Implementing WithAttrs and WithGroup 9.5.6 Integration 9.6 Handler-chaining pattern 9.6.1 Chainable handlers 9.6.2 Response handlers 9.6.3 Responder 9.6.4 Integration 9.7 Encoding and decoding 9.7.1 Encoding JSON 9.7.2 Decoding JSON 9.7.3 Speaking JSON 9.8 Wrapping and unwrapping 9.8.1 Safeguarding against denial-of-service attacks 9.8.2 Unwrapping the original 9.9 Outro 9.10 Exercises 10 Polymorphic storage 10.1 Interacting with SQL databases
Page
17
10.1.1 Registering a driver 10.1.2 Opening a connection pool 10.1.3 File embedding 10.2 Database-backed service 10.2.1 Insertion 10.2.2 Test database 10.2.3 Integration testing 10.2.4 errors.As 10.2.5 Retrieval 10.3 Valuer and Scanner 10.3.1 Supporting custom database types 10.3.2 Satisfying Valuer and Scanner 10.3.3 Encoding and decoding 10.4 Implicit interfaces 10.4.1 Consumers-first approach 10.4.2 Providing an interface 10.4.3 Activating the new implementation 10.5 Exercises appendix A Modules and packages A.1 Packages A.1.1 Example A.2 Package main A.3 Modules A.4 Building and running A.5 Exercises
Page
18
appendix B Variables and pointers B.1 Variables B.2 Pointers B.2.1 What is a pointer? B.2.2 Nil pointers and type safety B.2.3 Address operators B.2.4 Pass-by-value mechanics B.2.5 Stack and heap memory B.3 Exercises appendix C Arrays, slices, and maps C.1 Arrays C.1.1 Array operations C.1.2 Passing arrays around C.1.3 Passing a pointer C.1.4 Exercises C.2 Slices C.2.1 Usage C.2.2 Underlying arrays C.2.3 Passing slices around C.2.4 Slice expressions and capacity C.2.5 Appending C.2.6 Making slices C.2.7 Strings and byte slices C.2.8 Aliasing effects C.2.9 Copying slices
Page
19
C.2.10 Exercises C.3 Maps C.4 Exercises appendix D Object-oriented programming D.1 Structs D.1.1 Declaring a new type D.1.2 Creating new values D.1.3 Anonymous types D.1.4 Named types D.2 Methods and receivers D.2.1 Receiver types D.2.2 Value receivers D.2.3 Pointer receivers D.2.4 Avoid mixing receiver types D.2.5 Which receiver type to choose? D.2.6 Exercises D.3 Implicit interfaces D.3.1 Starting with the concrete types D.3.2 Discovering an interface D.3.3 Wiring up D.3.4 Harnessing the power of interfaces D.3.5 Interface values and method sets D.3.6 Exercises D.4 Generics appendix E Concurrent programming
Page
20
E.1 Goroutines E.1.1 Running sequentially E.1.2 Running concurrently E.2 Using WaitGroup E.2.1 Using syncx.SafeGroup E.2.2 Exercises E.3 Unbuffered channels E.3.1 Usage E.3.2 Synchronization E.3.3 Pattern: Sending and receiving E.4 Closing channels E.4.1 Pattern: for-range E.4.2 Pattern: Coordinating with a closing signal E.5 Using the select statement E.5.1 Waiting for multiple channels E.5.2 Pattern: Nonblocking operations E.6 Buffered channels E.6.1 Pattern: Timing out E.6.2 Directional channels E.6.3 Pattern: Limiting concurrency E.7 Exercises appendix F Self-referential options F.1 Struct options F.1.1 Zero-value ambiguity F.1.2 Zero-value options F.2 Option functions
The above is a preview of the first 20 pages. Register to read the complete e-book.
AI Reading Assistant
Whole-book reading guide from stratified index samples; jump to passages in the text
AI guide
# Go by Example — Reading Guide
## 【One-Line Pitch】
A practical, project-driven guide for experienced developers who want to master idiomatic Go through building, testing, and refining a real-world HTTP client tool — ideal for those ready to move beyond syntax into Go's philosophy of simplicity, composition, and testability.
## 【Book Arc】
- **Opening (~0%–10%)**: Establishes the book's audience and philosophy — experienced developers from Java, C++, Python, or JavaScript who need to "unlearn" habits that conflict with Go's design. Introduces core concepts like goroutines, channels, and the "share memory by communicating" principle, setting the foundation for idiomatic thinking.
- **Early (~10%–23%)**: Dives deep into testing fundamentals — table-driven tests, subtests, coverage measurement, and benchmarking. Uses a URL parsing package as the working example, showing how to structure tests, measure coverage with `coverprofile`, and compare benchmark results using `benchstat`.
- **Early (~23%–32%)**: Transitions to building the HIT tool — a command-line HTTP client. Starts with manual flag parsing using `os.Args` to explore language mechanics, then adopts the `flag` package. Refactors the tool to decouple from global state using `io.Writer` interfaces, making it testable without changing behavior.
- **Middle (~32%–42%)**: Introduces iterators and concurrent pipelines. Shows how to design a `Results` iterator using closures and yield functions, then refactors the HIT client to use a concurrent pipeline with producer, throttler, and dispatcher stages connected through channels.
- **Middle (~42%–48%)**: Covers panic testing with `recover`, then moves into HTTP client testing — using `httptest`, `RoundTripper` for intercepting requests, and testing clients against servers. Emphasizes cancellation propagation using the `context` package to prevent goroutine leaks.
- **Late (~48%–end)**: Focuses on structuring packages and services — avoiding import cycles, organizing code into core/service layers, and building HTTP servers with handlers, routing via `ServeMux`, health checks, and proper status codes. The excerpts do not cover the final chapters in detail.
## 【Key Takeaways】
- **Idiomatic Go requires unlearning** (Early): Code should be pragmatic, explicit, and testable — not speculative or over-abstracted. The book's core message is that simplicity is hard to achieve but essential for maintainable Go programs.
- **Table-driven tests are the idiomatic standard** (Early): Separating test data from logic reduces duplication and makes adding new cases trivial. Subtests ensure one failure doesn't halt the entire test suite, as each subtest runs independently.
- **Benchmarking requires comparison, not absolutes** (Early): Use `benchstat` to compare old and new implementations statistically. Coverage tools like `coverprofile` help identify untested code paths, but coverage alone doesn't guarantee quality.
- **Decoupling from global state enables testability** (Early): Injecting dependencies like `io.Writer` (instead of using `os.Stdout` directly) allows the same code to run with real or fake outputs. This pattern is central to writing testable CLI tools.
- **Iterators invert control flow** (Middle): Using closures as iterators — where the producer pushes values to a consumer's yield function — enables lazy, composable data processing. The Go compiler's built-in support for iterators simplifies this pattern.
- **Concurrent pipelines need explicit cancellation** (Middle): Without `context` propagation, pipelines can leak goroutines when consumers stop early. Passing a `Context` through pipeline stages enables graceful shutdown, such as handling Ctrl+C.
- **Panic and recover are test-only tools** (Middle): Use `recover` in tests to verify panic behavior, but avoid it in production code — explicit error handling is more maintainable than disrupting control flow.
- **Package structure prevents import cycles** (Late): Organizing code into core, service, and HTTP layers with clear boundaries avoids circular imports and keeps dependencies flowing in one direction.
## 【Reading Tips】
- **Skim the opening philosophy sections** (~0%–5%) if you're already convinced about Go's value — the real substance starts with testing in section 2.
- **Deep-read the testing chapters** (~10%–23%) — they're foundational for everything that follows. Pay special attention to table-driven tests and subtests; these patterns recur throughout the book.
- **Follow the HIT tool project closely** (~23%–48%) — it's the book's spine. Each refactoring step (flag parsing → dependency injection → iterators → pipelines → context) builds on the previous one, so skipping ahead will confuse you.
- **Watch for "Deep Dive" boxes** — they explain language mechanics (like variadic function backing arrays and closure memory management) that clarify why Go behaves as it does.
- **Try the exercises at each chapter's end** — the book is explicitly practical, and the exercises reinforce the patterns before you move to the next layer of complexity.
## 【Coverage Limits】
This guide covers the book's progression through testing, CLI tooling, iterators, concurrent pipelines, HTTP testing, and package structuring. The excerpts do not cover the final chapters on advanced HTTP server patterns, routing details, or the book's concluding exercises in depth.
##
Passage locations
Excerpt 1
into advanced patterns for concurrency, API design, package structuring, and idiomatic testing, empowering you to build more sophisticated, robust, and maint...
View in text
Excerpt 2
rage of the url package and outputs the result to cover.out The tool analyzes the url package’s code and saves the resulting test coverage profile to the cov...
View in text
Excerpt 3
er of requests (default 100) -rps value Requests per second This output shows that our flag parser still works correctly. The n flag expects a number, but we...
View in text
Excerpt 4
e worker goroutines send HTTP requests and produce results. #6 Sets the WaitGroup counter to the number of worker goroutines #7 Launches as many goroutines a...
View in text
Recommended for You
{{#thumbnailUrl}}
{{/thumbnailUrl}}
{{^thumbnailUrl}}
{{/thumbnailUrl}}
Loading recommended books...
Failed to load, please try again later
Tip the Site
Scan the WeChat Pay or Alipay code to tip. No login required.
WeChat Pay
Alipay