Architecture guardrails: Bug → Codify → Lint Rule
A systematic process for converting recurring bugs into permanent lint rules turns each incident into an improvement to the quality gate, progressively hardening the codebase against known failure modes.
- ·AI review agent runs as a first-pass reviewer on every PR before human review
- ·Lint rules enforce architectural standards (not just style) - the "Bug to Codify to Lint Rule" pipeline is active
- ·At least 3 architectural guardrail rules have been created from past bugs or incidents
- ·AI review agent findings are categorized by severity (info, warning, blocking)
- ·New lint rules are proposed automatically when recurring review comments are detected
Evidence
- ·CI configuration showing AI review agent as required check
- ·Lint rule change history showing rules created from incident post-mortems
- ·AI review agent output logs with severity categories
What It Is
The Bug → Codify → Lint Rule process is a systematic practice where each recurring architectural violation or class of bug is converted into a machine-enforced constraint. When a bug caused by an architectural violation is found, the team doesn't just fix the bug - they codify the constraint as a custom lint rule so the same class of bug can never slip through review again.
The three-step cycle is:
- Bug - A bug caused by an architectural violation reaches production (or is caught in review). The team identifies the root cause: "We called the payment service directly from the UI layer, bypassing the validation layer."
- Codify - The team articulates the architectural constraint explicitly: "UI layer code must not import from
paymentmodule directly - it must go throughcheckout-service." This is documented as an architectural decision record (ADR). - Lint Rule - The constraint is implemented as a custom lint rule (import restriction, module boundary check, forbidden API usage). The rule is added to CI and fails any future PR that violates it.
At L3 (Systematic), this process is habitual - it happens after every incident where an architectural violation was the root cause. The result is a lint configuration that grows progressively more comprehensive, with each addition representing a real-world failure mode that has been permanently eliminated.
This is distinct from ad-hoc lint rule addition. The Bug → Codify → Lint Rule cycle is triggered by actual failures, not theoretical concerns. Every rule in the configuration represents something that actually went wrong, which means the rules have high relevance and clear justification.
Why It Matters
The compounding nature of this practice is its core value. Each iteration:
- Permanently eliminates a failure class - The bug that triggered the rule will never happen again via the same path. Not "we'll be more careful" - mechanically impossible to merge.
- Creates institutional memory in code - The lint rule with its comment ("Added after incident-2024-11: payment calls bypassing validation caused overcharging") is institutional memory that persists through team turnover.
- Makes the quality gate progressively stronger - A codebase with 50 custom lint rules based on real incidents has a dramatically higher quality floor than one with only default rules. This isn't a step function - it's continuous improvement.
- Enables faster review and AI generation - Every constraint that's machine-enforced is a constraint that reviewers don't need to mentally check. At L4-L5, where AI agents generate large amounts of code quickly, the lint rules serve as an automated safety net that catches AI code violating constraints the AI wasn't aware of.
- Surfaces architectural ambiguity - Sometimes the attempt to write a lint rule reveals that the architectural constraint isn't actually clear. "Don't call the database from the HTTP layer" - but what about health check endpoints? The act of codifying the rule forces the team to articulate the constraint precisely.
The process at L4 and L5 extends naturally: as AI agents generate more code, the lint rules protect against AI-specific failure modes. "AI agents tend to use direct SQL rather than the ORM in unfamiliar contexts" becomes a lint rule: "no raw SQL strings outside the migration files." The Bug → Codify → Lint Rule cycle applies to AI-generated bugs as much as human-generated ones.
Maintain a ARCHITECTURE_RULES.md alongside your lint configuration that maps each custom rule to the incident or decision that created it. This document becomes a valuable onboarding resource - new team members can read the history of architectural decisions and understand why each constraint exists.
Getting Started
- Define the process trigger - Establish that any incident or post-merge bug where the root cause includes "violated an architectural constraint" triggers the Bug → Codify → Lint Rule cycle. Add this as an item in your incident retrospective template: "Does this incident call for a new lint rule?"
- Start with your highest-frequency violations - Audit the last 6 months of PR review comments and post-mortem reports. What architectural violations appear most often? Start with the top 3 - these are the rules that will have the highest immediate impact.
- Write the ADR first - Before writing the lint rule, write the architectural decision record. What is the constraint? Why does it exist? What is the allowed pattern? What is the forbidden pattern? This discipline prevents writing lint rules that are ambiguous or that no one understands six months later.
- Involve the author in the rule-writing - The engineer who caused the bug (or made the architectural violation) is often well-positioned to write the lint rule. They understand the failure mode. This also turns incidents into learning opportunities rather than blame events.
- Celebrate each new rule - When a lint rule is added, announce it: "We've permanently eliminated the class of bug that caused incident X." This makes the process feel like progress rather than punishment.
- Review the rules quarterly - Are all existing rules still relevant? Have patterns changed enough that a rule now fires on legitimate code? Review the custom rule set quarterly to remove rules that have become obsolete and refine rules that are generating false positives.
Common Pitfalls
Writing rules for theoretical problems. The Bug → Codify → Lint Rule process is specifically triggered by actual failures. Writing lint rules for hypothetical future problems (without the evidence of a real incident) tends to produce rules that are too broad, poorly calibrated, or generate false positives. Reserve custom rules for real failure modes.
Not writing the rationale. A lint rule without a comment explaining its origin is orphaned institutional knowledge. Future team members will disable it when it seems wrong without understanding why it was added. Always write the comment: "Added after incident-YYYY-NN: [one sentence description]."
Stopping at "we'll add a check in code review." The most common failure mode of this process is the team agreeing that "reviewers will check for this from now on." This is an intention, not a guarantee. The process is only complete when the lint rule is deployed and verified to catch the original violation pattern.
Over-engineering the rule immediately. A lint rule that covers 95% of the failure mode with 10 lines of code is better deployed now than a perfect rule deployed in two weeks. Start with the common case, add edge case handling as the rule proves its value.
How Different Roles See It
Bob's team has had three separate incidents caused by code making direct database calls in HTTP handler functions. Each time, the post-mortem includes "we should be more careful about this." After the third incident, a senior engineer points out that they've said "be more careful" twice before. Bob wants to break the pattern.
What Bob should do: Bob should make the Bug → Codify → Lint Rule process explicit. After the third incident, he should assign someone to write the lint rule that prevents direct database calls in HTTP handlers. He should announce it to the team: "After three incidents with this pattern, we've added a lint rule. This class of bug is now mechanically impossible to merge." Bob should also add "check for lint rule opportunity" to the standard incident retrospective template. This institutionalizes the practice: the process doesn't depend on Bob noticing the pattern - it happens after every incident automatically.
Sarah tracks post-merge bugs as a quality metric. Her data shows that 30% of post-merge bugs over the past year fall into patterns that have appeared before - the same architectural violations causing different surface-level bugs. She wants to demonstrate that quality is improving, but the recurring pattern suggests it isn't.
What Sarah should do: Sarah should present the pattern data to Bob and the engineering leads as evidence that current review processes aren't preventing recurrence. The solution she should propose: the Bug → Codify → Lint Rule cycle, with a metric she'll track: "Number of active architectural guardrail rules" growing over time, and "Recurring bug class rate" (what percentage of bugs are repeats of a previous failure mode) decreasing. After 6 months of the practice, she should be able to show that the recurring bug class rate is trending down as the guardrail count grows up. This is a concrete demonstration that the quality gate is improving.
Victor has been writing the same review comment - "use the repository pattern, don't query the DB directly" - for two years. He's written it on 30+ PRs. After a production incident last month where a direct DB call caused a connection pool exhaustion under load, he's ready to automate it.
What Victor should do: Victor should write the lint rule now. He should also write an ADR documenting the constraint and referencing the incident. Then he should present the before/after to the team: "I've written this comment on 30 PRs over 2 years. The incident last month is the fifth time a violation caused a production issue. This lint rule means I never have to write that comment again, and those incidents can't happen." Victor should also propose making his process into a team process: "Going forward, when any of us writes the same review comment for the third time, we commit to writing a lint rule instead." That norm, if adopted, will produce a progressively hardening quality gate over the next year without requiring any centralized mandate.
Further Reading
From the Field
Recent releases, projects, and discussions relevant to this maturity level.
Where does your team actually sit on this?
This guide describes one level of one area. Run the assessment to place your team across all 16 areas, see which gates you have passed, and get a report you can take to your stakeholders.