Files
my-vault/00_Inbox/Clippings/2024/07/The Go libraries that never failed us 22 libraries you need to know.md
T

937 lines
58 KiB
Markdown
Raw Normal View History

2026-01-05 13:03:55 +08:00
---
page-title: "The Go libraries that never failed us: 22 libraries you need to know"
url: https://threedots.tech/post/list-of-recommended-libraries/
date: "2024-07-23 08:01:03"
---
Did you have a situation when you lost a ton of time finding a Go library for your need? In theory, you can check lists like [Awesome Go](https://github.com/avelino/awesome-go) or make a choice based on GitHub stars. But Awesome Go contains over 2600 libraries, and popularity is not always the best indicator of library quality. **I often thought that it would be great to have a place where I could find just the best and battle-tested libraries I could use in my project.** Because we didnt find such a place with Miłosz, we decided to create it.
![Frankenstein Gopher](https://threedots.tech/post/list-of-recommended-libraries/library-gopher.svg)
Based on our experience leading multiple Go teams and working on various projects, including complex financial, health, and security, we will recommend tools that could work well for different projects.
In addition to providing a list of libraries, we also want to show you some non-obvious uses for those tools and libraries. However, its important to note that most of these tools can be misused. Weve included some common anti-patterns to help you avoid making those mistakes.
This list is intended to be opinionated. **We only wanted to include libraries we used on real production systems. Thanks to that, we recommend just libraries that we are 100% sure about.** Unfortunately, our day is limited to 24 hours, so checking all available libraries is impossible.
**If you know of any libraries we should include on this list, please let us know in the comments!** We will continue to update the list with new findings over time.
Table of Contents
1. [HTTP](https://threedots.tech/post/list-of-recommended-libraries/#http)
1. [Routers](https://threedots.tech/post/list-of-recommended-libraries/#routers)
1. [Echo](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-echo-githubhttpsgithubcomlabstackecho-docshttpsecholabstackcomguide-exampleshttpsecholabstackcomcookbook) [\[GitHub\]](https://github.com/labstack/echo) [\[Docs\]](https://echo.labstack.com/guide/) [\[Examples\]](https://echo.labstack.com/cookbook/)
2. [chi](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-chi-githubhttpsgithubcomgo-chichi-docshttpspkggodevgithubcomgo-chichi-exampleshttpsgithubcomgo-chichitreemaster_examples) [\[GitHub\]](https://github.com/go-chi/chi) [\[Docs\]](https://pkg.go.dev/github.com/go-chi/chi) [\[Examples\]](https://github.com/go-chi/chi/tree/master/_examples)
2. [Middlewares](https://threedots.tech/post/list-of-recommended-libraries/#middlewares)
3. [Serving static content](https://threedots.tech/post/list-of-recommended-libraries/#serving-static-content)
4. [OpenAPI](https://threedots.tech/post/list-of-recommended-libraries/#openapi)
5. [Generating Go server and clients](https://threedots.tech/post/list-of-recommended-libraries/#generating-go-server-and-clients)
1. [deepmap/oapi-codegen](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-deepmapoapi-codegen-githubhttpsgithubcomdeepmapoapi-codegen-docshttpsgithubcomdeepmapoapi-codegenreadme-examplehttpsthreedotstechpostserverless-cloud-run-firebase-modern-go-applicationpublic-http-api) [\[GitHub\]](https://github.com/deepmap/oapi-codegen) [\[Docs\]](https://github.com/deepmap/oapi-codegen#readme) [\[Example\]](https://threedots.tech/post/serverless-cloud-run-firebase-modern-go-application/#public-http-api)
6. [Bonus: Client for JavaScript/TypeScript](https://threedots.tech/post/list-of-recommended-libraries/#bonus-client-for-javascripttypescript)
1. [openapi-generator-cli](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-openapi-generator-cli-githubhttpsgithubcomopenapitoolsopenapi-generator-cli-docshttpsgithubcomopenapitoolsopenapi-generator-clireadme) [\[GitHub\]](https://github.com/OpenAPITools/openapi-generator-cli) [\[Docs\]](https://github.com/OpenAPITools/openapi-generator-cli#readme)
2. [Alternative types of communication](https://threedots.tech/post/list-of-recommended-libraries/#alternative-types-of-communication)
1. [gRPC](https://threedots.tech/post/list-of-recommended-libraries/#grpc)
1. [protoc](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-protoc-docshttpsgrpciodocs) [\[Docs\]](https://grpc.io/docs/)
2. [Messaging](https://threedots.tech/post/list-of-recommended-libraries/#messaging)
1. [Watermill](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-watermill-githubhttpsgithubcomthreedotslabswatermill-docshttpswatermillio-exampleshttpsgithubcomthreedotslabswatermilltreemaster_examples) [\[GitHub\]](https://github.com/ThreeDotsLabs/watermill) [\[Docs\]](https://watermill.io/) [\[Examples\]](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples)
3. [Database](https://threedots.tech/post/list-of-recommended-libraries/#database)
1. [SQL](https://threedots.tech/post/list-of-recommended-libraries/#sql)
1. [sqlx](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-sqlx-githubhttpsgithubcomjmoironsqlx-docshttpjmoirongithubiosqlx) [\[GitHub\]](https://github.com/jmoiron/sqlx) [\[Docs\]](http://jmoiron.github.io/sqlx/)
2. [SQLBoiler](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-sqlboiler-githubhttpsgithubcomvolatiletechsqlboiler-docshttpsgithubcomvolatiletechsqlboilertable-of-contents-exampleshttpsgithubcomvolatiletechsqlboilerfeatures--examples) [\[GitHub\]](https://github.com/volatiletech/sqlboiler) [\[Docs\]](https://github.com/volatiletech/sqlboiler#table-of-contents) [\[Examples\]](https://github.com/volatiletech/sqlboiler#features--examples)
2. [Migrations](https://threedots.tech/post/list-of-recommended-libraries/#migrations)
1. [sql-migrate](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-sql-migrate-githubhttpsgithubcomrubenvsql-migrate-docshttpsgithubcomrubenvsql-migratereadme) [\[GitHub\]](https://github.com/rubenv/sql-migrate) [\[Docs\]](https://github.com/rubenv/sql-migrate#readme)
2. [goose](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-goose-githubhttpsgithubcompresslygoose-docshttpspkggodevgithubcompresslygoose) [\[GitHub\]](https://github.com/pressly/goose) [\[Docs\]](https://pkg.go.dev/github.com/pressly/goose)
4. [Observability](https://threedots.tech/post/list-of-recommended-libraries/#observability)
1. [Logging](https://threedots.tech/post/list-of-recommended-libraries/#logging)
1. [Logrus](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-logrus-githubhttpsgithubcomsirupsenlogrus-docshttpspkggodevgithubcomsirupsenlogrus) [\[GitHub\]](https://github.com/sirupsen/logrus) [\[Docs\]](https://pkg.go.dev/github.com/sirupsen/logrus)
2. [zap](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-zap-githubhttpsgithubcomuber-gozap-docshttppkggodevgithubcomuber-gozap) [\[GitHub\]](https://github.com/uber-go/zap) [\[Docs\]](http://pkg.go.dev/github.com/uber-go/zap)
2. [Metrics and tracing](https://threedots.tech/post/list-of-recommended-libraries/#metrics-and-tracing)
1. [opencensus-go](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-opencensus-go-githubhttpsgithubcomcensus-instrumentationopencensus-go-docshttpsopencensusio) [\[GitHub\]](https://github.com/census-instrumentation/opencensus-go) [\[Docs\]](https://opencensus.io/)
5. [Configuration](https://threedots.tech/post/list-of-recommended-libraries/#configuration)
1. [Env variables](https://threedots.tech/post/list-of-recommended-libraries/#env-variables)
1. [caarlos0/env](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-caarlos0env-githubhttpsgithubcomcaarlos0env-docshttpspkggodevgithubcomcaarlos0env) [\[GitHub\]](https://github.com/caarlos0/env) [\[Docs\]](https://pkg.go.dev/github.com/caarlos0/env)
2. [Multi-format configuration](https://threedots.tech/post/list-of-recommended-libraries/#multi-format-configuration)
3. [koanf](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-koanf-githubhttpsgithubcomknadhkoanf-docshttpspkggodevgithubcomknadhkoanf) [\[GitHub\]](https://github.com/knadh/koanf) [\[Docs\]](https://pkg.go.dev/github.com/knadh/koanf)
6. [Building CLI](https://threedots.tech/post/list-of-recommended-libraries/#building-cli)
1. [Building CLI libraries](https://threedots.tech/post/list-of-recommended-libraries/#building-cli-libraries)
1. [urfave/cli](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-urfavecli-githubhttpsgithubcomurfavecli-docshttpscliurfaveorg-exampleshttpscliurfaveorgv2examplesgreet) [\[GitHub\]](https://github.com/urfave/cli/) [\[Docs\]](https://cli.urfave.org/) [\[Examples\]](https://cli.urfave.org/v2/examples/greet/)
7. [Testing](https://threedots.tech/post/list-of-recommended-libraries/#testing)
1. [Assertions](https://threedots.tech/post/list-of-recommended-libraries/#assertions)
1. [testify](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-testify-githubhttpsgithubcomstretchrtestify-docshttpspkggodevgithubcomstretchrtestify) [\[GitHub\]](https://github.com/stretchr/testify) [\[Docs\]](https://pkg.go.dev/github.com/stretchr/testify)
2. [go-cmp](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-go-cmp-githubhttpsgithubcomgooglego-cmp-docshttpspkggodevgithubcomgooglego-cmp-examples-1httpsgithubcomgooglego-cmpblobmastercmpexample_testgo-examples-2httpsgithubcomgooglego-cmpblobmastercmpcmpoptsexample_testgo) [\[GitHub\]](https://github.com/google/go-cmp) [\[Docs\]](https://pkg.go.dev/github.com/google/go-cmp) [\[Examples 1\]](https://github.com/google/go-cmp/blob/master/cmp/example_test.go) [\[Examples 2\]](https://github.com/google/go-cmp/blob/master/cmp/cmpopts/example_test.go)
3. [gofakeit](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-gofakeit-githubhttpsgithubcombrianvoegofakeit-docshttpspkggodevgithubcombrianvoegofakeit) [\[GitHub\]](https://github.com/brianvoe/gofakeit) [\[Docs\]](https://pkg.go.dev/github.com/brianvoe/gofakeit)
2. [Mocking](https://threedots.tech/post/list-of-recommended-libraries/#mocking)
1. [Writing mocks by hand](https://threedots.tech/post/list-of-recommended-libraries/#writing-mocks-by-hand)
8. [Misc](https://threedots.tech/post/list-of-recommended-libraries/#misc)
1. [Extra types support](https://threedots.tech/post/list-of-recommended-libraries/#extra-types-support)
1. [google/uuid](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-googleuuid-githubhttpsgithubcomgoogleuuid-docshttpspkggodevgithubcomgoogleuuid) [\[GitHub\]](https://github.com/google/uuid) [\[Docs\]](https://pkg.go.dev/github.com/google/uuid)
2. [oklog/ulid](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-oklogulid-githubhttpsgithubcomoklogulid-docshttpspkggodevgithubcomoklogulid) [\[GitHub\]](https://github.com/oklog/ulid) [\[Docs\]](https://pkg.go.dev/github.com/oklog/ulid)
3. [shopspring/decimal](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-shopspringdecimal-githubhttpsgithubcomshopspringdecimal-docshttpspkggodevgithubcomshopspringdecimal) [\[GitHub\]](https://github.com/shopspring/decimal) [\[Docs\]](https://pkg.go.dev/github.com/shopspring/decimal)
2. [Errors](https://threedots.tech/post/list-of-recommended-libraries/#errors)
1. [hashicorp/go-multierror](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-hashicorpgo-multierror-githubhttpsgithubcomhashicorpgo-multierror-docsgithubcomhashicorpgo-multierror) [\[GitHub\]](https://github.com/hashicorp/go-multierror) [\[Docs\]](https://threedots.tech/post/list-of-recommended-libraries/github.com/hashicorp/go-multierror)
9. [Useful tools](https://threedots.tech/post/list-of-recommended-libraries/#useful-tools)
1. [Misc](https://threedots.tech/post/list-of-recommended-libraries/#misc-1)
1. [samber/lo](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-samberlo-githubhttpsgithubcomsamberlo-docshttpspkggodevgithubcomsamberlo) [\[GitHub\]](https://github.com/samber/lo) [\[Docs\]](https://pkg.go.dev/github.com/samber/lo)
2. [Task](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-task-githubhttpsgithubcomgo-tasktask-docshttpstaskfiledev) [\[GitHub\]](https://github.com/go-task/task) [\[Docs\]](https://taskfile.dev/)
2. [Live code reloading](https://threedots.tech/post/list-of-recommended-libraries/#live-code-reloading)
1. [reflex](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-reflex-githubhttpsgithubcomcesparereflex-docshttpspkggodevgithubcomcesparereflex-examplehttpsthreedotstechpostgo-docker-dev-environment-with-go-modules-and-live-code-reloading) [\[GitHub\]](https://github.com/cespare/reflex) [\[Docs\]](https://pkg.go.dev/github.com/cespare/reflex) \[[Example](https://threedots.tech/post/go-docker-dev-environment-with-go-modules-and-live-code-reloading/)\]
3. [Linter](https://threedots.tech/post/list-of-recommended-libraries/#linter)
1. [golangci-lint](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-golangci-lint-githubhttpsgithubcomgolangcigolangci-lint-docshttpsgolangci-lintrun) [\[GitHub\]](https://github.com/golangci/golangci-lint) [\[Docs\]](https://golangci-lint.run/)
2. [go-cleanarch](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-go-cleanarch-githubhttpsgithubcomroblaszczakgo-cleanarch-docshttpspkggodevgithubcomroblaszczakgo-cleanarchsection-readme) [\[GitHub\]](https://github.com/roblaszczak/go-cleanarch) [\[Docs\]](https://pkg.go.dev/github.com/roblaszczak/go-cleanarch#section-readme)
4. [Formatters](https://threedots.tech/post/list-of-recommended-libraries/#formatters)
1. [go fmt](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-go-fmt)
2. [goimports](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-goimports-docshttpspkggodevgolangorgxtoolscmdgoimports) [\[Docs\]](https://pkg.go.dev/golang.org/x/tools/cmd/goimports)
3. [gofumpt](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-gofumpt-githubhttpsgithubcommvdangofumpt-docshttpspkggodevmvdanccgofumptsection-readme) [\[GitHub\]](https://github.com/mvdan/gofumpt) [\[Docs\]](https://pkg.go.dev/mvdan.cc/gofumpt#section-readme)
10. [Example projects](https://threedots.tech/post/list-of-recommended-libraries/#example-projects)
1. [DDD & Clean Architecture](https://threedots.tech/post/list-of-recommended-libraries/#ddd--clean-architecture)
1. [Wild Workouts Go DDD Example application](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-wild-workouts-go-ddd-example-application-githubhttpsgithubcomthreedotslabswild-workouts-go-ddd-example) [\[GitHub\]](https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example)
2. [General purpose](https://threedots.tech/post/list-of-recommended-libraries/#general-purpose)
1. [Modern Go Application by Márk Sági-Kazár](https://threedots.tech/post/list-of-recommended-libraries/#white_check_mark-modern-go-application-by-m%C3%A1rk-s%C3%A1gi-kaz%C3%A1r-githubhttpsgithubcomsagikazarmarkmodern-go-application) [\[GitHub\]](https://github.com/sagikazarmark/modern-go-application)
11. [Summary](https://threedots.tech/post/list-of-recommended-libraries/#summary)
## HTTP
### Routers
As I mentioned in my [previous article](https://threedots.tech/post/best-go-framework/), its generally better to use libraries instead of frameworks for long-term projects. One of the most fundamental components of any service is an HTTP router. While its technically possible to build an application without one by using the standard librarys [http](https://pkg.go.dev/net/http) package, its routing capabilities are limited. Using a dedicated router will make your life much easier.
❌ Anti-pattern: Frameworks in Go
If the library you consider using impacts how you write your domain models, its probably a framework, not a router.
We recommend using lightweight routers instead. Learn more about the risks of using the framework in my [previous article](https://threedots.tech/post/best-go-framework/).
By design, router functionality is limited to routing requests to a proper handler. All non-standard functionalities like CORS, CSRF, error handling, HTTP logging, and authorization (that frameworks usually provide) are provided by reusable middlewares. I recommend some in the [section on middlewares](https://threedots.tech/post/list-of-recommended-libraries/#middlewares).
I use one of two router libraries in most projects: Echo or chi. Both of them are great routers, with different characteristics. They work perfectly with [OpenAPI](https://threedots.tech/post/list-of-recommended-libraries/#openapi) code generation.
#### ✅ Echo [\[GitHub\]](https://github.com/labstack/echo) [\[Docs\]](https://echo.labstack.com/guide/) [\[Examples\]](https://echo.labstack.com/cookbook/)
Compared to chi, Echo does offer a custom `*http.Request` handler signature. Some people may find it a downside, but I think it helps to write less error-prone HTTP handlers.
If you have been writing Go for a while, you probably made this mistake at least once:
```
func someHandler(w http.ResponseWriter, r *http.Request) {
err := foo()
if err != nil {
w.WriteHeader(http.StatusBadRequest)
// you forgot the return here, bar() will be executed
}
bar()
}
```
Echo makes you return an error:
```
func someHandler(c echo.Context) error {
err := foo()
if err != nil {
return err
}
bar()
return c.NoContent(http.StatusNoContent)
}
```
The advantage of Echo is the ability to define a custom [error handler](https://echo.labstack.com/guide/error-handling/). Its not possible to do it in the same way with chi.
For detailed usage and examples, please check Echo docs.
#### ✅ chi [\[GitHub\]](https://github.com/go-chi/chi) [\[Docs\]](https://pkg.go.dev/github.com/go-chi/chi) [\[Examples\]](https://github.com/go-chi/chi/tree/master/_examples)
Compared to Echo, chis handler functions are compatible with the standard library. For some people, it may be an upside; for some, it may be a downside you should make your own judgment.
What chi does better than Echo is the format of [defining routes and grouping](https://github.com/go-chi/chi/blob/0fe6bf1ba3ac601700b7993bc4c62f6c5f707932/_examples/rest/main.go#L83). It gives you better control over middleware per path or sub-path.
```
r.Route("/articles", func(r chi.Router) {
r.With(paginate).Get("/", ListArticles)
r.Post("/", CreateArticle) // POST /articles
r.Get("/search", SearchArticles) // GET /articles/search
r.Route("/{articleID}", func(r chi.Router) {
r.Use(ArticleCtx) // Load the *Article on the request context
r.Get("/", GetArticle) // GET /articles/123
r.Put("/", UpdateArticle) // PUT /articles/123
r.Delete("/", DeleteArticle) // DELETE /articles/123
})
// GET /articles/whats-up
r.With(ArticleCtx).Get("/{articleSlug:[a-z-]+}", GetArticle)
})
```
❌ Anti-pattern: You should not choose tools based just on benchmarks
Some developers choose libraries based on the benchmark results. Its a risky approach because extreme performance optimizations lead to worse API and limited functionalities set. In most cases, performance differences are negligible in real-life use cases.
Even if, for some applications, it may make a difference, for most applications, it doesnt matter that much. Making just one extra database query or up-scaling a service can make a much more significant difference in performance.
If performance is not absolutely critical for you, you should prefer other characteristics, like the ease of use and number of features.
### Middlewares
HTTP middlewares can give you functionalities like CORS, CSRF, error handling, HTTP logging, authorization, etc.
Echo and chi provide their set of middlewares:
- [Echo middlewares](https://echo.labstack.com/middleware/)
- [chi middlewares](https://github.com/go-chi/chi/tree/master/middleware)
Echo middlewares have a different interface, so they cant be used in chi. Generally speaking, all standard-library compatible middlewares are compatible with chi and echo.
To use standard library-compatible middleware with echo, you need to call `echo.WrapMiddleware`:
```
package main
import (
"github.com/go-chi/chi/v5/middleware"
"github.com/labstack/echo/v4"
)
// echo version
func main() {
e := echo.New()
// You can use a middleware from chi with echo.
e.Use(
echo.WrapMiddleware(middleware.BasicAuth("realm", map[string]string{
"admin": "password",
})),
)
e.Logger.Fatal(e.Start(":8080"))
}
```
If none of them provides the middleware you are looking for, you can check [the Awesome Go list](https://github.com/avelino/awesome-go#middlewares). All of them will be compatible with chi, Echo, and servers built just with the standard library. You can also write your own middleware. Check example middlewares for inspiration!
### Serving static content
You dont need any library to serve static content in Go. Since Go 1.16, you can easily [embed static files into your Go binary](https://pkg.go.dev/embed).
Heres how to do it for `Echo` and `chi`:
```
package main
import (
"embed"
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/v5"
"github.com/labstack/echo/v4"
)
// your static files should be in the static/ directory, for example static/index.html, static/main.js etc.
//
//go:embed static
var staticFs embed.FS
// chi version
func main() {
r := chi.NewRouter()
r.Handle("/static/*", http.StripPrefix("/", http.FileServer(http.FS(staticFs))))
log.Fatal(http.ListenAndServe(":8080", r))
}
// echo version
func main() {
e := echo.New()
e.GET("/static/*", echo.WrapHandler(http.StripPrefix("/", http.FileServer(http.FS(staticFs)))))
e.Logger.Fatal(e.Start(":8080"))
}
```
After running the server, assets will be available under `http://localhost:8080/static/index.html`, `http://localhost:8080/static/main.js` etc.
❌ Anti-pattern: Do not use no-name libraries for trivial functionalities
Do you remember the `leftpad` JavaScript library? It was 11 lines of code adding padding on the left side of a string.
Some day, the author decided to remove that library. It wouldnt be a big problem if it wasnt a dependency of thousands of projects, including Node and Babel.
Serving static content from your web server is one of such trivial functionalities.
[![Go In One Evening](https://threedots.tech/img/sidebar/course.svg)](https://threedots.tech/go-in-one-evening/?utm_source=blog-content)Are you experienced engineer who wants to learn Go basics?
You don't become an engineer by watching videos.
[Learn Go hands-on by building real projects.](https://threedots.tech/go-in-one-evening/?utm_source=blog-content)
### OpenAPI
Nobody likes to maintain API contracts manually. Its annoying and counterproductive to keep multiple boring JSONs up-to-date. OpenAPI solves this problem with a JavaScript HTTP client and Go HTTP server generated from the provided specification.
This is how an [example specification](https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/tree/a0a41253db96d46d75e7ff4c7e7f95848f47dcc3/api/openapi) looks like. If you didnt work with OpenAPI before, you can read more details in my [previous article](https://threedots.tech/post/serverless-cloud-run-firebase-modern-go-application/#openapi-swagger-client). Here, I focus on tools that we recommend for OpenAPI spec-generated code.
### Generating Go server and clients
We do not recommend using the official OpenAPI generator for the Go code. We recommend the `oapi-codegen` tool instead because of the higher quality of the generated code. It also has more functionalities.
❌ Anti-pattern: Don't try to generate OpenAPI spec from Go code
There are tools that can generate an OpenAPI spec from Go code. We dont recommend using them.
The entire OpenAPI specification is very rich, and it will be hard to generate everything from Go code. Its likely that you will need to add something to the OpenAPI spec at some point, and it may be impossible to do it from the Go code.
Its much easier to generate it the other way around: Go code from OpenAPI spec.
#### ✅ deepmap/oapi-codegen [\[GitHub\]](https://github.com/deepmap/oapi-codegen) [\[Docs\]](https://github.com/deepmap/oapi-codegen#readme) [\[Example\]](https://threedots.tech/post/serverless-cloud-run-firebase-modern-go-application/#public-http-api)
`oapi-codegen` is a great tool that doesnt just generate models but also the entire [router](https://threedots.tech/post/list-of-recommended-libraries/#routers) definition, headers validation, and proper parameters parsing. It works with [chi](https://threedots.tech/post/list-of-recommended-libraries/#chi) and [Echo](https://threedots.tech/post/list-of-recommended-libraries/#echo).
To generate a server, run the following:
```
oapi-codegen -generate types -o "<OUTPUT DIR>/openapi_types.gen.go" -package "<GO PACKAGE>" "api/openapi/service.yml"
oapi-codegen -generate <TYPE> -o "<OUTPUT DIR>/openapi_api.gen.go" -package "<GO PACKAGE>" "api/openapi/service.yml"
```
Where `<TYPE>` for `chi` should be `chi-server`, and for `Echo` just `server`.
To generate clients:
```
oapi-codegen -generate types -o "<OUTPUT DIR>/$service/openapi_types.gen.go" -package "<GO PACKAGE>" "api/openapi/service.yml"
oapi-codegen -generate client -o "<OUTPUT DIR>/$service/openapi_client_gen.go" -package "<GO PACKAGE>" "api/openapi/service.yml"
```
Dont forget to change `<GO PACKAGE>` to the desired Go package name and `<OUTPUT DIR>` to the desired output dir. 😉
Your job on the server side is just to implement the `ServerInterface` interface, like:
```
// ServerInterface represents all server handlers.
type ServerInterface interface {
// (GET /trainer/calendar)
GetTrainerAvailableHours(w http.ResponseWriter, r *http.Request, params GetTrainerAvailableHoursParams)
// (PUT /trainer/calendar/make-hour-available)
MakeHourAvailable(w http.ResponseWriter, r *http.Request)
// (PUT /trainer/calendar/make-hour-unavailable)
MakeHourUnavailable(w http.ResponseWriter, r *http.Request)
}
```
You can see it in action in the [Wild Workouts project](https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example).
### Bonus: Client for JavaScript/TypeScript
Even if its the list of **recommended Go libraries**, you may need to generate code for the browser.
#### ✅ openapi-generator-cli [\[GitHub\]](https://github.com/OpenAPITools/openapi-generator-cli) [\[Docs\]](https://github.com/OpenAPITools/openapi-generator-cli#readme)
In that case, we also recommend a non-official library instead of the official one.
In contrast to `oapi-codegen`, `openapi-generator-cli` is a Java tool. To avoid any JVM-related issues, we recommend generating clients using Docker:
```
docker run --rm --env "JAVA_OPTS=-Dlog.level=error" -v "${PWD}:/local" \
"openapitools/openapi-generator-cli:v6.2.1" generate \
-i "/local/api/openapi/service.yml" \
-g javascript \
-o "/local/web/src/clients/service"
```
It assumes that the spec is available locally in `api/openapi/service.yml`.
You can use `openapi-generator-cli` for TypeScript and other languages as well.
## Alternative types of communication
### gRPC
gRPC is a technology that can help you with building robust, internal communication between your services (but not only!).
I already described in detail why its [worth using gRPC for internal communication](https://threedots.tech/post/robust-grpc-google-cloud-run/) and how to do it.
Ill not repeat it here and will focus on the tooling you need.
With gRPC, you have little choice for generating server and client: you should use official tooling. The good news is that you dont need anything more because it does its job!
#### ✅ protoc [\[Docs\]](https://grpc.io/docs/)
To generate Go code from `.proto` files, you need to install [protoc](https://grpc.io/docs/protoc-installation/) and [protoc Go Plugin](https://grpc.io/docs/quickstart/go/).
A list of supported types can be found in [Protocol Buffers Version 3 Language Specification](https://developers.google.com/protocol-buffers/docs/reference/proto3-spec#fields). More complex built-in types like Timestamp can be found in [Well-Known Types list](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf).
### Messaging
#### ✅ Watermill [\[GitHub\]](https://github.com/ThreeDotsLabs/watermill) [\[Docs\]](https://watermill.io/) [\[Examples\]](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples)
About four years ago, when working on one of our projects, we found that there is no library that can simplify building message-driven or event-driven applications easily. To make our lives easier, we decided to write a library that will allow us to write event-driven code as easily as writing HTTP services. This is how Watermill was born.
Today, Watermill is one of the most popular Go libraries with almost 5k GitHub stars, +35 contributors, and 10 officially supported Pub/Subs.
Usually, message-broker libraries are very low-level. With Watermill, publishing messages may be as simple as:
```
publisher.Publish("example.topic", msg)
```
And subscribing like:
```
messages, err := subscriber.Subscribe(ctx, "example.topic")
if err != nil {
panic(err)
}
for msg := range messages {
fmt.Printf("received message: %s, payload: %s\n", msg.UUID, string(msg.Payload))
msg.Ack()
}
```
Compared to using just the message brokers library, Watermill provides support for some higher level functionalities like [middlewares](https://watermill.io/docs/middlewares/), [CQRS support](https://watermill.io/docs/cqrs/), or [event-forwarder](https://watermill.io/docs/forwarder/) component (that can be used to stream your messages from an SQL database to the message broker).
Today, Watermill officially supports [Kafka](https://watermill.io/pubsubs/kafka/), [GCP Pub/Sub](https://watermill.io/pubsubs/googlecloud/), [NATS](https://watermill.io/pubsubs/nats/), [RabbitMQ](https://watermill.io/pubsubs/amqp/) message brokers (Pub/Subs). It can also listen to and emit events as [HTTP hooks](https://watermill.io/pubsubs/http/), from databases like [MySQL/Postgres](https://watermill.io/pubsubs/sql/), [BoltDB](https://watermill.io/pubsubs/bolt/) or [Firestore](https://watermill.io/pubsubs/firestore/). It can also work with in-memory [Go-channel based Pub/Sub](https://watermill.io/pubsubs/gochannel/).
Don't miss new posts.
Join over 15k subscribers of our newsletter and get a [**free e-book**](https://threedots.tech/go-with-the-domain/)!
[
![Cover](https://threedots.tech/img/go-with-domain-cover-retina_hu7b716367e1ec5d427a88b8765e593fda_120136_300x424_resize_q80_h2_lanczos.webp)
## Go With The Domain Three Dots Labs
](https://threedots.tech/go-with-the-domain/)
🔒 We do not send spam. You can unsubscribe at any time!
## Database
### SQL
There is no golden hammer solution for interacting with SQL databases. The reason is simple: it depends greatly on what kind of data you store.
In some projects, data models are relatively simple. In some, they are very complex. Because of that, I have two libraries to recommend. You should choose one of them based on the requirements of your project.
For projects with straightforward data models, you should check `sqlx`. For a bit more complex, you should look at `SQLBoiler`.
✅ Tactic: Using ORM
I hear more and more that using ORM is not a good idea. I understand a reason for such thinking: many people are hurt by the improper use of ORMs.
Its like with a knife: I have an extremally sharp Japanese knife without which I cant imagine cooking. On another side, I need to be very careful with using it. But that fact doesnt make this knife a bad tool! If you are using it properly, its making your life much easier. Its the same situation with ORMs. Writing queries by hand may be time-consuming and error-prone when your models are complex. ORMs were invented to solve that problem.
If you have a bad experience using ORMs, you should check [Things to know about DRY](https://threedots.tech/post/things-to-know-about-dry/) article. The tactics presented in that article will help you to avoid all common problems with ORMs.
❌ Anti-pattern: Avoid weakly typed ORMs
Most ORMs depend heavily on reflection and `interface{}`/`any`. The type system is one of the biggest strengths of Go. It helps you build applications efficiently. Resigning from strict typing makes your application more error-prone.
#### ✅ sqlx [\[GitHub\]](https://github.com/jmoiron/sqlx) [\[Docs\]](http://jmoiron.github.io/sqlx/)
The standard librarys `database/sql` package is rather a low-level one. `sqlx` provides a more convenient and powerful API to work with databases. It includes helper functions for common tasks like inserting and querying data and support for more advanced features like prepared statements and transactions. `sqlx` also has more advanced support for data unmarshaling (for example to structs, lists of structs, json data, etc.). As a nice bonus, `sqlx`s interface is compatible with interfaces from `database/sql`.
But even if `sqlx` is a great library, it works well for relatively simple database models. At some level of complexity, you should consider migration to an ORM.
#### ✅ SQLBoiler [\[GitHub\]](https://github.com/volatiletech/sqlboiler) [\[Docs\]](https://github.com/volatiletech/sqlboiler#table-of-contents) [\[Examples\]](https://github.com/volatiletech/sqlboiler#features--examples)
So far, the only ORM that fully meets our requirements is SQLBoiler. At first, how you define SQLBoiler models may surprise you. Most ORMs generate the database schema out of your Go models. SQLBoiler does the opposite: it generates Go models from your database schema.
This approach has multiple advantages. One of the most important features is stricter typing than other libraries. Thanks to that, many checks are done during compilation. You dont need to depend on a ton of reflection and magic struct tags. In most cases, as long as the code compiles, it will work correctly.
Generating code from the database schema helps with migration from an existing database because you dont need to re-write DB models: SQLBoiler generates them for you. So if you start with `sqlx` and move to SQLBoiler later, the migration should be pretty easy.
SQLBoiler supports PostgreSQL, MySQL, MSSQLServer 2012+, SQLite3, and CockroachDB.
❌ Anti-pattern: Using database models in the API responses
As long as youre not writing a stupid simple CRUD application (and the chances are youre not), you should not couple your database models with the API responses.
At some point, requirements will force you to return data in a format different from the format you have in the database. Instead of trying to follow DRY at all costs, its time to split your models.
You can read more on this in [“Business Applications in Go: Things to know about DRY” article](https://threedots.tech/post/things-to-know-about-dry/) and [“Common Anti-Patterns in Go Web Applications”](https://threedots.tech/post/common-anti-patterns-in-go-web-applications/).
### Migrations
SQLBoiler and `sqlx` dont provide out-of-the-box support for migrations. Its okay because you are not forced to use any particular solution.
In my recent projects, I used both `sql-migrate` and `goose`, and I was happy about them.
#### ✅ sql-migrate [\[GitHub\]](https://github.com/rubenv/sql-migrate) [\[Docs\]](https://github.com/rubenv/sql-migrate#readme)
#### ✅ goose [\[GitHub\]](https://github.com/pressly/goose) [\[Docs\]](https://pkg.go.dev/github.com/pressly/goose)
We like `sql-migrate` and `goose` because of their simplicity and flexibility. `sql-migrate` and `goose` can be executed as CLI tools and as part of your service.
I like to embed it into the binary of the service. Thanks to that, the migration is executed when the service starts, and it keeps the setup simple. Its also much less complex to run. For example, `sql-migrate` with `go:embed`:
```
// migrations/run.go
package migrations
import (
"database/sql"
"embed"
migrate "github.com/rubenv/sql-migrate"
)
//go:embed *
var migrationsFiles embed.FS
func Run(postgresConn string) error {
db, err := sql.Open("postgres", postgresConn)
if err != nil {
return err
}
migrations := &migrate.EmbedFileSystemMigrationSource{
FileSystem: migrationsFiles,
Root: ".",
}
if _, err := migrate.Exec(db, "postgres", migrations, migrate.Up); err != nil {
return err
}
return nil
}
```
Put your migrations in `migrations/`, for example: `migrations/1_init.sql`, `migrations/2_alter_some_table.sql`, etc. Then run `Run` in your `main`.
## Observability
### Logging
The standard librarys logger doesnt provide essential features like log levels and output formatting.
For logging, we can recommend two libraries: `Logrus` and `zap`. In contrast to `zap`, `Logrus` provides a bit nicer user API, but `zap` is faster.
You can check detailed benchmarks in [zaps readme](https://github.com/uber-go/zap#performance).
❌ Anti-pattern: You should not choose tools based just on benchmarks
Some developers tend to choose libraries based on the benchmark results. Its a risky approach because extreme performance optimizations lead to worse API and limited functionalities set. In most cases, performance differences are negligible in real-life use cases.
Even if, for some applications, it may make a difference, for most applications, it doesnt matter that much. Making just one extra database query or up-scaling a service can make a much more significant difference in performance.
If performance is not absolutely critical for you, you should prefer other characteristics, like the ease of use and number of features.
#### ✅ Logrus [\[GitHub\]](https://github.com/sirupsen/logrus) [\[Docs\]](https://pkg.go.dev/github.com/sirupsen/logrus)
#### ✅ zap [\[GitHub\]](https://github.com/uber-go/zap) [\[Docs\]](http://pkg.go.dev/github.com/uber-go/zap)
### Metrics and tracing
#### ✅ opencensus-go [\[GitHub\]](https://github.com/census-instrumentation/opencensus-go) [\[Docs\]](https://opencensus.io/)
OpenCensus Go is a library that helps you add metrics and tracing to your endpoints or database queries. The integration uses middleware/decorator patterns, and it doesnt require a lot of custom code. It supports [HTTP endpoints](https://pkg.go.dev/go.opencensus.io/plugin/ochttp), [gRPC endpoints](https://pkg.go.dev/go.opencensus.io/plugin/ocgrpc), [SQL databases](https://pkg.go.dev/github.com/opencensus-integrations/ocsql), [MongoDB](https://pkg.go.dev/github.com/orijtech/mongo-go-driver), etc.
You can export traces and metrics to Prometheus, OpenZipkin, GCP Stackdriver Monitoring, Jaeger, AWS X-Ray, Datadog, Graphite, Honeycomb, or New Relic.
## Configuration
Gos standard library doesnt support much more configuration options than the [flag package](https://pkg.go.dev/flag). Even if its enough for simple CLI tools, you may need a bit more for building services.
### Env variables
#### ✅ caarlos0/env [\[GitHub\]](https://github.com/caarlos0/env) [\[Docs\]](https://pkg.go.dev/github.com/caarlos0/env)
This library should provide everything you need for configuration for most applications. Compared to the standard library, it does support loading envs to structs and setting env defaults. It helps to save a lot of boilerplate for bigger configurations. It also supports embedded structs, so you can compose bigger a configuration from independent components.
✅ Tactic: Use env variables for your services configuration
For most applications, environment variables should be good enough as configuration.
Configuration is where you should keep secrets and things that differ between environments. If your configuration is massive and does not change often, it may be worth hardcoding it instead. Its much more pragmatic than having tens of never-changing configuration options.
#### Multi-format configuration
#### ✅ koanf [\[GitHub\]](https://github.com/knadh/koanf) [\[Docs\]](https://pkg.go.dev/github.com/knadh/koanf)
Koanf is an excellent tool if your project requires multiple configuration formats. Its often the case when you write tools that are used externally (for example, CLI tools).
This is my most recent finding. Compared to other [more popular libraries](https://github.com/knadh/koanf#alternative-to-viper), `koanf` just does loading multi-format configuration right. Bonus points for a nice abstraction that allows extending parsing and loading.
Koanf does support the most important configuration formats, like `json`, `yaml`, `dotenv`, env vars, or `hcl`. They can be loaded from the filesystem, flags, and multiple external sources like `s3`, `vault`, `etcd`, or `consul`.
## Building CLI
### Building CLI libraries
#### ✅ urfave/cli [\[GitHub\]](https://github.com/urfave/cli/) [\[Docs\]](https://cli.urfave.org/) [\[Examples\]](https://cli.urfave.org/v2/examples/greet/)
We like `urfave/cli` because of its simple interface and extensibility. We used it in multiple projects without any issues.
Compared to other alternatives, it offers a big-enough feature set while keeping the library lightweight.
## Testing
### Assertions
#### ✅ testify [\[GitHub\]](https://github.com/stretchr/testify) [\[Docs\]](https://pkg.go.dev/github.com/stretchr/testify)
`testify` became the standard assertion library, and Ive seen it in every project I worked on. It provides asserts for the most common cases and also some more complex. One of `testify`s key features are friendly messages for all failed asserts. It makes writing and debugging tests much faster.
The library provides two ways of asserting:
- `assert` from `github.com/stretchr/testify/assert` - the test continues after failure. You should use it when you want to see multiple errors (not just the first one). Works when called in a goroutine.
- `require` from `github.com/stretchr/testify/require` - the test is interrupted after failure. You should use it when some critical condition was not met and continuing doesnt make any sense (for example: storing to database failed). Doesnt work when called in a goroutine.
Some example asserts:
- [Equal](https://pkg.go.dev/github.com/stretchr/testify/assert#Equal) - good enough in most cases
- [Eventually](https://pkg.go.dev/github.com/stretchr/testify/assert#Eventually) - useful for asserting asynchronous conditions
- [ElementsMatch](https://pkg.go.dev/github.com/stretchr/testify/assert#ElementsMatch) - useful for unsorted slices
- [WithinDuration](https://pkg.go.dev/github.com/stretchr/testify/assert#WithinDuration) - useful when comparing time that is not exactly equal
- [ErrorIs](https://pkg.go.dev/github.com/stretchr/testify/assert#ErrorIs)
- [JSONEq](https://pkg.go.dev/github.com/stretchr/testify/assert#JSONEq)
- [Panics](https://pkg.go.dev/github.com/stretchr/testify/assert#Panics)
✅ Tactic: Use assert messages just if it is really needed
Ive seen people who obsessively write messages for all failed assert.
For example:
```
assert.Equal(t, 123, 321, "123 is not equal to 321")
```
will give output:
```
Error: Not equal:
expected: 123
actual : 321
Test: TestEqual
Messages: 123 is not equal to 321
```
As you can see, the message doesnt add anything more than testify would figure out. It can even be harmful because with time, you will need to spend a lot of time to keep the message up to date.
In most cases, the message provided by testify will be good enough. If the test fails, the person who sees the failure will navigate to this test and will understand the reason from the surrounding code.
✅ Tactic: Do not write basic asserts by hand
Many people advocate for writing all asserts by hand. It wont give you much advantage in the end.
`testify` is also very smart in showing the difference between the expected and actual value.
For example:
```
assert.Equal(t, []byte("foo bar baz"), []byte("foo bar 42"))
```
prints:
```
Error: Not equal:
expected: []byte{0x66, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72, 0x20, 0x62, 0x61, 0x7a}
actual : []byte{0x66, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72, 0x20, 0x34, 0x32}
Diff:
--- Expected
+++ Actual
@@ -1,3 +1,3 @@
-([]uint8) (len=11) {
- 00000000 66 6f 6f 20 62 61 72 20 62 61 7a |foo bar baz|
+([]uint8) (len=10) {
+ 00000000 66 6f 6f 20 62 61 72 20 34 32 |foo bar 42|
}
Test: TestEqual
--- FAIL: TestEqual (0.00s)
Expected :[]byte{0x66, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72, 0x20, 0x62, 0x61, 0x7a}
Actual :[]byte{0x66, 0x6f, 0x6f, 0x20, 0x62, 0x61, 0x72, 0x20, 0x34, 0x32}
```
It makes no sense to reinvent the wheel and write it from scratch.
❌ Anti-pattern: Do not use test suites from testify
Testify is an excellent library for assertions, but we dont recommend its test suites. They dont support parallel sub-tests. They may be fine for unit tests, but for integration/API/E2E tests **its a deal-breaker**.
The standard library can achieve most of the functionalities provided by testifys test suites. You can see specific examples in [this article on testing microservices](https://threedots.tech/post/microservices-test-architecture/#keeping-integration-tests-stable-and-fast).
#### ✅ go-cmp [\[GitHub\]](https://github.com/google/go-cmp) [\[Docs\]](https://pkg.go.dev/github.com/google/go-cmp) [\[Examples 1\]](https://github.com/google/go-cmp/blob/master/cmp/example_test.go) [\[Examples 2\]](https://github.com/google/go-cmp/blob/master/cmp/cmpopts/example_test.go)
Sometimes, you must assert a complex struct in your tests skipping some fields. Or the struct contains fields that should be compared in a specific way. Or you need to ignore the slice order or time delta. Its where `go-cmp` can help you!
```
import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
diff := cmp.Diff(
want,
got,
// FieldToIgnore and AnotherFieldToIgnore will be ignored in SomeStruct
cmpopts.IgnoreFields(SomeStruct{}, "FieldToIgnore", "AnotherFieldToIgnore"),
// when comparing time, truncate it to one second
// can be written for any type
opt := cmp.Comparer(func(x, y time.Time) bool {
return x.Truncate(time.Second).Equal(y.Truncate(time.Second))
})
// sort all []int
cmpopts.SortSlices(func(x, y int) bool {
return x < y
}))
)
// cmp returns diff if two objects are different
// to check if objects are equal, you can assert if the diff is empty
assert.Empty(t, diff)
```
To see the list of all available options, I recommend checking the godoc of [`cmp`](https://pkg.go.dev/github.com/google/go-cmp/cmp) and [`cmpopts`](https://pkg.go.dev/github.com/google/go-cmp/cmp/cmpopts) package.
go-cmp can also be used outside of tests, but be careful its another tool that, used irresponsibly, may hurt your project.
#### ✅ gofakeit [\[GitHub\]](https://github.com/brianvoe/gofakeit) [\[Docs\]](https://pkg.go.dev/github.com/brianvoe/gofakeit)
If you need more realistic data for your tests, `gofakeit` helps.
### Mocking
#### Writing mocks by hand
*Initially, I recommended one popular mocking tool here. But after some thinking, we decided that the tool is not good enough to recommend. Instead, consider an alternative mocking strategy 👇*
✅ Tactic: Consider writing mocks by hand
Even if it sounds like a waste of time, writing mocks yourself may be good enough. Objectively speaking, writing them by hand doesnt require much more code and time. As a bonus, it gives you much more flexibility.
This is how an example mock can look like:
```
type BalanceUpdate struct {
UserID string
AmountChange int
}
type UserServiceMock struct {
BalanceUpdates []BalanceUpdate
balanceUpdatesLock sync.Mutex
}
func (u *UserServiceMock) UpdateTrainingBalance(ctx context.Context, userID string, amountChange int) error {
u.balanceUpdatesLock.Lock()
defer u.balanceUpdatesLock.Unlock()
u.BalanceUpdates = append(u.BalanceUpdates, BalanceUpdate{userID, amountChange})
return nil
}
```
It took me literally 1 minute to write it.
✅ Tactic: Keep your interfaces small, so it's easier to mock them
Its hard to mock complex types by hand. But if your interface is so complex you cant write a mock for it, you should reconsider if it needs to be that big. Using mocking libraries obfuscates the real problem.
Try to simplify the type that you are mocking. Maybe [the interface segregation principle](https://en.wikipedia.org/wiki/Interface_segregation_principle) will help? It could be possible to split this type into multiple smaller types.
It will not only simplify your mocks but will improve your codebase.
## Misc
#### ✅ google/uuid [\[GitHub\]](https://github.com/google/uuid) [\[Docs\]](https://pkg.go.dev/github.com/google/uuid)
This library generates UUIDs.
#### ✅ oklog/ulid [\[GitHub\]](https://github.com/oklog/ulid) [\[Docs\]](https://pkg.go.dev/github.com/oklog/ulid)
UUIDs [may be slow to store](https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/) at a larger scale in relational databases. A solution may be using Universally Unique Lexicographically Sortable Identifier: ULIDs. ULIDs are compatible with UUIDs, are unique enough for large scale, and have shorter string representation (Crockfords base32). ULIDs are lexicographically sortable, thanks to what building indexes should be much faster.
Its worth mentioning that UUID v6, v7, and v8 will also be lexicographically sortable. But its spec is still draft when during the release of the article. If you want to try UUID v6 or v7, you can check [github.com/gofrs/uuid](https://github.com/gofrs/uuid/blob/e1079f31cfcadf78856b9866d15574dd6546e29b/uuid.go#L66) which does already implement them.
#### ✅ shopspring/decimal [\[GitHub\]](https://github.com/shopspring/decimal) [\[Docs\]](https://pkg.go.dev/github.com/shopspring/decimal)
Go doesnt have built-in support for decimals. `shopspring/decimal` does the job. We have used this library for a couple of years to build a large financial system.
✅ Tactic: Use decimals for monetary values
Floats are not designed to accurately store decimal numbers.
For example:
```
fmt.Printf("%.16f", 12.1+0.03)
> Output: 12.1300000000000008
```
To make sure your money calculations are correct (and you are not losing or getting extra cents in calculations), we recommend using a decimal type.
Its also a good idea to use the string representation of decimals instead of floats in transport (in events, API requests and responses, etc.).
### Errors
#### ✅ hashicorp/go-multierror [\[GitHub\]](https://github.com/hashicorp/go-multierror) [\[Docs\]](https://threedots.tech/post/list-of-recommended-libraries/github.com/hashicorp/go-multierror)
Did you ever need to handle an error while you were handling another error? `hashicorp/go-multierror` is here to help you!
Its also helpful if an operation can return multiple errors, and you dont want to return just the first one (for example, validation).
Example use cases:
```
func validate() {
var resultErr error
if err := validateFoo(); err != nil {
resultErr = multierror.Append(resultErr, err)
}
if err := validateBar(); err != nil {
resultErr = multierror.Append(resultErr, err)
}
return resultErr
}
```
or
```
func ExecuteStuff() error {
if err := makeStuff(); err != nil {
if cleanupErr := cleanup(); cleanupErr != nil {
err = multierror.Append(err, cleanupErr)
}
return err
}
return nil
}
```
*Note: Go 1.20 [will introduce](https://github.com/golang/go/issues/53435) `errors.Join` function. After release of Go 1.20 you should consider using it instead.*
### Misc
#### ✅ samber/lo [\[GitHub\]](https://github.com/samber/lo) [\[Docs\]](https://pkg.go.dev/github.com/samber/lo)
Lodash-style Go library based on Go 1.18+ Generics. It may be especially useful for you if you are coming to Go from Python and missing some basic slice/map functions.
Some functions that Im using the most:
- [Filter](https://pkg.go.dev/github.com/samber/lo#Filter)
- [Map](https://pkg.go.dev/github.com/samber/lo#Map)
- [Keys](https://pkg.go.dev/github.com/samber/lo#Keys)
- [Values](https://pkg.go.dev/github.com/samber/lo#Values)
- [Find](https://pkg.go.dev/github.com/samber/lo#Find)
- [Max](https://pkg.go.dev/github.com/samber/lo#Max)
- [Must](https://pkg.go.dev/github.com/samber/lo#Must) 😈 please use it just for tests or if you really have a good reason
Even if some may find it “non-idiomatic”, I find it useful in some cases. Its similar to using an [ORM](https://threedots.tech/post/list-of-recommended-libraries/#sql) as long as such libraries are used responsibly and dont obfuscate code, they are useful.
So if you find yourself writing code like:
```
lo.Map(
lo.Filter(someSlice, func(v SomeType, _ int) bool {
return v.IsSpecial
}),
func(t SomeType, _ int) string {
return t.SpecialName()
},
)
```
…its just better to convert it to a simple, more readable loop. 😉
#### ✅ Task [\[GitHub\]](https://github.com/go-task/task) [\[Docs\]](https://taskfile.dev/)
Task is not really a Go library, but its a tool written in Go that may be useful for your projects.
Its an excellent alternative to Makefile. The most important features that it offers are:
- Parallel tasks execution (supported by [task dependencies](https://taskfile.dev/usage/#task-dependencies))
- Preventing [unnecessary work](https://taskfile.dev/usage/#prevent-unnecessary-work)
- [Loading .env](https://taskfile.dev/usage/#env-files)
- [Dynamic variables](https://taskfile.dev/usage/#dynamic-variables)
- [Forwarding CLI arguments](https://taskfile.dev/usage/#forwarding-cli-arguments-to-commands)
- [Templating](https://taskfile.dev/usage/#gos-template-engine)
Its a must-have for each of my new projects.
### Live code reloading
#### ✅ reflex [\[GitHub\]](https://github.com/cespare/reflex) [\[Docs\]](https://pkg.go.dev/github.com/cespare/reflex) \[[Example](https://threedots.tech/post/go-docker-dev-environment-with-go-modules-and-live-code-reloading/)\]
Go doesnt provide code live-reloading out of the box. But you can achieve it quickly with the `reflex` library.
Some time ago, Miłosz wrote an article that shows how to create a [local environment with Docker and reflex](https://threedots.tech/post/go-docker-dev-environment-with-go-modules-and-live-code-reloading/).
### Linter
#### ✅ golangci-lint [\[GitHub\]](https://github.com/golangci/golangci-lint) [\[Docs\]](https://golangci-lint.run/)
golangci-lint is a linter that aggregates multiple linters and runs them in parallel and does it very fast.
Heres [an example configuration](https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/blob/b519c611e9d1248a149c89db9bcf879fd78b1e35/internal/trainer/.golangci.yml) that we use in our projects.
#### ✅ go-cleanarch [\[GitHub\]](https://github.com/roblaszczak/go-cleanarch) [\[Docs\]](https://pkg.go.dev/github.com/roblaszczak/go-cleanarch#section-readme)
If you use [Clean/Hexagonal Architecture](https://threedots.tech/post/introducing-clean-architecture/) in your project, you can use this linter to ensure that The Dependency Inversion Rule and interaction between packages are kept.
### Formatters
#### ✅ go fmt
The standard formatter provided by Go toolchain.
#### ✅ goimports [\[Docs\]](https://pkg.go.dev/golang.org/x/tools/cmd/goimports)
Goimports does all that `go fmt` does, but it also sorts imports of your Go files. Its one of the tools that you will see widely adopted in most Go projects.
Not everybody knows, but you can also separately group your local imports with the `-local` flag.
```
goimports -local "github.com/ThreeDotsLabs/some-repository" -l -w .
```
#### ✅ gofumpt [\[GitHub\]](https://github.com/mvdan/gofumpt) [\[Docs\]](https://pkg.go.dev/mvdan.cc/gofumpt#section-readme)
Just for the biggest formatting freaks! Does all that `go fmt` and `goimports` do and more!
Personally, I like gofumpts formatting decisions.
## Example projects
### DDD & Clean Architecture
#### ✅ Wild Workouts Go DDD Example application [\[GitHub\]](https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example)
**Wild Workouts is an example Go DDD project that we created to show how to build Go applications that are easy to develop, maintain, and fun to work with, It shows a project developed over time and with complex problems to solve.** In contrast to other example projects, it was not blindly copied from other languages.
This is the way how we build our services daily. Highly recommended if you are looking for patterns that will allow you to build more complex projects!
❌ Anti-pattern: Low-quality example repositories
Avoid projects that look like over-engineered copies from other programming languages.
People who write such “DDD” projects often just read a couple of articles about it without understanding it correctly and without using it in real-life projects. If you see DDD/Clean Architecture examples without encapsulated domain models (with public fields) and `json` tags: run! Its definitely not DDD nor Clean Architecture.
### General purpose
#### ✅ Modern Go Application by Márk Sági-Kazár [\[GitHub\]](https://github.com/sagikazarmark/modern-go-application)
Another example repository that we can recommend. It doesnt cover patterns like DDD or Clean Architecture but emphasizes infrastructure beats like observability.
## Summary
Should we check some library that is not listed here? Please let us know in the [comments](https://threedots.tech/post/list-of-recommended-libraries/#disqus_thread)!