Introduction to Linting in Go
Get latest articles directly in your inbox
In this blog I’ll be explaining details about linting and it’s benefits. You’ll also learn about multiple linters available in Golang and how you can use start using linting in your Go applications.
What is Linting?
When you are building any software application you’ll have to deal with a lot of bugs and errors during development. No matter how good you are, there will always be a fair chance that you miss something. Linting is one way to avoid such errors right during developement.
Linting is the process of analyzing source code for bugs, programming and stylistic errors. It also helps in following a good coding standard across the project where multiple developers might be working together. Linting is achieved using a tool popularly known as linter.
Importance of linting code
- Detecting bugs, typos, unused code and possible errors.
- Linting helps in faster development.
- Improving the overall quality of the code. Eg. Setting line length limit, avoiding redundant print statements, etc.
- Faster and better code review since linting already performs static code checks.
Linting in Golang
Now that we have a basic understanding about linting, let’s learn how to use this in our Go applications. Actually golang already provides some basic tools like gofmt
that deals with formatting the Go code and govet
that examines the source code and reports suspicious constructs.
There is a wide range of linters available today most of which have been built by the amazing Go community. Let’s discuss about some some useful linters -
- goimports - handles formatting and automatically adds the required packages. It also removes the unreferenced imports.
- gocyclo - calculates cyclomatic complexities of functions in Go source code. This is really helpful in identifying possible refactoring to make functions concise and clean.
- unused - checks for unused constants, variables, functions and types.
- errcheck - checks for unhandled errors in go programs. This helps in identifying possible critical bugs that were missed during developement.
There are 50+ linters available. You don’t want to import each one of them in your package manually. To solve this we have golangci-lint. It is the preferred Go linters aggregator that covers most the linters you’ll ever need. It runs these linters in parallel and reuses the build cache for improved performance during development.
Install golangci-lint
We will install golangci-lint
by fetching the binary via curl. You can modify the version if required. Here we are installing the v1.41.1
version.
# binary will be $(go env GOPATH)/bin/golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.41.1
Let’s confirm the installation by running the following command.
golangci-lint --version
Running golangci-lint
To run the linters run the following command.
# for current directory and subdirectories
golangci-lint run
# for a specific directory
golangci-lint run <dirname>
This tool comes with a set of linters that are enabled by default while others are disabled. You can check all the available linters using help
golangci-lint help linters
This would return a list of linters. Output would be like this.
Enabled by default linters:
deadcode: Finds unused code [fast: true, auto-fix: false]
errcheck: Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: true, auto-fix: false]
gosimple (megacheck): Linter for Go source code that specializes in simplifying a code [fast: true, auto-fix: false]
govet (vet, vetshadow): Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: true, auto-fix: false]
ineffassign: Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
staticcheck (megacheck): Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: true, auto-fix: false]
structcheck: Finds unused struct fields [fast: true, auto-fix: false]
typecheck: Like the front-end of a Go compiler, parses and type-checks Go code [fast: true, auto-fix: false]
unused (megacheck): Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
varcheck: Finds unused global variables and constants [fast: true, auto-fix: false]
If you want to enable a specific linter use -E/--enable
option. Similarly to disable a linter use -D/--disable
option with run
command.
golangci-lint run -E errcheck
You can disable all linters using --disable-all
option.
golangci-lint run --disable-all
Using golangci-lint
You can possibly add tens of linters in the above command and enable them, but this seems like a lot of work. To make things clean and simple - recommended way to use golangci-lint
is via configuration file.
Create the .golangci-lint.yml
in root directory of your project. We are using the yaml
format but you can choose toml
or json
format as well.
You can directly use this official sample configuration that contains a lot of linters with explanation. Feel free to edit this and add new linters available here.
Here is the golanci-lint.yml
I use for my projects.
run:
concurrency: 4
timeout: 3m
issues-exit-code: 1
tests: true
skip-dirs-use-default: true
modules-download-mode: readonly
output:
format: colored-line-number
print-issued-lines: true
print-linter-name: true
uniq-by-line: true
linters-settings:
errcheck:
check-type-assertions: true
check-blank: true
govet:
check-shadowing: true
enable-all: true
disable-all: false
disable:
- fieldalignment
unused:
check-exported: true
gocyclo:
min-complexity: 7
gocognit:
min-complexity: 7
nakedret:
max-func-lines: 10
goconst:
min-len: 3
min-occurrences: 2
gofmt:
simplify: true
maligned:
suggest-new: true
fieldalignment:
suggest-new: true
tagliatelle:
case:
rules:
json: snake
lll:
line-length: 160
revive:
ignore-generated-header: true
severity: warning
formatter: friendly
confidence: 0.8
errorCode: 0
warningCode: 0
rules:
- name: atomic
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
- name: unreachable-code
- name: redefines-builtin-id
linters:
enable:
- bodyclose
- deadcode
- depguard
- dogsled
- dupl
- errcheck
- exportloopref
- exhaustive
- funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- revive
- gomnd
- goprintffuncname
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- noctx
- nolintlint
- rowserrcheck
- staticcheck
- structcheck
- stylecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
fast: false
issues:
exclude-use-default: true
max-issues-per-linter: 0
max-same-issues: 0
new-from-rev: origin/master
exclude-rules:
- path: test
linters:
- lll
- dupl
- funlen
- exhaustivestruct
- gocognit
- fieldalignment
- scopelint
- testpackage
- paralleltest
That’s it. Run golangci-lint run
from root folder of your project. A good idea is to add this as a part of your CI pipeline. You can use the golangci github action for projects hosted on github.
Excluding linting in Go code
golangci-lint
provides a //nolint
directive that can be used in code to ignore linting for a particular line/block or file.
// exclude a line
// here we are ignoring this line for golint linter
var userLastName string //nolint:golint
// Here we are excluding this function block for all linters.
//nolint
function solve() {
...
}
// to exclude the complete file use nolint on top of file
//nolint
pkg myservice
Congrats! You are now ready to use linting to your Go projects. This will help you making your code less error-prone and improve overall code quality.
Resources
Books to learn Golang
I hope you learned something new. Feel free to suggest improvements ✔️
I share regular updates and resources on Twitter. Let’s connect!
Keep exploring 🔎 Keep learning 🚀
Liked the content? Do support :)