# mgechev/revive: a configurable Go linter that replaces golint

> revive is a drop-in golint replacement for Go that adds TOML configuration, per-rule return codes, multiple output formatters and a rule framework. The trade-off is that you now have to decide which rules run.

**mgechev/revive** — 🔥 ~6x faster, stricter, configurable, extensible, and beautiful drop-in replacement for golint

- Repository: https://github.com/mgechev/revive
- Website: https://revive.run
- Stars: 5,553 · Forks: 331
- Language: Go
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/mgechev-revive

## What revive replaces, and who ends up maintaining the rule list

golint is frozen and its rule set is fixed. revive targets the same class of findings but moves the decisions into a configuration file. The README states that revive allows you to enable or disable rules using a configuration file and to configure the linting rules with a TOML file. That single change is the whole pitch: instead of arguing about whether a check should exist, you argue about whether it is enabled in your repository.

The audience is Go teams that already run a linter in CI and want the policy to be explicit. It is also aimed at people writing their own checks. The README says revive provides a framework for development of custom rules, and that everyone can extend it easily with custom rules or formatters. The repository layout backs this up: rule/, formatter/, config/, lint/ and revivelib/ are separate top-level directories, so rules and formatters are not buried inside the CLI.

The cost is real. A default golint-compatible run needs no configuration at all, but the moment you want strictness you own the rule list, and you own keeping it current as the project adds rules.

## How the linting pipeline is put together

The module is github.com/mgechev/revive, and go.mod declares go 1.26.0. Dependencies that shape the behaviour are github.com/BurntSushi/toml for reading the config, github.com/spf13/afero for filesystem access, golang.org/x/tools for parsing and type information, and codeberg.org/chavacava/garif, which is the SARIF library behind the SARIF formatter. The Dockerfile builds with CGO_ENABLED=0 and copies the resulting binary into a scratch image, so the shipped container is a static binary with no shell.

The interesting part is the type-checking switch. The README states that optional type checking exists, that most rules in golint do not require type checking, and that if you disable them in the config file revive will run over 6x faster than golint. So the pipeline is not one pass: rules that need types pull in the type checker, rules that only need syntax do not. That is why the same binary can be fast or slow depending on the rule set you enable, and why the repository carries both defaults.toml and untyped.toml. Those two files are different starting points, not documentation.

Output is a separate concern. The README lists formatters named Friendly, Stylish, Default, Plain, Unix, JSON, NDJSON, Checkstyle and SARIF. That range matters for CI: SARIF is what code scanning dashboards consume, while Stylish and Friendly are for humans reading a terminal.

## Installing revive and running it on a real tree

The README lists Homebrew, source installation, Docker and manual binary download. Source installation is the shortest path if you already have a Go toolchain, and it installs the latest stable release. To pin the main branch instead, the README gives a second form with @HEAD.

```bash
go install github.com/mgechev/revive@latest
```

After that, revive -version should print the binary version, which is the check the README gives for a manual download and works the same for a source install.

```bash
revive -version
```

With no flags, the README says the default behaviour is compatible with golint and the only difference you would notice is faster execution. The -config flag points at a TOML file describing which rules to use. If you pass no config, revive tries $HOME/revive.toml, and if that is missing it falls back to a built-in set of default rules.

```bash
revive --config revive.toml ./...
```

That invocation is the one the project's own Makefile uses in its lint target, alongside golangci-lint run. If you would rather not install anything, the README's Docker example mounts the working directory and passes the same flags.

```bash
docker run -v "$(pwd)":/var/YOUR_REPOSITORY ghcr.io/mgechev/revive:v1.16.0 -config /var/YOUR_REPOSITORY/revive.toml -formatter stylish ./var/YOUR_REPOSITORY/...
```

For editor integration, the README shows VS Code through vscode-go by setting go.lintTool to revive in settings.json, and vim through dense-analysis/ale with a g:ale_linters entry mapping go to revive. A GitHub Action with annotation support is also listed.

## Where revive gets in your way

The strongest feature is also the sharpest edge. Because rules are opt-in through configuration, an empty or partial revive.toml silently gives you a weaker linter than you think you have. Nothing in the README describes a warning for a config file that enables fewer rules than the defaults. If your team copies a config from another repository and never revisits it, the linter keeps passing and keeps missing things.

The speed claim is conditional, not universal. The over 6x figure is tied to disabling rules that need type checking; the 2x figure applies when running the same rules as golint. A configuration that enables the type-checking rules will not see that multiplier, and the README does not publish a table of per-rule costs, so the only way to know your own number is to measure your own rule set.

Comment directives are another trade-off. revive lets you disable a specific rule, or the entire linter, for a file or a range of lines, which golint only allowed for generated files. That is genuinely useful for legacy code, and it is also how a codebase accumulates suppressions that nobody removes. There is no expiry mechanism described.

Finally, revive is a Go linter. It does not lint other languages, and it is not a formatter. The Makefile pairs revive with golangci-lint fmt for formatting, which is a fair signal that formatting is out of scope.

## revive against golangci-lint, and against golint

golint is the baseline revive was built to replace, and the README is direct about the differences: configuration, TOML rule settings, comment directives beyond generated files, multiple formatters, customizable return codes, and more rules. If you are still on golint, the migration is a flag change plus a config file, and the default behaviour is compatible.

golangci-lint is a different kind of tool and a more interesting comparison. It is an aggregator: it runs many linters behind one command and one config. revive appears inside it as a linter you enable, and the README gives the golangci-lint configuration showing revive added under linters.enable. The difference in approach is that golangci-lint owns the orchestration and revive becomes one of several checks, while running revive directly means you own the invocation, the config path and the exit codes yourself. Neither is better in the abstract: if you already have a golangci-lint config with a dozen linters, adding revive there avoids a second CI step, but you configure revive through golangci-lint's file rather than a standalone revive.toml. If you want revive's own formatters, return-code control or a custom rule compiled into the binary, running it directly is the path the README describes.

## Maintenance, releases and the MIT licence

The repository is not archived, and the last push was on 2026-09-22. Releases are frequent enough to plan around: v1.16.0 on 2026-08-21, v1.15.0 on 2026-03-06, and v1.14.0 on 2026-02-10. There is a renovate.json in the repository root alongside .goreleaser.yml, which is consistent with automated dependency updates and tagged binary releases.

Upgrade cost depends on how you pinned things. Homebrew users run brew upgrade revive. Source users re-run go install github.com/mgechev/revive@latest. Docker users are pinned to a tag in the image name, and the README's example uses ghcr.io/mgechev/revive:v1.16.0, so a container-based CI job will not move until you change that tag. Users who installed from @HEAD get whatever the main branch holds, which is the option with the least predictable upgrade story and the one the README presents without a stability caveat.

The licence is MIT, which is permissive and imposes no copyleft obligation on your own code. That is a statement about the licence text, not legal advice; check how your organisation treats attribution notices before shipping binaries.

## Conclusion

Adopt revive if you are still running golint, or if you want lint policy in a file that reviewers can argue with in a pull request. Skip it if you already run golangci-lint and have no reason to configure revive separately, since golangci-lint can enable it as a linter. Before rolling it out, run revive -version, then try revive --config revive.toml ./... against your own tree and read the diff in findings before you wire it into CI.

## FAQ

### How do I install revive?

The README lists Homebrew with brew install revive, source installation with go install github.com/mgechev/revive@latest, a Docker image at ghcr.io/mgechev/revive, and manual binary downloads from the Releases page. After a manual download the README suggests verifying with revive -version.

### How do I configure which rules revive runs?

revive accepts a -config flag pointing at a TOML file that describes which rules to use. If you pass no config it looks for $HOME/revive.toml, and if that is absent it falls back to a built-in set of default rules.

### Does revive work as a drop-in replacement for golint?

The README states that revive's default behaviour is compatible with golint, so without extra flags the difference you notice is faster execution. The project also describes itself as a drop-in replacement of golint with configuration, formatters and more rules on top.

### Can I use revive together with golangci-lint?

Yes. The README shows enabling revive in the golangci-lint configuration by adding revive to the linters enable list, after which revive is configured through golangci-lint rather than a standalone revive.toml.

### How do I get revive output in a format my CI can read?

revive ships several formatters, including JSON, NDJSON, Checkstyle and SARIF, and the formatter is selected with the -formatter flag. The README's Docker example passes -formatter stylish for human-readable terminal output.

## Sources

- [License: MIT](https://github.com/mgechev/revive/blob/master/LICENSE)
- [mgechev/revive on GitHub](https://github.com/mgechev/revive)
- [Project website](https://revive.run)
- [README](https://github.com/mgechev/revive/blob/master/README.md)
- [Releases](https://github.com/mgechev/revive/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mgechev-revive
