# wemake-python-styleguide: a strict flake8 plugin that sits next to ruff

> wemake-python-styleguide is a flake8 plugin that enforces complexity limits, naming rules and error-prevention checks on Python code. It is opinionated by design, and the README is explicit about what it will not do.

**wemake-services/wemake-python-styleguide** — The strictest and most opinionated python linter ever!

- Repository: https://github.com/wemake-services/wemake-python-styleguide
- Website: https://wemake-python-styleguide.rtfd.io
- Stars: 2,915 · Forks: 432
- Language: Python
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/wemake-services-wemake-python-styleguide

## What wemake-python-styleguide adds that flake8 and ruff do not

flake8 has always been a host for plugins, and wemake-python-styleguide registers itself as one: the pyproject.toml declares a flake8.extension entry point named WPS pointing at wemake_python_styleguide.checker:Checker. That single line is the whole integration story. There is no separate runner, no daemon, no editor protocol. You install the package and flake8 gains a new family of codes.

The README's comparison table is the clearest statement of intent. It marks the project as not formatting code, not finding style issues in the broad sense, but as finding bugs and finding complex code, with a lot of strict rules and a lot of plugins. The two rows that matter are complexity and strictness, because those are the rows where flake8, black, mypy and ruff are all marked as not doing the job. The project's stated objectives are to reduce complexity, to enforce the idea that there should be one obvious way to do something, and to protect developers from errors.

Who is this for? Teams that already run ruff for formatting and fast linting and want a second, slower pass that argues about structure. The README frames the tool as "the only one you will need as your ruff companion" and claims full compatibility with all rules and format conventions from ruff, so the intended workflow is additive rather than a replacement. The project also positions itself for AI-assisted work: it ships a /wps skill under .agents/skills/wps, an MCP server documented in the CLI docs, and llms.txt files for context. If your team reviews agent-generated Python, that is a concrete reason to look at it beyond ordinary linting.

## How the flake8 plugin mechanism actually works

The architecture is conventional for a flake8 plugin, and the repository layout confirms it. wemake_python_styleguide/ holds the checker and a visitors/ast directory; the Makefile runs a script called scripts/check_generic_visit.py against that directory, which implies the rules are implemented as AST visitors that are checked for correct traversal. The checker receives the parsed module from flake8 and walks it with those visitors, emitting violations with WPS-prefixed codes.

Because the work happens on the AST rather than on text, the rules can see structure: how deeply branches nest, how many statements a function holds, whether a name shadows a builtin, whether an expression is written in a way that hides a bug. That is the difference between this tool and a line-length checker. It also explains the cost: parsing and visiting every file is slower than a regex pass, and the README's own recommended command sequence puts ruff first and wemake second, which reads as an acknowledgement of that ordering.

The violations are documented by family in the docs under pages/usage/violations/index.html. Two families show up in real search traffic: WPS110, which people search for when a variable name is rejected, and WPS338, which appears in the same list. The existence of those searches is itself informative. Users hit a naming rule, do not immediately see why, and go looking. The MCP server is the project's answer to that: the README says it helps explain violations and provide context on how to fix them. That is a more useful framing than a rule list, because a strict linter's real cost is the time spent understanding each rejection.

## Installing wemake-python-styleguide and running it on one file

Installation is a single pip command, and the README notes that you will also need a setup.cfg with the configuration described in the usage docs. That second step is not optional in practice: the default rule set is large, and the configuration page exists because most teams need to turn parts of it off.

```bash
pip install wemake-python-styleguide
```

After that, flake8 knows about the plugin. The README's running example selects only the WPS codes, which keeps the output focused on this tool and leaves the rest of flake8's checks to whatever else you have configured:

```bash
flake8 your_module.py --select=WPS
```

Expect a first run on existing code to be noisy. The project is explicit that strictness is the point, and the docs describe how to ignore violations you do not want. A typical adoption path is to run the command above, pick the codes you disagree with, and record them in setup.cfg rather than in inline comments, so the decision is visible in review.

The README recommends pairing it with ruff and gives the intended order. Ruff handles formatting and its own checks first; wemake runs after.

```bash
ruff check && ruff format
flake8 . --select=WPS
```

There is also a CLI entry point. The pyproject.toml declares a script named wps mapped to wemake_python_styleguide.cli.cli_app:main, so the package installs a wps command in addition to the flake8 plugin. The README points to the CLI docs for the MCP server and the AI integrations rather than spelling out flags, so treat the docs as the source for those. For a quick look without installing anything, the README links an online playground at wps.orsinium.dev.

## Where the strictness becomes a liability

The README has a section titled "What we are not", and it is unusually candid. The project does not assume or check types and tells you to use mypy alongside it. It does not format code and tells you to use ruff format. It does not check for logical bugs in general and tells you to write tests. And it says it does not appeal to everyone, with the escape hatch being that any rule can be switched off.

That last point is the honest summary of the trade-off. A linter with a large rule set and a per-rule disable mechanism tends to converge, in real projects, on a setup.cfg that encodes the team's opinions rather than the project's. The rules you keep are the ones you already agreed with. The ones you disable are the ones that would have changed your code. That is not a flaw unique to this tool, but it is worth knowing before you present it as an objective standard.

The README also recommends ondivi specifically for integrating into a legacy codebase. That recommendation is itself a limitation statement: without something like it, turning on WPS across a large existing repository produces a violation count that no team will work through. New projects and small modules are the natural fit. A ten-year-old Django monolith is not, unless you scope the run to changed files.

There is a maintenance dimension too. The plugin pins flake8 to ^7.3 and Python to ^3.10 in pyproject.toml, and the Dockerfile builds on python:3.14.7-alpine with WPS_VERSION set to 1.8.1. Those pins mean the tool moves with flake8's plugin API, not independently of it.

## wemake-python-styleguide versus ruff and pylint

The comparison that matters is with ruff, because the README itself frames the two as companions. Ruff is a single fast binary that formats code, implements a large portion of flake8's rules, and runs its checks in one pass. wemake-python-styleguide is a flake8 plugin written in Python that does not format and does not try to cover flake8's own rules. The README's table puts a checkmark next to ruff for formatting, style issues, bugs and strict rules, and next to wemake for bugs, complex code and strict rules only.

The practical difference is the rule set, not the category. Ruff's flake8-compatible rules cover a known subset; wemake adds its own families on top, particularly around complexity and naming, and the project's claim is that these are the rules ruff does not have. Whether that claim holds for your codebase is testable in an afternoon: run ruff alone, then run flake8 with --select=WPS, and look at what the second command reports that the first did not. If the answer is nothing, you do not need this plugin.

Pylint is the other reference point. Pylint is a standalone checker with its own runner and its own configuration format, and the README's table marks it as finding style issues and bugs and as having somewhat strict rules. Choosing between them is largely a question of which runner you already have. If flake8 is in your CI, wemake is a plugin install. If it is not, adopting wemake means adopting flake8 as well, which is a larger change than it first appears. The README's position is that this is fine, since the app "is still just good old flake8" and will not change your existing workflow.

## Licence, releases and what upgrades cost

The project is MIT licensed, declared both in pyproject.toml and in the LICENSE file at the repository root. MIT is permissive: you can use it commercially, modify it and ship it inside a product, provided the copyright notice and permission notice travel with it. That is the standard reading, and nothing in the repository adds a clause on top. This is not legal advice; if you vendor the code or bundle it into a distributed product, have your own counsel read the LICENSE file rather than this paragraph.

Release cadence is visible from the changelog and the release list. Version 1.8.1 landed on 2026-09-12, 1.8.0 on 2026-08-22, and 1.7.1 on 2026-07-31. The last push to the repository was on 2026-09-23. That is a steady stream of patch and minor releases, and the CHANGELOG.md at the root is where the rule changes are recorded.

The upgrade cost is the part teams underestimate. A linter upgrade can add violations to code that passed yesterday, and this project adds rules across releases. Because the whole rule set is configurable through setup.cfg, the mitigation is to pin the version in CI, read the changelog entry for each new rule, and decide per rule whether to fix the code or ignore the code. The pyproject.toml pins flake8 to ^7.3, so a flake8 major release is also a wemake release you will have to schedule.

## Conclusion

Adopt it if you want complexity and naming rules that ruff does not ship, and if you are willing to maintain a setup.cfg with per-rule ignores. Do not adopt it expecting formatting, type checking or a quiet first run: the project states it is not for everyone, and a legacy codebase will need ondivi or a long ignore list. Before committing, run flake8 on one module with --select=WPS and read the WPS1xx naming codes, since those are the ones teams most often disable.

## FAQ

### Is wemake-python-styleguide a flake8 plugin or a standalone linter?

It is a flake8 plugin. The pyproject.toml registers a flake8.extension entry point named WPS, and the README states that the app is still just good old flake8. It also ships a separate wps CLI entry point.

### Can wemake-python-styleguide replace ruff?

No. The README presents it as a ruff companion and says it is fully compatible with all rules and format conventions from ruff, while the comparison table shows wemake as not formatting code. The recommended sequence runs ruff first and flake8 with --select=WPS after.

### How do I install wemake-python-styleguide?

The README gives pip install wemake-python-styleguide as the install command, and notes that you will also need a setup.cfg file with the documented configuration.

### What licence does wemake-python-styleguide use?

MIT. The licence is declared as MIT in pyproject.toml and the LICENSE file sits at the repository root.

### Does wemake-python-styleguide check types or format code?

Neither. The README's "What we are not" section says the project does not assume or check types and directs users to mypy, and that it does not format code or produce stylistic errors, directing users to ruff format.

### What is wemake-python-styleguide good for in a legacy codebase?

The README recommends ondivi for easy integration into a legacy codebase, which suggests running the linter on changed code rather than the whole repository. The README does not document a rollback procedure for the linter itself.

## Sources

- [License: MIT](https://github.com/wemake-services/wemake-python-styleguide/blob/master/LICENSE)
- [Project website](https://wemake-python-styleguide.rtfd.io)
- [README](https://github.com/wemake-services/wemake-python-styleguide/blob/master/README.md)
- [Releases](https://github.com/wemake-services/wemake-python-styleguide/releases)
- [wemake-services/wemake-python-styleguide on GitHub](https://github.com/wemake-services/wemake-python-styleguide)

---

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