winterjung blog


Go Package Recommendations for Production Server Development

I started using Go at my previous job and have continued using it at my current job — about five years of active use in total. Compared to the early days, the Go ecosystem has grown increasingly rich: communities like GopherCon Korea have emerged, and reference material keeps expanding. That said, well-organized Korean-language resources are still relatively scarce (not that Go is unique in this regard), and there’s still a fair amount of trial and error involved. To share what I’ve learned alongside colleagues over the years, I previously wrote up our trial and error and best practices in two posts: Golang and gRPC in Production and Banksalad Go Coding Conventions. In a similar vein, this post is a roundup of the packageslibraries I’ve found most useful and frequently reached for while doing server development in Go. Go’s standard library certainly has plenty of reliable, well-crafted packages — net/http/httptest and crypto/rand come to mind — but this post focuses on third-party packages.

Every time I write something like this, I second-guess myself: “Anyone could find this by just searching ‘golang xxx package’ — is there even a point?” Still, I hope this saves someone the decision-making overhead and the guesswork, and that the responses from readers help make it even better over time.

Update: In the 2025 Go Developer Survey, 26% of respondents said “finding reliable Go modules and packages” was one of the biggest challenges they face as Go developers — so hopefully this post pulls its weight.

stretchr/testify#

func TestSomething(t *testing.T) {
  assert.Equal(t, expected, got, "they should be equal")
  assert.NoError(t, err)
  assert.Len(t, result, 1)
  assert.JSONEq(t, `{"name": "hello"}`, msg)
}

A toolkit with common assertions and mocks that plays nicely with the standard library

rs/zerolog & sirupsen/logrus#

import (
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
	"github.com/rs/zerolog/pkgerrors"
)

func main() {
	zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
	// ...
	log.Error().Stack().Err(err).Msg("failed to insert row to db")
}

rs/zerolog: Zero Allocation JSON Logger

import (
	log "github.com/sirupsen/logrus"
)

func main() {
	log.WithFields(log.Fields{
		"animal": "walrus",
	}).Info("A walrus appears")
}

sirupsen/logrus: Structured, pluggable logging for Go.

pkg/errors#

resp, err := userSvc.GetUser(ctx, &GetUserRequest{...})
if err != nil {
	return errors.Wrap(err, "user.GetUser")
}

Simple error handling primitives

hashicorp/go-multierror#

var errs *multierror.Error
if err := step1(); err != nil {
	errs = multierror.Append(errs, err)
}
if err := step2(); err != nil {
	errs = multierror.Append(errs, err)
}
return errs.ErrorOrNil()
// Output:
// 2 errors occurred:
//	* error 1
//	* error 2

A Go (golang) package for representing a list of errors as a single error.

samber/lo#

names := lo.Uniq([]string{"Samuel", "John", "Samuel"})
// []string{"Samuel", "John"}

A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find…)

shopspring/decimal#

func main() {
	price, err := decimal.NewFromString("136.02")
	quantity := decimal.NewFromInt(3)
	subtotal := price.Mul(quantity)

	fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06
}

Arbitrary-precision fixed-point decimal numbers in Go

dgraph-io/ristretto#

func main() {
	cache, _ := ristretto.NewCache(&ristretto.Config[string,string]{
		NumCounters: 1e7,     // number of keys to track frequency of (10M).
		MaxCost:     1 << 30, // maximum cost of cache (1GB).
		BufferItems: 64,      // number of keys per Get buffer.
	})
	cache.Set("key", "value", 1)
	value, found := cache.Get("key")
	fmt.Println(value)
}

A high performance memory-bound Go cache

coocood/freecache#

cacheSize := 100 * 1024 * 1024 // 100MB
cache := freecache.NewCache(cacheSize)

cache.Set([]byte("key"), []byte("value"), 60) // 60 seconds TTL
value, err := cache.Get([]byte("key"))

A cache library for Go with zero GC overhead

volatiletech/sqlboiler#

import (
	"github.com/volatiletech/sqlboiler/v4"
)
func main() {
	users, err := model.Users().All(ctx, db)
	token, err := model.Tokens(model.TokenWhere.AccessToken.EQ(accessToken)).One(ctx, db)
}
	token.Update(ctx, db, boil.Whitelist(
        model.TokenColumns.AccessToken,
        model.TokenColumns.AccessTokenExpiredAt,
    ))

Generate a Go ORM tailored to your database schema.

DATA-DOG/go-sqlmock#

func TestShouldUpdateStats(t *testing.T) {
	db, mockDB, err := sqlmock.New()
	require.NoError(t, err)
	t.Cleanup(db.Close)

	mockDB.ExpectQuery(regexp.QuoteMeta(
		"SELECT * FROM `token` WHERE (`user_id` = ?);"
	)).WithArgs("some-valid-user-id").WillReturnRows(...)
}

Sql mock driver for golang to test database interactions

golangci/golangci-lint & mvdan/gofumpt#

# .golangci.yml
linters:
  enable:
    - govet
    - errcheck
    - staticcheck

formatters:
  enable:
    - gofumpt
  settings:
    gofumpt:
      extra-rules: true

golangci-lint: Fast linters runner for Go / gofumpt: A stricter gofmt

failsafe-go/failsafe-go#

retryPolicy := retrypolicy.NewBuilder[*http.Response]().
	HandleIf(func(r *http.Response, err error) bool {
		return r != nil && r.StatusCode == http.StatusTooManyRequests
	}).
	WithBackoff(time.Second, 30*time.Second).
	WithMaxRetries(3).
	Build()

resp, err := failsafe.With(retryPolicy).Get(func() (*http.Response, error) {
	return http.Get("https://example.com")
})

Fault tolerance and resilience patterns for Go

hashicorp/go-retryablehttp#

client := retryablehttp.NewClient()
client.RetryMax = 3

resp, err := client.Get("https://example.com/api")

Retryable HTTP client in Go

twmb/franz-go#

// producer
client, _ := kgo.NewClient(kgo.SeedBrokers("localhost:9092"))
defer client.Close()

ctx := context.Background()
client.Produce(ctx, &kgo.Record{Topic: "my-topic", Value: []byte("hello")}, nil)

// consumer
client, _ := kgo.NewClient(
	kgo.SeedBrokers("localhost:9092"),
	kgo.ConsumeTopics("my-topic"),
	kgo.ConsumerGroup("my-group"),
)
for {
	fetches := client.PollFetches(ctx)
	fetches.EachRecord(func(r *kgo.Record) {
		fmt.Println(string(r.Value))
	})
}

franz-go is a feature complete, pure Go library for Kafka from 0.8.0 through 4.1+. Producing, consuming, transacting, administrating, etc.

go-resty/resty & dghubble/sling#

// resty
client := resty.New()
resp, err := client.R().
	SetHeader("Accept", "application/json").
	SetResult(&ApiResponse{}).
	Get("https://api.example.com/users/1")

// sling
type Issue struct {
	Title string `json:"title"`
	Body  string `json:"body"`
}
issue := new(Issue)
resp, err := sling.New().Base("https://api.github.com/").
	Path("repos/user/repo/").
	Get("issues/1").
	ReceiveSuccess(issue)

go-resty: Simple HTTP, REST, and SSE client library for Go / dghubble/sling: A Go HTTP client library for creating and sending API requests

go-chi/chi#

r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)

r.Get("/", func(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("hello"))
})
r.Route("/users", func(r chi.Router) {
	r.Get("/", listUsers)
	r.Post("/", createUser)
	r.Get("/{userID}", getUser)
})

http.ListenAndServe(":3000", r)

lightweight, idiomatic and composable router for building Go HTTP services

Miscellaneous#


That wraps up my collection of frequently used and genuinely useful packages. The list skews toward server-side development, and I’ve left out highly niche packages (e.g., one for computing Levenshtein distance). If there are other packages worth recommending, I’ll keep adding to this list over time.

Other Resources Worth Checking Out#