The packages everyone actually uses
The well-known third-party libraries, what each is for, and when the standard library is still the better choice.
Topic 50 of 98 · 10 min to read · 50 min to build
After this topic you can
- Explain when the standard library is a better choice than a popular third-party package.
- Judge a package before adding it, using maintenance, licence and dependency count.
- Name the usual library for routing, databases, config, logging and testing.
Why this topic exists
Go has an unusually strong standard library, so for most problems the advice is use the stdlib. That is why the three projects ahead in this track need only four dependencies.
But you will read other people's code, join teams with existing choices, and hit problems the stdlib does not solve. This is the map: what the well-known packages are, what each one is for, and, where one exists, the stdlib alternative to try first.
How to judge a package before you add it
terminal — the checks worth running before you commit a new dependency
go get example.com/thinggo mod graph | wc -l # how much did that just pull in?go mod why -m example.com/thinggo run golang.org/x/vuln/cmd/govulncheck@latest ./...go doc example.com/thing # read the API before you build on it- Is it still alive? Look at the last commit and the open issue count on GitHub, not the star count. Stars measure a blog post from 2019.
- What does it drag in? Run
go mod graphafter adding it. A logging library that pulls in forty modules is a supply-chain decision, not a convenience. - Could you write it in an afternoon? If yes, and it is not security-critical, write it. Every dependency is code you must keep updating.
- Does it wrap something you should understand? An ORM you adopt before you know SQL tends to cost more than it saves.
- Check `pkg.go.dev` for the docs and the import count, and run `govulncheck` after adding anything.
Web frameworks and routing
| Package | What it is | Try the stdlib first |
|---|---|---|
net/http | The standard library. Since Go 1.22 it routes on method and path parameters. | — this *is* the answer for most services |
chi | A thin router on top of net/http. Middleware chaining, route groups, sub-routers. | http.ServeMux plus your own middleware |
gin | The most popular full framework. Its own context type, binding, validation. | net/http: gin handlers take *gin.Context, not the standard handler signature |
echo | Similar to gin, slightly cleaner API. | net/http |
fiber | Express-style API built on fasthttp, not net/http. | net/http: fiber cannot use standard middleware directly |
gorilla/mux | The classic router. Was unmaintained for a while, now revived. | http.ServeMux does this in 1.22+ |
Note: On a new service today, start with
net/http. Reach for chi when you want route groups and middleware helpers without leaving the standard interfaces. Reach for gin or echo when the team already uses them.Databases
| Package | What it is | When it is the right call |
|---|---|---|
database/sql | Standard library. The common interface SQL drivers implement. | the default; sqlx, GORM and most drivers plug into it |
pgx | The Postgres driver. Used directly, or through database/sql. | any Postgres project; this is the default |
go-sql-driver/mysql | The MySQL driver. | MySQL projects |
sqlx | A thin layer over database/sql that scans rows into structs. | when hand-writing Scan calls gets tedious |
sqlc | Generates type-safe Go from your SQL. You keep writing SQL. | you want typed Go and still want to write the SQL |
ent | A schema-as-code entity framework, first built at Facebook. Powerful, opinionated. | complex relational graphs |
GORM | A full ORM. Auto-migration, hooks, associations. | CRUD-heavy apps where SQL control matters less |
golang-migrate / goose | Schema migration runners. | every project with a database |
go-redis | The Redis client. | caching, rate limiting, queues |
Configuration and CLIs
| Package | What it is | Try first |
|---|---|---|
os.Getenv + flag | Standard library. Environment variables and command-line flags. | — enough for most services |
godotenv | Loads a .env file in development. | used in this track; small, with no dependencies of its own |
viper | Config from files, env, flags and remote stores, merged together. | when you need layered config; it pulls in many dependencies |
kelseyhightower/envconfig | Fills a struct from environment variables using tags. | a light middle ground |
cobra | The CLI framework behind kubectl, hugo and gh. Subcommands, help, completion. | multi-command CLIs |
urfave/cli | A lighter CLI framework. | simpler CLIs |
Logging
| Package | What it is | Verdict |
|---|---|---|
log/slog | Standard library since Go 1.21. Structured, levelled, with handlers. | start here; this is what the track uses |
zap | Uber's logger. Very fast, more ceremony. | when profiling shows logging is your bottleneck |
zerolog | Zero-allocation JSON logger, chained API. | similar case to zap |
logrus | The original structured logger. In maintenance mode. | you will meet it in old code; do not start with it |
Testing
| Package | What it is | Note |
|---|---|---|
testing | Standard library. Tests, subtests, benchmarks, fuzzing. | no framework needed; see the Testing topic |
testify | Assertions (assert, require) plus mocks and suites. | the most common add-on; require.NoError is the draw |
google/go-cmp | Deep comparison built for tests, with readable diffs. | better than reflect.DeepEqual for structs |
testcontainers-go | Starts real Postgres, Redis or Kafka in Docker for a test. | testing a repository layer against a real database |
ory/dockertest | Lighter alternative to testcontainers. | same job, fewer features |
uber-go/mock | Generated mocks from interfaces (successor to golang/mock). | hand-written fakes are usually smaller |
goleak | Fails a test if goroutines are still running at the end. | catches leaks that no other test would notice |
Watch out: The one to think twice about is
testify's mock and suite packages. Assertions are fine and save real typing. Generated mocks and test suites tend to hide what a test is doing. Go interfaces are small and implicit, so a hand-written fake is usually shorter than the mock setup.Concurrency helpers
| Package | What it is | Why it matters |
|---|---|---|
sync, context | Standard library. Mutex, WaitGroup, Once, cancellation. | covers most of what you need |
golang.org/x/sync/errgroup | Run goroutines, collect the first error, cancel the rest. SetLimit bounds concurrency. | the one worth adding; see the Concurrency topics |
golang.org/x/sync/singleflight | Collapses duplicate concurrent calls into one. | cache stampede protection in ~5 lines |
golang.org/x/sync/semaphore | A weighted semaphore. | when a plain channel-as-semaphore is not enough |
golang.org/x/time/rate | Token-bucket rate limiter. | used in this track for rate limiting |
sourcegraph/conc | Structured concurrency helpers with panic handling built in. | newer; nice, but errgroup covers most cases |
Everything else you will meet
| Package | What it is for |
|---|---|
google/uuid | UUID generation. The near-universal choice. |
shopspring/decimal | Exact decimal arithmetic for money, when integer cents is not enough. |
go-playground/validator | Struct validation driven by tags. Common with gin. |
golang-jwt/jwt | JSON Web Tokens. Used in this track. |
golang.org/x/crypto/bcrypt | Password hashing. Used in this track. |
golang.org/x/oauth2 | OAuth2 clients for Google, GitHub and friends. |
prometheus/client_golang | Metrics: counters, histograms, the /metrics endpoint. |
open-telemetry/opentelemetry-go | Traces and metrics across services. |
grpc/grpc-go + protobuf | gRPC services and protocol buffers. |
segmentio/kafka-go, IBM/sarama | Kafka clients. |
nats-io/nats.go | NATS messaging. |
hibiken/asynq | Background job queue backed by Redis. |
uber-go/automaxprocs | Sets GOMAXPROCS from the container CPU limit. Not needed from Go 1.25, which does this itself on Linux. |
samber/lo | Lodash-style generic helpers. Check slices and maps first. |
spf13/afero | A filesystem interface, so you can fake the disk in tests. |
swaggo/swag, oapi-codegen | OpenAPI docs from comments, or Go from an OpenAPI spec. |
Tools you install, rather than import
terminal
# linting and safetygo install honnef.co/go/tools/cmd/staticcheck@latest # the best-value lintergo install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latestgo install golang.org/x/vuln/cmd/govulncheck@latest # official, low noise# developmentgo install github.com/air-verse/air@latest # live reloadgo install github.com/go-delve/delve/cmd/dlv@latest # the debuggergo install golang.org/x/perf/cmd/benchstat@latest # honest benchmark comparisongo install github.com/rakyll/hey@latest # simple load testing# databasego install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latestgo install github.com/sqlc-dev/sqlc/cmd/sqlc@latestWhat a typical production service actually imports
A realistic dependency list for a company's Go API is short, like this one.
go.mod — a normal, boring, production service
require ( github.com/go-chi/chi/v5 v5.2.3 // routing niceties github.com/jackc/pgx/v5 v5.7.5 // Postgres github.com/golang-jwt/jwt/v5 v5.3.0 // tokens github.com/google/uuid v1.6.0 // ids github.com/prometheus/client_golang v1.23.2 // metrics golang.org/x/crypto v0.43.0 // bcrypt golang.org/x/sync v0.17.0 // errgroup)// Everything else — HTTP, JSON, SQL interface, logging, context,// testing, templates, crypto — comes with Go.Worth knowing: If your
go.mod has 200 lines, that is worth a look. Go culture leans hard towards fewer dependencies, and a reviewer may ask why each one is there. "Because the standard library already does it" is a common reason to remove one.The short version
- Start with the standard library. It covers HTTP, SQL, JSON, logging, testing and crypto.
net/httproutes on method and path since 1.22, so a web framework is now a preference, not a need.- The few genuinely worth adding: a database driver,
errgroup,bcrypt, a JWT library, and metrics. - Judge a package by its last commit and what it drags in — not by its star count.
Remember: Every dependency is code you have to update, audit and explain for as long as you keep it. The stdlib rarely needs justifying.
Build this before moving on
Open the go.mod of a Go project you did not write and look up every dependency on pkg.go.dev. For each one, decide whether the standard library could have done it.Then try these in your own editor
- 1Add
ginto a scratch project, rungo mod graph | wc -l, then do the same forchi. Compare. - 2Rewrite one
reflect.DeepEqualtest assertion withgoogle/go-cmpand compare the failure output. - 3Replace a hand-rolled worker pool with
errgroup.SetLimitand count the lines removed. - 4Run
govulncheck ./...on any project you have and read what it reports.
Official documentation