Linters
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:
Formatters
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 withgofmt
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
callsgofmt
.goreturns
callsgoimports
.gofumpt
providesgofumports
which isgoimports
callinggofumpt
instead ofgofmt
.
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.
Integrations
- Golangci-lint has integrations with everything.
- Gofmt is the standard and integrated in every IDE.
- Gofumpt has integration with VSCode and a guide for GoLand.
- Other tools can be integrated into git pre-commit hooks.
Frameworks for pre-commit hooks:
- pre-commit (Python)
- lefthook (Go)
- husky and lint-staged (JS)
- overcommit (Ruby)