Skip to content

aagumin/uberlint

Repository files navigation

uberlint

Uber Go Style Guide, enforced by golangci-lint.

Disclaimer: uberlint is an independent open source project. It is not affiliated with, endorsed by, sponsored by, or connected to Uber Technologies, Inc.

uberlint is a focused set of Go analyzers for teams that like the Uber Go Style Guide but do not want to rely on memory, review comments, or tribal knowledge to enforce it. It catches the style-guide rules that common golangci-lint setups usually miss: panic usage, oversized channel buffers, zero-valued enums, pointer-to-interface types, raw sync/atomic calls, and more.

The goal is simple: make the preferred Go style automatic, reviewable, and CI-friendly.

Why Use It

Code review should be about design, correctness, and trade-offs, not repeatedly asking for &T{} instead of new(T) or for enum values to start at iota + 1.

uberlint gives you:

  • Uber Go Style Guide coverage for rules that are missing or only partially covered by standard linters.
  • Native golangci-lint integration through the module plugin system.
  • Small, explainable analyzers built on golang.org/x/tools/go/analysis.
  • Rule selection through plugin settings, so teams can adopt strictness gradually.
  • Standalone runner support for development and debugging outside golangci-lint.

Included Linters

Linter Checks
ifaceptr Avoid pointers to interfaces.
chansize Keep channel buffer sizes at 0 or 1.
enumstart Start iota-style enums at a non-zero value.
nopanic Avoid panic() in production code.
atomicstd Prefer typed sync/atomic values over raw atomic functions.
stringbytes Avoid repeated []byte("literal") conversions inside loops.
vartype Omit redundant top-level var types when the initializer already provides the type.
globalprefix Prefix unexported top-level globals with _.
nilslice Return nil instead of empty slices where nil is sufficient.
rawstring Prefer raw string literals when they avoid quote escaping without changing semantics.
zerovar Use var x T for zero-value structs instead of x := T{}.
newref Prefer &T{} over new(T) for struct references.
publicembed Avoid embedded fields in exported structs.
embedlayout Keep embedded fields grouped before named fields.
localvar Prefer := for local variables with inferred types.
zerofields Omit explicit zero-value fields in struct literals.
mapinit Prefer make(map[K]V) for empty map initialization.
constprintf Use constants for printf format strings stored outside call sites.
nakedparams Avoid calls with multiple naked literal arguments.
timefield Require unit suffixes for serialized numeric time fields.

Quick Start

Use uberlint in your Go project as a golangci-lint module plugin. The setup has two files:

  • .custom-gcl.yml tells golangci-lint how to build a custom binary with uberlint inside.
  • .golangci.yml enables the uberlint rules your project wants to run.

After that, you run ./custom-gcl run ./... instead of the stock golangci-lint run ./....

Requirements:

  • Go
  • git
  • golangci-lint v2 with golangci-lint custom

In the Go project you want to lint, add .custom-gcl.yml:

version: v2.0.0
name: custom-gcl
plugins:
  - module: github.com/aagumin/uberlint
    version: v0.1.6

If you are trying a local checkout before a release is available, use path instead:

version: v2.0.0
name: custom-gcl
plugins:
  - module: github.com/aagumin/uberlint
    path: /absolute/path/to/uberlint

Then add .golangci.yml:

version: "2"

linters:
  default: none
  enable:
    - uberlint
  settings:
    custom:
      uberlint:
        type: "module"
        description: "Uber Go Style Guide linter"

Build and run:

golangci-lint custom -v
./custom-gcl run ./...

The first command reads .custom-gcl.yml and builds a local custom-gcl binary. The second command runs that binary with the uberlint plugin enabled by .golangci.yml.

Important: enable uberlint, not individual analyzer names like nopanic or ifaceptr. The module plugin registers one golangci-lint linter named uberlint; that linter runs the analyzers listed below.

Selecting Rules

golangci-lint sees one custom linter named uberlint. Individual checks are selected through plugin settings.

To run only specific analyzers:

version: "2"

linters:
  default: none
  enable:
    - uberlint
  settings:
    custom:
      uberlint:
        type: "module"
        description: "Uber Go Style Guide linter"
        settings:
          enable:
            - nopanic
            - ifaceptr
            - enumstart

To run all analyzers except a few:

version: "2"

linters:
  enable:
    - uberlint
  settings:
    custom:
      uberlint:
        type: "module"
        description: "Uber Go Style Guide linter"
        settings:
          disable:
            - nakedparams
            - constprintf

Use either enable or disable, not both. Unknown analyzer names fail the run with an explicit error.

Troubleshooting

plugin "uberlint" not found

This means the custom-gcl binary was built without the uberlint module plugin registration. Check these points:

  • .golangci.yml must enable uberlint, not individual analyzer names:
linters:
  enable:
    - uberlint
  settings:
    custom:
      uberlint:
        type: "module"
        description: "Uber Go Style Guide linter"
  • Rebuild the custom binary after changing .custom-gcl.yml or upgrading uberlint:
rm -f ./custom-gcl
golangci-lint custom -v
./custom-gcl run ./...
  • If diagnostics still look stale after upgrading, for example zerovar: zerovar: ..., clear the golangci-lint cache and run again:
./custom-gcl cache clean
./custom-gcl run ./...
  • If you are testing a local checkout, make sure .custom-gcl.yml points to that checkout with path.

CI Example

For CI, build the custom binary and run it like any other golangci-lint command:

name: lint

on:
  pull_request:
  push:
    branches: [main]

jobs:
  golangci-lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: "1.25"

      - name: Install golangci-lint
        run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest

      - name: Build custom golangci-lint
        run: golangci-lint custom -v

      - name: Run linters
        run: ./custom-gcl run ./...

For faster CI, cache Go modules and the golangci-lint cache using your CI provider's normal Go caching setup.

Standalone Runner

For development, you can also run uberlint without golangci-lint:

go run ./cmd/uberlint ./...

This is useful when developing new analyzers or debugging a rule. For normal project adoption, prefer the golangci-lint module plugin path so output, CI behavior, exclusions, and editor integrations stay consistent with the rest of your lint setup.

Suppressing Findings

Use normal golangci-lint suppression comments when a rule is intentionally not applicable:

//nolint:enumstart // zero is the wire-compatible default
const (
	Unknown Status = iota
	Ready
	Failed
)

Keep suppressions narrow and documented. A suppression should explain why this instance is exceptional, not why the rule is inconvenient.

Current Status

uberlint currently ships 20 analyzers covering the first two implementation slices of the Uber Go Style Guide. The analyzers are tested with analysistest, and the project also includes a standalone multichecker binary for local development.

Run the project tests with:

go test ./...

Links

About

Uber golang style guide linter.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages