DEV Community

Jeff
Jeff

Posted on Originally published at powerduck.com

Custom Lint Rules and Decorators: Encoding Team Conventions in the Spec

Quick answer: Built-in linting catches generic mistakes, but every team has conventions that generic rules do not know, naming standards, required headers, versioning rules, and which operations an agent may expose. Custom lint rules and decorators encode those conventions into the spec pipeline so a violation fails the build instead of relying on review. The discipline is to treat a repeated review comment as a candidate for a rule: if a human keeps checking the same thing, the linter should check it. Rules turn tribal knowledge into executable, version-controlled constraints that apply consistently to every author and every pull request.

Generic linting is the floor, not the ceiling. A default ruleset catches missing descriptions and obvious structural problems, but it does not know that this team requires an owner on every operation, that internal endpoints must be tagged a certain way, or that deprecated fields must carry a sunset date. Without custom rules, those conventions live in people's heads and surface inconsistently in review. The shift is to codify them. A custom rule is a small, testable function over the document, and a decorator extends how the document is processed, both kept in the repository alongside the spec they govern.

What to encode as a custom rule

The strongest candidates are the checks that already happen repeatedly and objectively.

Convention Rule behavior Failure result
Naming standards Check operation and field patterns Non-conforming name fails
Required headers Every operation has a request id Missing header fails
Owner metadata Each path declares an owner Unowned path fails
Deprecation policy Deprecated items carry sunset Incomplete deprecation fails
Agent exposure Allowed operations only Undocumented tool fails

Write a rule as a small function

A lint rule receives the document and returns structured problems, so the message tells the author exactly what to fix. Keeping it focused and deterministic makes it easy to test.

// rule: every operation must declare an owner and a request id parameter function requireOwnerAndRequestId(operation, path, method) { const problems = []; if (!operation["x-owner"]) { problems.push(${method} ${path} is missing required x-owner metadata); } const hasRequestId = (operation.parameters || []).some( (p) => p.name === "X-Request-Id" && p.in === "header" ); if (!hasRequestId) { problems.push(${method} ${path} requires an X-Request-Id header parameter); } return problems; } // the linter runs this across every operation and fails the build on // any returned problem, so the convention is enforced rather than requested

Use decorators for processing, not just validation

While a rule reports problems, a decorator transforms the document during processing, for example injecting a standard header into operations or shaping the generated MCP tool list. Keeping decorators explicit and versioned makes the transformation reviewable instead of a hidden build step.

# declarative decorator configuration; the transformation is data and # runs as part of the documented build rather than a manual edit decorators: - name: inject-request-id target: operations - name: filter-agent-operations config: allow: [getOrder, listOrders, createOrder, captureOrder]

Test the rules themselves

A lint rule is code, so give it fixtures: a valid document and a violating one, and assert the rule stays silent on the first and reports the right problem on the second. Run the whole ruleset in CI against the spec so a broken convention is caught before merge. A local-first workflow keeps the rules, decorators, and spec together, so the conventions evolve in the same review process as the API they describe. This is how a team scales consistency without scaling the number of things reviewers must remember.

Frequently asked questions

How many custom rules should we start with?

A handful covering the conventions that already cause repeated review comments. Adding rules for every preference creates noise; codify the objective, recurring checks first and expand as patterns emerge.

What is the difference between a rule and a decorator?

A rule validates and reports problems; a decorator transforms the document during processing. Use rules for constraints and decorators for consistent, automated changes.

Can custom rules enforce business semantics?

They can enforce structural and metadata conventions reliably, but not runtime behavior. Keep rules focused on what the document can express, and verify actual behavior through tests.

How do we avoid rules that block legitimate work?

Test rules against both valid and invalid fixtures, write clear messages, and make them version-controlled so they can be refined in review. If a rule is wrong, fix it in the repository rather than teaching people to work around it.

This is part of the Powerduck vs Redocly comparison — start there for the full decision around OpenAPI linting and documentation tooling.

Top comments (0)