Go (Golang)
Build with Go - goroutines, channels, interfaces, error handling, and writing HTTP services with the standard library and Chi.
skill: Go (Golang) category: Backend tags: [Go, Golang, Goroutines, Channels, Concurrency] readingTime: 6
Go trades raw power for simplicity. Its concurrency model (goroutines + channels) is elegant, its compile times are fast, and its standard library covers most server-side needs. Go forces explicit error handling - no exceptions - which leads to more reliable code at the cost of more verbose call sites.
Workflow
- Use goroutines and channels for concurrencyGoroutines are cheap (a few KB of stack) - you can run thousands concurrently. Channels provide type-safe communication between goroutines, avoiding shared memory bugs.
- Handle errors explicitlyGo has no exceptions. Functions return (value, error) - the caller must check the error. This makes error handling explicit, visible, and auditable.
- Build HTTP servicesGo's net/http standard library is production-ready. Chi adds routing with middleware without the overhead of heavyweight frameworks.
- Use interfaces for testabilityGo interfaces are implicit - any type that implements the methods satisfies the interface, without declaration. This makes mocking trivially easy and enables dependency injection.
- Write idiomatic GoGo has strong style conventions. gofmt enforces formatting; following idioms makes code readable to any Go developer.
Output Format
Go: goroutines+channels for concurrent I/O, explicit error handling with %w wrapping, Chi router with middleware, implicit interface for mockability, and idiomatic guard clauses + table-driven tests.
go func(u url) { results <- fetch(u) }(url)
errors.As/Is for typed error checking
r := chi.NewRouter(); r.Use(middleware.Logger)
interface UserRepository { FindByID; Create }
context.WithTimeout(ctx, 5*time.Second) for all I/OExamples
Input
Output
Edge Cases & Gotchas
- Goroutine leaks - Goroutines that block forever (waiting on an unclosed channel, stuck in infinite loop) are goroutine leaks. Use context.WithCancel and check ctx.Done() in loops. goleak in tests detects leaks.
- nil interface vs. nil pointer - A nil *User cast to an interface{} is not nil - the interface has a type even though the value is nil. Check concrete type nilness before returning as interface, or the caller's nil check will fail.
- sync.WaitGroup vs. errgroup - sync.WaitGroup ignores errors from goroutines. Use golang.org/x/sync/errgroup to collect errors from concurrent goroutines and cancel all on first error.
- defer in loops - defer defers until function return, not loop iteration. defer file.Close() inside a loop doesn't close files until the function returns - files accumulate. Close within the loop body explicitly.
When to Break These Rules
- Use Rust instead of Go for systems programming where memory layout control, no GC pauses, and maximum performance matter.
- Use Node.js/TypeScript instead of Go for I/O-bound web APIs in teams where JavaScript expertise is higher - Go's performance benefit over Node.js is rarely the bottleneck in typical web APIs.
Reference Files
https://go.dev/doc/https://go.dev/doc/effective_go