Tooling

Overview

Go ships with a strong suite of built-in tools. This chapter will go over the most common tools and how to use them in your daily development workflow. In addition to the tools that ship with Go, there are a set of very important linters and vetters that can catch runtime bugs as well as significantly improve code quality and performance. This chapter shows what those tools are and how to use them.

What To Use And When

  • Lot of built in and third party tools
  • Some are so amazing they should be automated in your development process or even your IDE
  • Some are rarely used, but when needed are lifesavers

The Go Command

$ go --help

bug         start a bug report
build       compile packages and dependencies
clean       remove object files and cached files
doc         show documentation for package or symbol
env         print Go environment information
fix         update packages to use new APIs
fmt         gofmt (reformat) package sources
generate    generate Go files by processing source
get         add dependencies to current module and install them
install     compile and install packages and dependencies
list        list packages or modules
mod         module maintenance
run         compile and run Go program
test        test packages
tool        run specified go tool
version     print Go version
vet         report likely mistakes in packages

Go Fmt

$ go help fmt

usage: go fmt [-n] [-x] [packages]

Fmt runs the command 'gofmt -l -w' on the packages named
by the import paths.  It prints the names of the files that are modified.

For more about gofmt, see 'go doc cmd/gofmt'.
For more about specifying packages, see 'go help packages'.

The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.

To run gofmt with specific options, run gofmt itself.
  • Built in to most editors
  • Useful for git hooks like a pre-commit (example provided at end of unit)

Gofmt Rewrite Patterns

gofmt supports powerful rewrite rules with the -r flag to automatically refactor code:

# Replace interface{} with any (Go 1.18+)
$ gofmt -w -r 'interface{} -> any' .

# Simplify slice expressions
$ gofmt -w -r 'α[β:len(α)] -> α[β:]' .

# Replace context.TODO() with context.Background()
$ gofmt -w -r 'context.TODO() -> context.Background()' .

# Convert errors.New(fmt.Sprintf(...)) to fmt.Errorf(...)
$ gofmt -w -r 'errors.New(fmt.Sprintf(a, b)) -> fmt.Errorf(a, b)' .

Pattern syntax:

  • Use Greek letters (α, β, γ) or single letters as wildcards
  • -> separates pattern from replacement
  • Patterns match AST nodes, not text

Example transformation:

// Before
var x interface{} = 42

// After running: gofmt -w -r 'interface{} -> any' .
var x any = 42

Go Vet

$ go help vet

usage: go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]

Vet runs the Go vet command on the packages named by the import paths.

For more about vet and its flags, see 'go doc cmd/vet'.
For more about specifying packages, see 'go help packages'.
For a list of checkers and their flags, see 'go tool vet help'.
For details of a specific checker such as 'printf', see 'go tool vet help printf'.

The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.

The -vettool=prog flag selects a different analysis tool with alternative
or additional checks.
For example, the 'shadow' analyzer can be built and run using these commands:

  go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
  go vet -vettool=$(which shadow)

The build flags supported by go vet are those that control package resolution
and execution, such as -n, -x, -v, -tags, and -toolexec.
For more about these flags, see 'go help build'.

See also: go fmt, go fix.
  • All code should pass go vet (even tests)
  • go vet will find runtime errors such as incorrect struct field tags, improper format strings, copying locks, etc.
  • run go doc cmd/vet for detailed information to see what go vet can detect

Doc

Look up docs on code definitions.

$
$ go doc --help
Usage of [go] doc:
        go doc
        go doc <pkg>
        go doc <sym>[.<method>]
        go doc [<pkg>].<sym>[.<method>]
        go doc <pkg> <sym>[.<method>]
For more information run
        go help doc

Flags:
  -all
        show all documentation for package
  -c    symbol matching honors case (paths not affected)
  -cmd
        show symbols with package docs even if package is a command
  -src
        show source code for symbol
  -u    show unexported symbols as well as exported

For more help, you can also use:

go help doc

Go Doc (Package)

To list the public methods in the json package (output abbreviated for slide):

$ go doc json
package json // import "encoding/json"

Package json implements encoding and decoding of JSON as defined in RFC 7159.
The mapping between JSON and Go values is described in the documentation for the
Marshal and Unmarshal functions.

See "JSON and Go" for an introduction to this package:
https://golang.org/doc/articles/json_and_go.html

func Compact(dst *bytes.Buffer, src []byte) error
func HTMLEscape(dst *bytes.Buffer, src []byte)
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
func Marshal(v any) ([]byte, error)
func MarshalIndent(v any, prefix, indent string) ([]byte, error)
func Unmarshal(data []byte, v any) error
func Valid(data []byte) bool
type Decoder struct{ ... }
    func NewDecoder(r io.Reader) *Decoder
type Delim rune
type Encoder struct{ ... }
    func NewEncoder(w io.Writer) *Encoder
type InvalidUTF8Error struct{ ... }
type InvalidUnmarshalError struct{ ... }
type Marshaler interface{ ... }
type MarshalerError struct{ ... }
type Number string
type RawMessage []byte
type SyntaxError struct{ ... }
type Token any
type UnmarshalFieldError struct{ ... }
type UnmarshalTypeError struct{ ... }
type Unmarshaler interface{ ... }
type UnsupportedTypeError struct{ ... }
type UnsupportedValueError struct{ ... }

Go Doc (Symbol)

$ go doc json.Decoder
package json // import "encoding/json"

type Decoder struct {
	// Has unexported fields.
}
    A Decoder reads and decodes JSON values from an input stream.

func NewDecoder(r io.Reader) *Decoder
func (dec *Decoder) Buffered() io.Reader
func (dec *Decoder) Decode(v any) error
func (dec *Decoder) DisallowUnknownFields()
func (dec *Decoder) InputOffset() int64
func (dec *Decoder) More() bool
func (dec *Decoder) Token() (Token, error)
func (dec *Decoder) UseNumber()

Go Doc (Method)

$ go doc json.Decoder.Decode
func (dec *Decoder) Decode(v interface{}) error
    Decode reads the next JSON-encoded value from its input and stores it in the
    value pointed to by v.

    See the documentation for Unmarshal for details about the conversion of JSON
    into a Go value.

Pkgsite - Local Package Documentation

pkgsite is the modern way to browse Go documentation locally. It provides the same experience as pkg.go.dev but for your local packages.

To install pkgsite:

go install golang.org/x/pkgsite/cmd/pkgsite@latest

To run Go documentation locally:

pkgsite -http=:8080

Open http://localhost:8080

  • Browse all your local packages
  • Very useful for closed source projects
  • Useful to see API changes you’ve made locally
  • Shows module information and dependencies
  • Better performance than the old godoc tool

Example:

Navigate to http://localhost:8080 and search for your package name

Go Env

  • Print your go environment.
  • Helpful for debugging path issues
$ go env

GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/user/.cache/go-build"
GOENV="/home/user/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/user/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/user/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.25.0"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/user/projects/myapp/go.mod"
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1234567890=/tmp/go-build -gno-record-gcc-switches"

Go Fix

Fix finds Go programs that use old APIs and rewrites them to use newer ones. After you update to a new Go release, fix helps make the necessary changes to your programs.

Usage:

    go tool fix [-r name,...] [path ...]
  • Use --diff to print the changes it will make instead of actually making them
  • go tool fix --help will list all available rewrites

Linters

Golangci-lint

The golangci-lint is a linter aggregator - your one-stop shop for linting Go code. It runs multiple linters in parallel, reuses Go build cache, and provides fast, comprehensive analysis.

Why golangci-lint?

  • Runs linters in parallel for speed
  • Reuses Go build cache
  • Includes 50+ linters (errcheck, staticcheck, revive, gosec, etc.)
  • Configurable - enable only what you need
  • Great CI/CD integration
  • Modern, actively maintained

Installing Golangci-lint

Recommended installation:

# Binary installation (recommended)
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin

# Or using go install (requires Go 1.19+)
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# Verify installation
golangci-lint --version

Note: It is NOT recommended to use go get for installation.

Full installation instructions: https://golangci-lint.run/welcome/install/

Running Golangci-lint

Basic usage:

# Run with default configuration
$ golangci-lint run

# Run on specific directories
$ golangci-lint run dir1 dir2/... dir3/file1.go

# Run with all linters enabled
$ golangci-lint run --enable-all

# Run only specific linters
$ golangci-lint run --disable-all -E errcheck -E staticcheck -E revive

Configuration File

Create .golangci.yml in your project root for modern configuration:

# Modern golangci-lint configuration
run:
  timeout: 5m
  go: '1.25'

linters:
  enable:
    - errcheck      # Check for unchecked errors
    - gosimple      # Simplify code
    - govet         # Vet examines Go source code
    - ineffassign   # Detect ineffectual assignments
    - staticcheck   # Advanced static analysis
    - unused        # Check for unused code
    - revive        # Fast, configurable linter (replaces golint)
    - gofmt         # Check formatting
    - goimports     # Check imports
    - gosec         # Security issues
    - misspell      # Spelling errors
    - unconvert     # Unnecessary type conversions
    - unparam       # Unused function parameters

linters-settings:
  revive:
    rules:
      - name: blank-imports
      - name: context-as-argument
      - name: error-return
      - name: error-strings
      - name: error-naming
      - name: exported
      - name: if-return
      - name: var-naming
      - name: package-comments
      - name: receiver-naming

  errcheck:
    check-type-assertions: true
    check-blank: true

  govet:
    enable-all: true

issues:
  exclude-use-default: false
  max-issues-per-linter: 0
  max-same-issues: 0

Enabling & Disabling Linters

Pass -E/--enable to enable linter and -D/--disable to disable:

# Disable all, enable specific linters
$ golangci-lint run --disable-all -E errcheck -E staticcheck

# Enable additional linters beyond defaults
$ golangci-lint run -E gosec -E revive

You can also use presets:

# Run bugs preset (errcheck, govet, staticcheck, etc.)
$ golangci-lint run --presets bugs

# Available presets: bugs, comment, complexity, error, format,
#                    import, metalinter, module, performance,
#                    sql, style, test, unused

Default Linters (golangci-lint V1.55+)

Enabled by default:

Important Linters To Enable

These linters are not enabled by default but highly recommended:

Security:

  • gosec - Security problems

Style & Quality:

Formatting:

Performance:

Revive - The Modern Linter

revive is included in golangci-lint and is the modern replacement for the deprecated golint.

Why revive?

  • Fast and configurable
  • Drop-in replacement for golint
  • Extensible rule system
  • Active maintenance
  • Great defaults

Enabling revive in golangci-lint

# .golangci.yml
linters:
  enable:
    - revive

linters-settings:
  revive:
    rules:
      - 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: var-naming

Or via command line:

$ golangci-lint run -E revive

For standalone revive usage (less common):

$ go install github.com/mgechev/revive@latest
$ revive -config revive.toml ./...

Note: Most projects use revive via golangci-lint rather than standalone.

Errcheck

errcheck is a program for checking for unchecked errors in go programs.

Install

$ go install github.com/kisielk/errcheck@latest

errcheck requires Go 1.12 or newer and depends on the package go/loader from the golang.org/x/tools repository.

For basic usage, just give the package path of interest as the first argument:

$ errcheck github.com/kisielk/errcheck/testdata

To check all packages beneath the current directory:

$ errcheck ./...

Staticcheck

staticcheck is go vet on steroids, applying a ton of static analysis checks you might be used to from tools like ReSharper for C#.

Installation

$ go install honnef.co/go/tools/cmd/staticcheck@latest

Purpose

The main purpose of staticcheck is editor integration, or workflow integration in general. For example, by running staticcheck when saving a file, one can quickly catch simple bugs without having to run the whole test suite or the program itself.

The tool shouldn’t report any errors unless there are legitimate bugs - or very dubious constructs - in the code.

Running

All of the following examples are valid invocations:

staticcheck github.com/example/foo
staticcheck github.com/example/foo github.com/example/bar
staticcheck github.com/example/...

In addition, a single package can be specified as a list of files:

staticcheck file1.go file2.go file3.go

Note that all files of the package need to be specified, similar to how go build works.

Gosimple

gosimple is a linter for Go source code that specializes on simplifying code.

Gosimple requires Go 1.6 or later.

Installation

go install honnef.co/go/tools/cmd/gosimple@latest

Usage

Invoke gosimple with one or more filenames, a directory, or a package named by its import path. Gosimple uses the same import path syntax as the go command and therefore also supports relative import paths like ./... Additionally the ... wildcard can be used as suffix on relative and absolute file paths to recurse into them.

The output of this tool is a list of suggestions in Vim quickfix format, which is accepted by lots of different editors.

Structcheck

structcheck finds unused struct fields.

Installation

go install gitlab.com/opennota/check/cmd/structcheck@latest
$ structcheck --help

Usage of structcheck:
  -a    Count assignments only
  -e    Report exported fields
  -t    Load test files too
  ```

```bash
$ structcheck fmt

fmt: /usr/lib/go/src/fmt/format.go:47:2: fmt.fmtFlags.zero
fmt: /usr/lib/go/src/fmt/format.go:41:2: fmt.fmtFlags.minus
fmt: /usr/lib/go/src/fmt/format.go:42:2: fmt.fmtFlags.plus
fmt: /usr/lib/go/src/fmt/format.go:43:2: fmt.fmtFlags.sharp
fmt: /usr/lib/go/src/fmt/format.go:44:2: fmt.fmtFlags.space
fmt: /usr/lib/go/src/fmt/format.go:52:2: fmt.fmtFlags.plusV
fmt: /usr/lib/go/src/fmt/format.go:53:2: fmt.fmtFlags.sharpV
fmt: /usr/lib/go/src/fmt/format.go:39:2: fmt.fmtFlags.widPresent
fmt: /usr/lib/go/src/fmt/format.go:40:2: fmt.fmtFlags.precPresent
fmt: /usr/lib/go/src/fmt/format.go:45:2: fmt.fmtFlags.unicode
fmt: /usr/lib/go/src/fmt/format.go:46:2: fmt.fmtFlags.uniQuote

Varcheck

varcheck finds unused global variables and constants.

$ varcheck --help

Usage of varcheck:
  -e=false: Report exported variables and constants
$ varcheck image/jpeg

image/jpeg: /usr/lib/go/src/image/jpeg/reader.go:74:2: adobeTransformYCbCr
image/jpeg: /usr/lib/go/src/image/jpeg/reader.go:75:2: adobeTransformYCbCrK
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:54:2: quantIndexLuminance
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:55:2: quantIndexChrominance
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:91:2: huffIndexLuminanceDC
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:92:2: huffIndexLuminanceAC
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:93:2: huffIndexChrominanceDC
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:94:2: huffIndexChrominanceAC

Ineffassign

ineffassign detects when assignments to existing variables are not used.

Example usage:

$ ineffassign ./

client/examples/example.go:168:5: ineffectual assignment to err
client/examples/example.go:184:2: ineffectual assignment to spaces
handler_test.go:616:5: ineffectual assignment to err
influxql/parser.go:777:8: ineffectual assignment to pos
influxql/parser.go:777:13: ineffectual assignment to lit

reference: influxql/parser.go:777:8: ineffectual assignment to pos

// Next token should be ASC, DESC, or IDENT | STRING.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok == IDENT || tok == STRING {
	field.Name = lit
	// Check for optional ASC or DESC token.
	tok, pos, lit = p.scanIgnoreWhitespace() // this line pos is assigned but not used
	if tok != ASC && tok != DESC {
		p.unscan()
		return field, nil
	}
}

Dead Code

deadcode is a very simple utility which detects unused declarations in a Go package.

Usage

$ deadcode [-test] [packages]

    -test     Include test files
    packages  A list of packages using the same conventions as the go tool

Limitations

  • Self-referential unused code is not currently reported
  • A single package can be tested at a time
  • Unused methods are not reported

Linters Disabled By Default

The following linters are disabled by default (-E/--enable). They can sometimes report false positive, or opinions and result in CI failing a build.

They are very useful for local development, but not as commonly enabled in a CI pipeline.

  • bodyclose - checks whether HTTP response body is closed successfully
  • golint - DEPRECATED: Use revive or staticcheck instead
  • stylecheck - Stylecheck is a replacement for golint (included in staticcheck)
  • gosec - Inspects source code for security problems
  • interfacer - ARCHIVED: No longer maintained, removed from golangci-lint
  • unconvert - Remove unnecessary type conversions
  • dupl - Tool for code clone detection
  • goconst - Finds repeated strings that could be replaced by a constant
  • gocyclo - Computes and checks the cyclomatic complexity of functions
  • gofmt - Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
  • goimports - Goimports does everything that gofmt does. Additionally it checks unused imports
  • maligned - DEPRECATED: Use fieldalignment (part of govet) instead
  • depguard - Go linter that checks if package imports are in a list of acceptable packages
  • misspell - Finds commonly misspelled English words in comments
  • lll - Reports long lines
  • unparam - Reports unused function parameters
  • dogsled - Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
  • nakedret - Finds naked returns in functions greater than a specified function length
  • prealloc - Finds slice declarations that could potentially be preallocated
  • scopelint - REMOVED: Replaced by exportloopref in golangci-lint
  • gocritic - The most opinionated Go source code linter
  • gochecknoinits - Checks that no init functions are present in Go code
  • gochecknoglobals - Checks that no globals are present in Go code
  • godox - Tool for detection of FIXME, TODO and other comment keywords
  • funlen - Tool for detection of long functions
  • whitespace - Tool for detection of leading and trailing whitespace

Golint (DEPRECATED - Historical Reference Only)

⚠️ WARNING: golint is officially deprecated and no longer maintained.

Use these modern alternatives instead:

  • revive - Fast, configurable, extensible drop-in replacement
  • staticcheck - Advanced static analysis with style checks included
  • golangci-lint with revive enabled - Aggregated linting solution

Historical Context (for reference only)

Golint was a style linter that pointed out style mistakes based on Effective Go. It required human judgment to decide which suggestions to apply.

For new projects: Do not install or use golint. Configure golangci-lint with revive instead.

For legacy projects: Migrate to revive or staticcheck as soon as possible.

Betteralign - Struct Field Alignment Optimization

betteralign finds inefficiently packed structs and can automatically fix them. It’s the modern replacement for the deprecated aligncheck tool.

Installation

$ go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest

Usage

Check for alignment issues:

$ fieldalignment ./...

Automatically fix alignment issues:

$ fieldalignment -fix ./...

Example

Before optimization:

type Example struct {
    flag   bool   // 1 byte + 7 bytes padding
    count  int64  // 8 bytes
    active bool   // 1 byte + 7 bytes padding
    value  int64  // 8 bytes
}
// Total: 32 bytes

After running fieldalignment -fix:

type Example struct {
    count  int64  // 8 bytes
    value  int64  // 8 bytes
    flag   bool   // 1 byte
    active bool   // 1 byte + 6 bytes padding
}
// Total: 24 bytes (25% reduction!)

For the visualization of struct packing see http://golang-sizeof.tips/

Note: While betteralign can reduce memory usage, reordering fields may affect:

  • Readability (logical grouping of fields)
  • API compatibility (if struct is part of public API)
  • Serialization formats (if field order matters)

Dupl

dupl is a tool written in Go for finding code clones. So far it can find clones only in the Go source files.

Due to the used method dupl can report so called “false positives” on the output. These are the ones we do not consider clones (whether they are too small, or the values of the matched tokens are completely different).

Installation

$ go install github.com/mibk/dupl@latest

Example

The reduced output of this command with the following parameters for the Docker source code looks like this.

$ dupl -t 200 -html > docker.html

Interfacer

interfacer is a linter that suggests interface types. In other words, it warns about the usage of types that are more specific than necessary.

$ go install github.com/mvdan/interfacer/cmd/interfacer@latest

Usage

func ProcessInput(f *os.File) error {
  b, err := ioutil.ReadAll(f)
  if err != nil {
    return err
  }
  return processBytes(b)
}
$ interfacer ./...

foo.go:10:19: f can be io.Reader

Goconst

goconst finds repeated strings that could be replaced by a constant.

Installation

$ go install github.com/jgautheron/goconst/cmd/goconst@latest

Examples:

$ goconst ./...
$ goconst -ignore "yacc|\.pb\." $GOPATH/src/github.com/cockroachdb/cockroach/...
$ goconst -min-occurrences 3 -output json $GOPATH/src/github.com/cockroachdb/cockroach
$ goconst -numbers -min 60 -max 512 .

Gosec - Golang Security Checker

gosec inspects source code for security problems by scanning the Go AST.

Installation

$ go install github.com/securego/gosec/v2/cmd/gosec@latest

Git Hook - Pre-commit

<project-root>/.git/hooks/pre-commit

#!/usr/bin/env bash

fmtcount=`git ls-files | grep '.go$' | xargs gofmt -l 2>&1 | wc -l`
if [ $fmtcount -gt 0 ]; then
    echo "Some files aren't formatted, please run 'go fmt ./...' to format your source code before committing"
    exit 1
fi

vetcount=`go tool vet ./ 2>&1  | wc -l`
if [ $vetcount -gt 0 ]; then
    echo "Some files aren't passing vet heuristics, please run 'go vet ./...' to see the errors it flags and correct your source code before committing"
    exit 1
fi

# Ensure FIXME lines are removed before commit.
fixme_lines=$(git diff --cached | grep ^+ | grep -v pre-commit | grep FIXME | sed 's_^+\s*__g')
if [ "$fixme_lines" != "" ]; then
    echo "Please remove the following lines:"
    echo -e "$fixme_lines"
    exit 1
fi
  • Catches go fmt, go vet, and FIXME comments. Custom tailor to your use.

Exercise - 10 Minutes

Using golangci-lint, run it against one of your local repos, or find one online to run against.

Installation: https://golangci-lint.run/welcome/install/

A sample repo to run against could be Pop

Mac/Linux

# Clone a sample repository
git clone https://github.com/gobuffalo/pop
cd pop

# Run golangci-lint with default linters
golangci-lint run

# Try with revive enabled
golangci-lint run -E revive

# Try with multiple linters
golangci-lint run -E revive -E gosec -E misspell

Windows

# Clone a sample repository
git clone https://github.com/gobuffalo/pop
cd pop

# Run golangci-lint with default linters
golangci-lint run

# Try with revive enabled
golangci-lint run -E revive

# Try with multiple linters
golangci-lint run -E revive -E gosec -E misspell