Enforcement at the Gate
Writing a linter is only half the job. A rule that isn't enforced is just a suggestion. The standard pattern is to run your linter as a CI gate — a step in your pipeline that blocks merges when violations are found.
The key property that makes this work is the exit code. By convention, Unix tools exit with code 0 on success and a non-zero code on failure. Pylint exits with a non-zero code when it finds any violations. CI systems (GitHub Actions, GitLab CI, Jenkins) treat a non-zero exit code as a failed step, which blocks the pipeline.
A CI lint job is just a pipeline step that sets up the language runtime, installs the linter, and runs it against the source tree with the custom plugin loaded. If any file triggers a violation, that step fails, the PR is blocked, and the developer sees the exact line number and message in the CI output.
Packaging the plugin for team use:
For a plugin used across multiple repositories, the standard approach is to publish it as an internal Python package, structured like any other installable package with its own pyproject.toml. Teams then add that package to their dev dependencies and point their linter's config file at the plugin's module path to load it — which means the rule runs automatically for every developer who installs the dev dependencies, without requiring any special flags.
A common platform engineering interview question is: "How would you enforce a coding standard across 50 microservices owned by different teams?" The answer has three layers: (1) write the rule as an AST-based linter plugin rather than a regex script, (2) package it as an internal library so it's versioned and distributable, and (3) enforce it as a CI gate so it runs on every PR without relying on developer discipline. Mentioning that teams can suppress specific violations with # pylint: disable=rule-name shows you understand the balance between enforcement and developer autonomy.