Ecosystem

Go’s standard library handles most needs, but these libraries fill the gaps.

Web Frameworks

FrameworkStarsMarket sharePhilosophy
Gin80k+~48%Fast, middleware-heavy, Rails-like
Echo30k+~16%Similar to Gin, slightly cleaner API
Fiber34k+~11%Express.js-inspired, fasthttp-based
Chi18k+~10%Composable, stdlib-compatible router
stdlib-growingGo 1.22+ ServeMux handles most cases

When to use what

stdlib net/http: Small-to-medium APIs. Go 1.22+ pattern matching covers most routing needs. No dependency, no lock-in.

mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", getUser)

Chi: When you outgrow stdlib but want stdlib compatibility. Chi handlers are http.Handler, so you can mix with any stdlib middleware.

r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/users/{id}", getUser) // same http.HandlerFunc signature

Gin: When you want batteries-included (binding, validation, rendering) and don’t mind a custom context type.

r := gin.Default()
r.GET("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    c.JSON(200, gin.H{"id": id})
})

Tip: Start with stdlib. Move to Chi if you need route groups and middleware chaining. Move to Gin/Echo only if you need their specific features (validation binding, SSE helpers, etc.).

CLI

Cobra is the standard for CLI apps. Used by kubectl, docker, gh, and hugo.

import "github.com/spf13/cobra"

var rootCmd = &cobra.Command{
    Use:   "myapp",
    Short: "Does things",
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("hello")
    },
}

func main() {
    rootCmd.Execute()
}
LibraryUse case
CobraFull CLI apps with subcommands, flags, completions
urfave/cliSimpler alternative to Cobra
flag (stdlib)Single-command tools with flags
Bubble TeaTerminal UIs (interactive prompts, TUIs)

Database

LibraryTypeBest for
database/sql (stdlib)Raw SQLSimple queries, full control
sqlxSQL + scanningStruct scanning, named params
pgxPostgreSQL driverBest Postgres performance
GORMORMRapid prototyping, ActiveRecord style
sqlcCode gen from SQLType-safe queries from SQL files
gooseMigrationsSchema versioning

sqlx is the sweet spot for most projects - raw SQL with convenient struct scanning:

import "github.com/jmoiron/sqlx"

type User struct {
    ID    int    `db:"id"`
    Name  string `db:"name"`
    Email string `db:"email"`
}

var users []User
err := db.SelectContext(ctx, &users,
    "SELECT id, name, email FROM users WHERE active = ?", true)

sqlc generates type-safe Go code from SQL queries:

-- query.sql
-- name: GetUser :one
SELECT id, name, email FROM users WHERE id = $1;
sqlc generate
# generates: func (q *Queries) GetUser(ctx context.Context, id int) (User, error)

HTTP Client

stdlib net/http works for most use cases:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.example.com/data", nil)
req.Header.Set("Authorization", "Bearer "+token)

resp, err := http.DefaultClient.Do(req)
if err != nil {
    return err
}
defer resp.Body.Close()

var result APIResponse
json.NewDecoder(resp.Body).Decode(&result)

For a fluent API with retries, use resty:

import "github.com/go-resty/resty/v2"

client := resty.New().
    SetBaseURL("https://api.example.com").
    SetHeader("Authorization", "Bearer "+token).
    SetRetryCount(3)

var result APIResponse
resp, err := client.R().
    SetResult(&result).
    Get("/data")

Other Essential Libraries

CategoryLibraryWhat it does
ConfigviperRead config from files, env vars, flags
Loggingslog (stdlib)Structured logging (Go 1.21+)
LoggingzerologZero-allocation JSON logger
ValidationvalidatorStruct validation with tags
UUIDgoogle/uuidUUID generation
Cronrobfig/cronScheduled tasks
WebSocketgorilla/websocketWebSocket connections
gRPCgrpc-gogRPC server and client

Decision Table

NeedRecommendation
REST API, smallstdlib net/http
REST API, mediumChi or stdlib + middleware
REST API, large teamGin or Echo
CLI toolCobra
PostgreSQLpgx + sqlc
Any SQL databasesqlx
Rapid prototype with DBGORM
Config managementviper
Structured loggingslog (stdlib)

Go vs Rust

Both are popular for backend services and CLI tools. Here is how they compare:

AspectGoRust
Learning curveDays to productiveWeeks to months
Compile timeSecondsMinutes
Memory safetyGC (garbage collector)Ownership system, no GC
ConcurrencyGoroutines + channelsasync/await + tokio
Error handlingif err != nilResult<T, E> + ?
Binary size5-15 MB typical1-5 MB typical
PerformanceFast (within 2-3x of C)Very fast (near C)
EcosystemMature, broadGrowing, deep
Best forAPIs, DevOps, cloud infraSystems, WASM, perf-critical
Used byDocker, K8s, TerraformRipgrep, fd, Deno, SWC

Choose Go when: You want fast development, easy deployment, and your team needs to be productive quickly. Great for microservices, APIs, and DevOps tooling.

Choose Rust when: You need maximum performance, minimal memory usage, or are targeting embedded/WASM. Worth the investment for long-lived, performance-critical systems.

Tip: They complement each other. Many projects use Go for services and Rust for performance-critical libraries (called via FFI or as separate binaries).

Next: Generics | Setup