Backend6 min read

Go (Golang)

Build with Go - goroutines, channels, interfaces, error handling, and writing HTTP services with the standard library and Chi.

GoGolangGoroutinesChannelsConcurrency

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

  1. 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.
  2. Handle errors explicitlyGo has no exceptions. Functions return (value, error) - the caller must check the error. This makes error handling explicit, visible, and auditable.
  3. Build HTTP servicesGo's net/http standard library is production-ready. Chi adds routing with middleware without the overhead of heavyweight frameworks.
  4. 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.
  5. 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/O

Examples

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

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.