← back to posts

When Go Is the Right Choice (and When It Isn't)

I write Go daily and love it. But I’ve also wasted time using Go where another language would have been better. Here’s my honest framework for when to reach for Go.

Go Excels At

Network Services and APIs

This is Go’s sweet spot. HTTP servers, gRPC services, API gateways, proxies. The standard library’s net/http is production-ready out of the box. No framework needed.

mux := http.NewServeMux()
mux.HandleFunc("GET /health", healthHandler)
mux.HandleFunc("POST /orders", createOrderHandler)
http.ListenAndServe(":8080", mux)

Go starts fast (milliseconds), uses little memory, handles thousands of concurrent connections, and compiles to a single binary. For microservices, this combination is unbeatable.

CLI Tools

Single binary distribution, fast startup, cross-compilation built in. Go has replaced Python as my default for CLI tools.

GOOS=linux GOARCH=amd64 go build -o mytool-linux
GOOS=darwin GOARCH=arm64 go build -o mytool-mac

No runtime dependencies. No “install Python 3.x and pip install these packages.” Just copy the binary.

Concurrent Data Processing

Goroutines and channels make concurrent pipeline architectures natural:

func pipeline(input <-chan Data) <-chan Result {
    validated := validate(input)
    enriched := enrich(validated)
    return transform(enriched)
}

Each stage runs concurrently. Channels provide backpressure. The code reads linearly despite being concurrent.

Infrastructure and DevOps Tools

Docker, Kubernetes, Terraform, Prometheus — all written in Go. The ecosystem for infrastructure tooling is unmatched.

Go Struggles With

Data Science and ML

Python owns this space. Go doesn’t have equivalents for NumPy, Pandas, scikit-learn, or PyTorch. Don’t fight this — use Python for ML, Go for serving.

Rapid Prototyping

Go’s type system and explicit error handling make it verbose for quick experiments. Python or TypeScript are faster for “let me try this idea.”

A 20-line Python script becomes 60 lines of Go with proper error handling, type definitions, and imports. That’s fine for production code — it’s overhead for a throwaway prototype.

Complex Domain Logic

Go doesn’t have generics (well, it does now, but the ecosystem is still catching up), algebraic data types, pattern matching, or rich type system features. For complex business logic with many states and transitions, languages like Rust, Kotlin, or even TypeScript model the domain more precisely.

// Go: runtime checks
func processOrder(order Order) error {
    switch order.Status {
    case "pending":
        // ...
    case "confirmed":
        // ...
    default:
        return fmt.Errorf("unexpected status: %s", order.Status)
    }
}

In Rust or Kotlin, the compiler would ensure you handle every case. In Go, you write a default case and hope.

Frontend Anything

Go + WASM is a novelty, not a production choice. Use TypeScript/React/Vue for frontend.

Go vs Specific Alternatives

Go vs Node.js/TypeScript

Choose Node.js when:

  • Your team is full-stack JavaScript
  • You need the npm ecosystem (payment SDKs, etc.)
  • You’re building a BFF (Backend for Frontend) that mirrors frontend data structures

Choose Go when:

  • Performance matters (Go is 5-10x faster for CPU-bound work)
  • Memory efficiency matters (Go uses 3-5x less memory)
  • You’re building infrastructure or platform services
  • You want simple deployment (single binary vs node_modules)

Go vs Rust

Choose Rust when:

  • You need zero-cost abstractions and maximum performance
  • Memory safety without garbage collection is critical
  • You’re building embedded systems, databases, or game engines

Choose Go when:

  • Development speed matters more than maximum performance
  • GC pauses of 1-2ms are acceptable
  • You want a simpler language with a faster learning curve
  • You’re building networked services (Go’s ecosystem is richer here)

Go vs Java/Kotlin

Choose Java/Kotlin when:

  • You need the JVM ecosystem (Spring, Hibernate, etc.)
  • Your team has deep JVM expertise
  • You need mature tooling for large monoliths

Choose Go when:

  • You want faster startup and lower memory (important for microservices and serverless)
  • You value simplicity over framework features
  • You’re building cloud-native services from scratch

My Decision Framework

Is it a network service or CLI tool?        → Go
Is it data science or ML?                   → Python
Is it frontend?                             → TypeScript
Is it a performance-critical system?        → Benchmark Go vs Rust
Is it a quick prototype?                    → Python or TypeScript
Does the team already know one well?        → Use what the team knows

The best language is the one your team can write, debug, and maintain. Go’s simplicity makes it easy to onboard new engineers and maintain long-term. That’s its real superpower — not raw performance, but sustained productivity.