# go-critic: nearly 100 Go checks with opinionated rules, tag-based filtering and dynamic ruleguard extensions

> go-critic adds diagnostic, style, performance and opinionated checks that other Go linters omit, and extends its rule set via ruleguard without recompiling. The #security tag in the README is listed alongside functional tags but is empty and causes a failure if enabled.

**go-critic/go-critic** — The most opinionated Go source code linter for code audit.

- Repository: https://github.com/go-critic/go-critic
- Website: https://go-critic.com
- Stars: 2,081 · Forks: 144
- Language: Go
- License: MIT
- Published: 2026-10-11 · Updated: 2026-10-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/go-critic-go-critic

## Five check groups covering bugs, style, performance, opinionated conventions and experimental rules

go-critic occupies the space between standard Go linters and a full code audit: it adds checks for bugs, style and performance that more common tools leave uncovered. The project description calls it the most opinionated Go source code linter for code audit.

Five tag groups organize the check set. `#diagnostic` catches bugs, `#style` flags style issues, and `#performance` identifies potential performance problems. Nearly 100 individual checks span these three stable groups, according to the README. Beyond the stable three, `#opinionated` marks checks where the project takes a firm position on Go style that not every team will share. `#experimental` covers checks still under active development.

Each checker's documentation is accessible through the tool itself. Running `go-critic doc <checkname>` prints what the check enforces and what it flags. All checkers with their group memberships appear at https://go-critic.com/overview, which is the reference to consult before enabling a new group in a configuration, since the inline README lists only group names and not individual check names.

## Installing go-critic standalone via go install, Homebrew or MacPorts

golangci-lint bundles go-critic and is where most teams encounter it; the standalone binary is for users who want to run go-critic outside that context. For standalone use, the standard Go install path is:

```bash
go install -v github.com/go-critic/go-critic/cmd/go-critic@latest
```

After installation, the binary must be on `$PATH` before the tool runs. Two additional paths exist for macOS. Via Homebrew:

```zsh
brew install go-critic
```

Via MacPorts:

```zsh
sudo port install go-critic
```

Building from source requires cloning the repository and running `make go-critic`. The go.mod file declares `go 1.25.0` as the module's minimum Go version, so the build environment must supply at least that version.

Running `go-critic` without arguments prints the help output, which covers three sub-commands: check, version and doc. To run all stable checkers across the current module:

```bash
go-critic check ./...
```

## The check sub-command: enabling and disabling checkers by name and by tag

The `check` sub-command takes package paths, package names and file names as targets. Checkers are selected with the `-enable` and `-disable` flags, accepting either individual checker names or tag names prefixed with `#`.

To run two specific checkers on one package:

```bash
go-critic check -enable elseif,paramName fmt
```

To run all stable checkers while excluding one by name:

```bash
go-critic check -disable=appendAssign ./...
```

To run stable diagnostic checks on the `strings` package while skipping experimental ones:

```bash
go-critic check -enable='#diagnostic' -disable='#experimental' strings
```

The `-enableAll` flag activates every checker in every group. Pairing it with `-disable` gives fine control. To run everything stable and non-opinionated:

```bash
go-critic check -enableAll -disable='#experimental,#opinionated' ./src/...
```

Per-checker configuration uses the `-@` syntax. To set the rangeExprCopy checker's size threshold to 128:

```bash
go-critic check -@rangeExprCopy.sizeThreshold 128 fmt
```

Running `go-critic doc <checkerName>` lists the available configuration keys for that checker before adding it to a configuration.

## The #security tag is documented like the others but contains no checks and fails if enabled

go-critic's README documents six tags in a single table: `#diagnostic`, `#style`, `#performance`, `#experimental`, `#opinionated` and `#security`. Five of these either run by default or can be activated cleanly. The sixth, `#security`, is a different case.

The README notes, in the same table row where it defines the tag, that `#security` is "disabled by default and empty, so will fail if enabled." Enabling `#security` through the check sub-command does not run zero checks and return a clean exit. It produces a failure.

This distinction is easy to miss on a first read. All six tags are in the same list with the same formatting. Nothing in the table visually distinguishes `#security` from `#performance`. The failure behavior appears only in the description text of that row, and only at runtime if someone enables it.

For configurations that use `-enableAll`, this means explicitly listing `#security` in the disable flag alongside any other groups to exclude. The README offers no explanation for why a tag with no checks is documented alongside functional ones.

## ruleguard integration allows project-specific checks without recompiling the binary

go-critic supports dynamic rules through go-ruleguard. The go.mod file lists `github.com/quasilyte/go-ruleguard v0.4.5` as a direct dependency. Rules written in Go syntax extend go-critic's check set at run time, with no fork and no recompile.

The capability is described in the README as extending the rule set without a recompile step; the ruleguard blog post at quasilyte.dev covers the rule-writing API. A team can write rules that enforce project-specific conventions, such as a required error-wrapping pattern, and run them alongside the built-in checks in one pass.

This differs from writing a golangci-lint plugin, which compiles into a shared library. Because ruleguard rules are plain Go files, they can be committed to the repository and picked up on every run without a separate build step for the linter. A change to a rule file takes effect the next time go-critic runs, making the feedback loop for new rules short.

The `#experimental` group in go-critic itself uses a similar isolation principle: experimental checkers are kept separate from stable ones so a problem in a new checker does not affect the stable run.

## The #opinionated group enforces strict conventions; its full check list lives only at go-critic.com

The `#opinionated` group is where go-critic diverges most from standard linting: the README warns that these checks can be unwanted for some projects, and they are disabled by default. The README does not list which individual checks belong to this group inline; that information is at https://go-critic.com/overview.

For a team with an established style guide that the opinionated checks happen to match, enabling them selectively can add coverage beyond the stable set. For a team inheriting a large existing codebase, enabling them all at once will surface a large number of findings on code that predates those conventions.

Getting the full check list at go-critic.com before enabling the `#opinionated` group is the only way to know what you are turning on. The inline README gives the category description but not the member checks. For `#experimental`, the README offers more comfort: experimental checkers are isolated from stable ones, so an unstable checker does not affect the stable run. Running `go-critic doc` without arguments prints the full list of installed checkers for the installed binary, which is a useful reference when auditing an existing configuration.

## go.mod sets Go 1.25.0 as the minimum and tag-based configurations pick up new stable checks after an upgrade

go-critic reached v0.15.0 on 2026-09-03 and is published under the MIT licence, with the last push on 2026-09-20. Three versions shipped across 2026, from v0.14.3 in January to v0.15.0 in September.

The go.mod file sets `go 1.25.0` as the module's minimum. In a golangci-lint setup, the version of go-critic bundled with golangci-lint may differ from the standalone binary's version. If a specific checker is needed that appeared only in a recent version, verifying golangci-lint's bundled go-critic version before relying on it is necessary.

The maintenance cost of tag-based configurations rises when the checker set changes across updates. A checker that moves from `#experimental` to stable becomes active in any configuration running all stable checks with `go-critic check ./...`. Configurations that enable checkers by explicit name are not affected, but configurations relying on tags pick up new stable checks automatically after an upgrade, which may produce new findings in CI without any explicit configuration change.

For golangci-lint users, checker selection sits in the golangci-lint configuration file alongside all other linter settings, keeping tool management in one place rather than split across two tools.

## Conclusion

go-critic suits Go teams who want more enforcement than golangci-lint's default set provides and are willing to configure explicitly which groups to enable. Skip it for projects where golangci-lint is already integrated and the default check selection is sufficient, since golangci-lint already bundles go-critic. Before enabling it in CI, confirm the build environment has at least Go 1.25.0, check which checkers the #opinionated group contains at https://go-critic.com/overview (the list changes between releases), and ensure tag-based configurations explicitly exclude #security, which contains no checks and fails on enable.

## FAQ

### What is go-critic and how does it differ from golangci-lint?

go-critic is a standalone Go linter that provides checks not covered by other tools, including opinionated style rules and experimental checks. golangci-lint integrates go-critic as one of its bundled linters, so most users run go-critic through golangci-lint rather than as a separate binary.

### How do I install go-critic?

Run `go install -v github.com/go-critic/go-critic/cmd/go-critic@latest` for the standard Go install. On macOS, `brew install go-critic` and `sudo port install go-critic` are also available. The go.mod file requires at least Go 1.25.0 to build.

### What does the #opinionated tag do in go-critic?

The #opinionated group marks checks where go-critic takes a firm position on Go style. These checks are disabled by default and must be enabled explicitly with -enable='#opinionated' or through golangci-lint configuration. The full check list is at https://go-critic.com/overview.

### What happens if I enable the #security tag in go-critic?

The README states that #security is empty and enabling it causes a failure. Teams using -enableAll must explicitly add #security to the -disable list to avoid this. The tag contains no checks at the current version.

### Can go-critic be extended with custom rules?

go-critic supports dynamic rules through its integration with go-ruleguard, listed as a direct dependency in go.mod as github.com/quasilyte/go-ruleguard v0.4.5. New checks are written as Go rule files and run alongside the built-in checks without recompiling the binary.

## Sources

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

---

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