The first linter you face coming into Go is go vet. It is a built-in linter targeted mostly at finding bugs rather than code style.

Another official linter from the Go core team is golint. This one is targeted at finding not bugs but mostly style issues from the official code review guide and effective go.

If you want to catch more bugs and write more effective code, consider running more linters. Go has plenty of them. Of course, it would be hard to download, install, and run all of them. Luckily, we have amazing golangci-lint. It is a wrapper, providing a single unified way to run and configure more than 40 linters. Keep in mind that by default, it runs only a few of them, so it’s good to have an explicit configuration with a listing of all linters you want to use in the project. And if you’d like to know from what you can start, below are some most notable linters.

A few biggest linters:

  • staticcheck has a huge collection of checks, target on finding bugs, improving code readability and performance, simplifying code.
  • go-critic has also many different checks of all kinds: bugs, performance, style issues. It is positioned as “the most opinionated linter”, so, probably, you want to disable a few checks but only a few. Don’t ask what’s better, staticcheck or go-critic, just use both.
  • gosec is targeted exclusively on finding security issues.

A few more specific but helpful linters:

  • errcheck finds errors that you forgot to check. Always check all errors and do something meaningful, don’t let them pass unnoticed.
  • ineffassign finds an assignment that has no effect. In most cases, it happens when you assigned an error to a previously created err variable but forgot to check it.

Useful linters without golangci-lint integration (yet?):

  • revive is a stricter and faster alternative to golint with a lot of rules. Most of the rules are about code style and consistency, some are opinionated. No need to worry, the linter allows to configure or disable any check.
  • sqlvet lints SQL queries in Go code for syntax errors and unsafe constructions.
  • semgrep-go finds simple bugs.

What should you use? Everything you can! If you have an existing project, enable all linters that are easy to integrate, and then slowly, one by one, try and enable all that look reasonable and helpful. Just give it a try! Also, be mindful of your coworkers, help them to fix a code that a linter complains about, and be ready to disable a check if it works not so well for your codebase, especially if it is only about a code style.

Further reading:


Your basic toolkit:

  • One of the most famous go features is a built-in code formatter gofmt. Gofmt is your friend, use gofmt. There is no specification about what exactly gofmt does because the code evolves and changes rapidly, fixing formatting bugs and corner cases.
  • goimports is another must-have code formatter. It automatically adds missed imports and removes unused ones. I am so used to it that I don’t remember when I last time added an import manually.
  • goreturns fills in return statement with zero values to match the function return type. It helps save a few keystrokes. However, be careful using it, the project seems to be not in active development for a long while.
  • gofumpt is a stricter fork gofmt with more rules. It is fully compatible with gofmt and helpful.

For historical reasons, Go extension for VSCode support specifying only one code formatter at once. So, every next level tool calls all previous tools under the hood:

  • goimports calls gofmt.
  • goreturns calls goimports.
  • gofumpt provides gofumports which is goimports calling gofumpt instead of gofmt.

So, I use gofumports as my code formatter in VSCode, which includes gofmt, gofumpt, and goimports.

A few smaller code formatters that can come in handy:

  • golines formats long lines of code. Probably, you won’t be happy with the result and would like to manually reformat it. However, it’s still better than a piece of code hiding outside your screen boundaries. There is an issue “consider breaking long lines” in gofumpt, so there is a chance that soon gofumpt will take care of it as well.
  • keyify turns unkeyed struct literals (T{1, 2, 3}) into keyed ones (T{A: 1, B: 2, C: 3}). This description says everything. Always use keyed struct literals because the order is hard to remember, can be changed, and so on.
  • unconvert removes unnecessary type conversions. It’s not so important but makes the code a bit cleaner.

See awesome-go-code-formatters for more tools.

Custom rules

If you have a custom rule you’d like to validate or reformat in your project, there are a few linters and tools that can be helpful:

  • gomodguard allows forbidding usage of particular modules or domains.
  • ruleguard is not a linter at the moment but a framework for fast writing of simple rules. It has a custom DSL that can be used to lint code as well as rewrite specific constructions. It can be integrated with golangci-lint via go-critic.


Frameworks for pre-commit hooks: