Skip to content

Instantly share code, notes, and snippets.

@kulti
Created March 15, 2026 10:48
Show Gist options
  • Select an option

  • Save kulti/c256ef2e529fa95a7334a3ff15d5eb78 to your computer and use it in GitHub Desktop.

Select an option

Save kulti/c256ef2e529fa95a7334a3ff15d5eb78 to your computer and use it in GitHub Desktop.
golangci-lint v2 config workflow
# Comments started with [!] are explanations of my workflow with golangci-lint.
# Another comments are real comments in my config.
# [!] Examine the https://golangci-lint.run/docs/configuration/file/ to know all possibilities.
version: "2"
run:
go: "1.25"
build-tags:
- integration # [!] If you disable code for integration tests to run all unit tests, enable it to lint all the same.
modules-download-mode: vendor # [!] if you are using vendoring
# [!] Linters are enabled manually. Disabled linters are commented and have comment about reason of disabling.
# [!] Linters disabled inside source code by //nolint should be specific and comments like this
# [!] //nolint:gocyclo // This legacy function is complex but the team too busy to simplify it.
# [!] (see also nolintlint settings).
#
# [!] During update version of golangci-lint do the following:
# [!] - add new linters to enable-list
# [!] - enable previously disabled linters if needed
# [!] - configure (see below) or disable new linters to be appropriate for your project
linters:
default: none
enable:
- asciicheck
- bidichk
- bodyclose
- contextcheck
# - cyclop
# Measuring code complexity is hard. Cyclomatic complexity is doubtfully
# a reliable metric for this purpose. Some problems are complex by nature
# and therefore more cyclomatically complex code might actually be
# simpler.
- depguard
- dogsled
- dupl
- durationcheck
- err113
- errcheck
- errname
- errorlint
- exhaustive
# - exhaustruct
# We frequently make use of zero-values and partial initialization.
- forbidigo
- forcetypeassert
- funlen
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocritic
- gocyclo
- godot
# - godox
# TODOs and FIXMEs function as a big warning sign: "Beware! This code has
# bugs and other problems. Be careful not to make things worse when
# editing. Or better fix the issues before implementing a new feature on
# top.". The idea of [`godox` comment
# extractor](https://github.com/766b/godox) seems to be different from
# the idea of [`godox` linter](https://github.com/matoous/godox). The
# first one lets you quickly browse through the code problems, while the
# second one asks you to remove that big warning sign in favor of tasks.
# Though tasks are great, they will not help you while editing.
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- govet
- importas
# - ireturn # See: https://github.com/go-proverbs/go-proverbs.github.io/issues/37.
- ineffassign
- lll
- makezero
- misspell
- mnd
- nakedret
- nestif
- nilnil # Zero-value of maps, slices and channels is `nil` but useful.
- nilerr
# - nlreturn # Too subjective.
- noctx
- nolintlint
- paralleltest
- prealloc
- promlinter
- revive
- rowserrcheck
- sqlclosecheck
- staticcheck
- tagliatelle
- testpackage
- thelper
- tparallel
- unconvert
- unparam
# - varnamelen
# Though the idea is idiomatic, the length is not the only thing that
# determines whether a variable is "easy to use".
- unused
- usestdlibvars
- usetesting
- wastedassign
- whitespace
# - wrapcheck # Is prone to bad suggestions (especially see issue #1).
# - wsl_v5 # Not sure that suggestions are always good.
# [!] Linters has a lot of settings and can be flexible configured.
settings:
lll:
line-length: 110
forbidigo: # [!] Useful to check some code writing rules. E.g. this config contains example of using own logger instead of global zap.
forbid:
- pattern: zap # we use our own logger that wraps zap.
- pattern: os\.Exit # it's not safe to exit in random places, because defer will not work.
- pattern: log\.Fatal.* # it calls os.Exit (see above)
# - pattern: c.viper # forbid it after full migrate to new declarative configs (see PLFM-616).
nilnil:
checked-types: # [!] choose only types relative to your code base or disable this linter.
- ptr
- func
- iface
nolintlint:
require-explanation: true # [!] it forces you to write explanation of //nolint comments.
require-specific: true
tagliatelle: # [!] configure it accordingly to your project rules.
case:
use-field-name: false
rules:
json: snake
yaml: snake
# [!] Extremely useful section to disable some linters for specific files or patterns.
exclusions:
generated: lax # [!] Exclude generated code with specific lines like 'autogenerated code'
warn-unused: true # [!] To keep exclusions clean
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- <generated files or dirs to exclude>
rules:
- path: (_test\.go|apitest)
linters:
- forcetypeassert # it's ok to have some type risks in tests
- gosec # security is not make sense in tests
- noctx # it's ok to do simple http request without context in tests
- path: (.+)_test\.go
linters:
- goconst
# Exclude lll issues for long lines with go:generate
- linters:
- lll
source: "^//go:generate "
# They are using same environment
- path: tests/integration
linters:
- paralleltest
# Init and New functions is a glue for service. They can be long, but simple.
- linters:
- funlen
path: service\.go
source: "Init|New"
# We allow to use zap only in log package.
- path: internal/log
linters:
- forbidigo
text: "use of .* forbidden by pattern `zap`"
# We allow to log.Fatal in main, tests, scripts and generators.
- path: main.go|main_test.go|tests|scripts|internal/blueprint/generator/generator.go|build/dockergen.go
linters:
- forbidigo
text: "forbidden by pattern `log..Fatal.*`"
# We allow to os.Exit in main, tests, scripts and generators.
- path: main.go|main_test.go|tests|scripts|internal/blueprint/generator/generator.go|build/dockergen.go
linters:
- forbidigo
text: "forbidden by pattern `os..Exit`"
# We have a lot of nil-tolerant types here.
- path: <package with a lot of nil-tolerant types>
linters:
- nilnil
# Should be fixed after full migration to generated config PLFM-616.
- path: config.go
linters:
- unparam
text: "newConfig - result 1 \\(error\\) is always nil"
# [!] Formatters should be configured as strict as possible to fix your project codestyle.
formatters:
enable:
- gci
- gofmt
- gofumpt
- goimports
settings:
gci:
sections:
- standard
- default
- prefix(<your go module>)
exclusions:
generated: lax # [!] Exclude generated code with specific lines like 'autogenerated code'
warn-unused: true # [!] To keep exclusions clean
paths:
- <generated files or dirs to exclude>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment