KognitaKognita.

Blog

The Silent Convention Violation: Why AI Writes Correct Code That Breaks Your Patterns

9 min read

The PR passes linting. It passes tests. The reviewer approves it — it looks fine. Two weeks later, the tech lead notices: the new payment handler uses a different error handling pattern than every other handler in the service. The retry logic is implemented inline instead of using the shared retry module. The event is published directly instead of going through the event bus abstraction. Each decision was technically valid. None of them followed how the team does things.

The AI did not know. And nobody told it.

This is the silent convention violation problem. It is not a corner case. It is the default behavior of AI coding tools used on production codebases without codebase grounding. The code is correct. The patterns are wrong. And the mismatch is invisible until it compounds into something that causes an incident.

What conventions actually are

Conventions are the unwritten rules that accumulate in every team's codebase. They are not in the README. They are not in the style guide. They exist entirely in pattern — in the way every existing example of a thing is written, and the implicit expectation that new examples will follow the same approach.

Some conventions are about structure: where files live, how modules are named, what belongs in shared versus service-specific directories. Some are about behavior: how the team handles retries, how errors are captured and routed, what logging patterns are used. Some are about data flow: which abstractions are always used, which direct dependencies are deliberately avoided, how events move between services. Some are about testing: what to mock versus integration-test, where test fixtures live, how test setup is shared.

None of these are documented in a way an AI tool can find. They are documented in the codebase itself — in the consistency of existing implementations. A developer who has spent three months working in a service learns these conventions by reading code. An AI tool that has not indexed the codebase does not have that three months of exposure. It has the language specification, a model of general coding patterns, and whatever files are currently open in the editor. That is not enough to know that this team uses the event bus abstraction with typed event constants instead of publishing directly to redis.

Why AI tools violate conventions systematically

The failure mode is structural, not a product deficiency in any specific tool. AI coding assistants are trained on general-purpose code from the internet. They know how to write TypeScript, how to structure a service, how to handle retries. What they do not know — what they cannot know without being told — is how your specific team has resolved the dozens of small architectural decisions that accumulate over years of building a production system.

Consider retry logic. A model trained on general TypeScript code has seen hundreds of ways to implement retries: inline try-catch loops, exponential backoff libraries, custom retry wrappers, framework-provided retry decorators. All of them are correct in general. Only one of them — the approach your team settled on and consolidated into shared/lib/retry.ts — is correct for your codebase. The model has no way to know which one that is unless you tell it, and telling it through a rules file or prompt context requires knowing to tell it in the first place.

This is the key asymmetry: convention violations happen when the AI makes a valid choice that happens to diverge from the team's established pattern. The reviewer sees valid code. The linter sees valid code. The tests see valid code. Nobody sees the divergence from convention unless they already know the convention and are specifically checking for it — which reviewers almost never are, because review bandwidth is spent on logic and correctness, not pattern matching.

The four categories of silent convention violations

Convention violations cluster into four categories based on what kind of pattern is being violated. Understanding the categories helps teams identify where they are most exposed and what to watch for in AI-generated code.

The four categories of silent convention violations with concrete examples
Four categories of silent convention violations:

1. Structural patterns
   WRONG: retry logic implemented inline in payment-handler.ts
   RIGHT: import { withRetry } from '@/shared/lib/retry'
   Why AI does it wrong: the model knows retry patterns; it doesn't know
   your team consolidated retry logic into shared/lib/retry.ts six months ago

2. Error handling patterns
   WRONG: try { ... } catch (err) { console.error(err); throw err }
   RIGHT: import { captureServiceError } from '@/lib/error-tracking'
   Why AI does it wrong: logging to console is valid TypeScript; the model
   doesn't know your team routes errors through a centralized tracking module

3. Data flow patterns
   WRONG: await db.publish('order.created', { orderId, userId })
   RIGHT: await eventBus.emit(OrderCreatedEvent, { orderId, userId })
   Why AI does it wrong: direct publish is technically correct; the model
   doesn't know your team always uses the EventBus abstraction with typed events

4. Testing conventions
   WRONG: jest.mock('../../services/payment-service')
   RIGHT: import { createPaymentServiceMock } from '@/test/mocks/payment-service'
   Why AI does it wrong: manual mocking works; the model doesn't know
   your team centralized mocks to ensure consistency across test suites

Structural violations are the most common because they are the hardest for a reviewer to catch without intimate knowledge of the repository's organization. When a developer adds a utility function inline in a service file because it is convenient, the reviewer sees a utility function. They do not see that the team has a shared/lib/ directory where exactly this kind of utility belongs, because they are reviewing the diff, not auditing the file's organizational correctness relative to the entire repository.

Error handling violations are the most dangerous because they affect observability and incident response. When an error is logged with console.error instead of routed through the centralized error tracking module, it does not appear in the error tracking dashboard. During an incident, the team looks at the dashboard and sees no errors — because the code that is failing is using the wrong error handling pattern and its errors are going nowhere observable.

Data flow violations — bypassing abstractions like event buses, message queues, or API clients — are the most architecturally damaging. These abstractions exist because the team decided to centralize concerns: circuit breaking, rate limiting, audit logging, retry logic. When AI-generated code bypasses an abstraction and reaches the underlying system directly, it bypasses all of those centralized concerns. The code works correctly in development and passes tests. In production, under load, the missing circuit breaker or absent rate limiting creates the conditions for an incident.

Testing convention violations compound the problem by making it harder to catch the other violations in review. When test fixtures and mocks are inconsistently set up, reviewers spend cognitive energy understanding the test structure rather than reasoning about what is actually being tested. The review of AI-generated code becomes more difficult precisely because the testing conventions are not being followed.

Why they pass code review

Code review is not designed to catch convention violations. Review bandwidth is scarce, and reviewers allocate it to the things that matter most: does the logic work, are there security issues, is performance acceptable, are edge cases handled? These are the questions that require judgment and domain knowledge. They are the questions that junior reviewers miss and senior reviewers catch.

Convention enforcement requires a different kind of attention: not "is this code correct?" but "is this code consistent with how we do things?" That check requires the reviewer to hold the entire codebase's conventions in their head simultaneously while reading a diff. That is not a realistic expectation. Senior engineers who have been in the codebase for years can sometimes do it instinctively — they feel when something looks wrong. But they cannot reliably articulate what they are pattern-matching against, and they miss violations in areas of the codebase they do not own.

The result is that AI-generated code that violates conventions routinely passes review. The violations are not obvious from the diff. They are only obvious to someone who knows the convention, is specifically looking for it, and has enough review bandwidth to check for pattern consistency in addition to logic correctness. In practice, that is almost nobody.

Why this compounds with AI-heavy teams

Before AI coding tools became widespread, convention violations were occasional. A developer new to a service would sometimes implement something in an idiosyncratic way; a code review would catch it; the developer would learn. The rate of violation was bounded by how many developers were working in the codebase and how quickly they learned its conventions.

With AI coding tools, the rate of violation is bounded by how well each developer has configured their AI tool's context — which is to say, it is largely unbounded. A developer who has configured a detailed CLAUDE.md or a well-crafted rules file in Cursor will produce AI-generated code that follows more conventions. A developer on the same team who has not configured anything will produce AI-generated code that violates conventions at the full rate of an AI tool with no codebase knowledge. The team produces code in two or three or five different styles, all technically correct, none of them consistently following the team's established patterns.

The problem is that this configuration is per-developer. One developer's Cursor rules file that captures the event bus convention does not help the next developer's Claude Code session. There is no shared ground truth. The team's accumulated conventions — the architectural decisions that took years to converge on — live in the codebase itself, and each developer's AI tool accesses as much or as little of that codebase as the developer happens to have open in their editor at the time.

The convention drift trajectory

Convention violations do not cause immediate failures. That is what makes them dangerous. A single inline retry implementation does not break anything. The event bus bypass works correctly in development. The non-standard error handler logs to the right place in tests. The violations accumulate silently, each one small, each one approved, until enough of them interact to create an incident.

Convention drift trajectory over 90 days of AI-heavy development
Convention drift trajectory — what happens over 90 days
of AI-heavy development without shared codebase grounding:

Week 1–2:
  -> New payment handler uses inline retry instead of shared/lib/retry
  -> New event published directly to redis instead of EventBus
  -> Reviewers approve: code is correct, just different

Week 3–6:
  -> Another developer sees the inline retry and follows the pattern
  -> shared/lib/retry now has two competing implementations
  -> EventBus is bypassed in three places, breaking the circuit breaker assumptions

Week 7–10:
  -> On-call engineer investigating an incident notices the EventBus bypass
  -> Circuit breaker never fired during a redis outage because two publishers
     were not going through it
  -> Root cause: convention drift from AI sessions without grounding
  -> Fix requires audit of all recent AI-generated code touching events

Week 11–12:
  -> Tech lead mandates a convention review
  -> Retrospective action item: "document conventions in CLAUDE.md"
  -> CLAUDE.md is added, immediately starts going stale as patterns evolve
  -> The real problem — AI sessions don't know the codebase — is not solved

The incident that surfaces the drift is almost never diagnosed as a convention violation in the moment. The on-call engineer sees a retry storm, or missing circuit breaker behavior, or errors that should have appeared in the dashboard but did not. The root cause investigation eventually finds the AI-generated code that bypassed the shared abstraction. By that point, the violation has been in production for weeks, and several other violations in nearby code have the same root cause.

The post-incident action is usually something like "add conventions to the team's AI rules file." This helps — slightly. It captures the convention that was violated, for the engineers who read and configure their rules files, going forward. It does not surface the other convention violations already in production. It does not help engineers who do not read their rules files carefully. And it requires the team to know which conventions were violated before they can document them — which means the documentation always lags the violations.

Grounding AI sessions in codebase conventions

The fix is not better rules files. Rules files are text that an AI model can read but cannot verify against the actual codebase. A rules file can say "always use the event bus abstraction" — but if the model has not seen how the event bus is used throughout the codebase, it does not understand the convention well enough to apply it correctly in a new context. It knows the rule; it does not know the pattern.

Convention enforcement requires the AI model to have seen how similar things are done throughout the codebase, not just been told how to do them. The difference is between knowing that a rule exists and understanding the behavioral pattern the rule is trying to preserve. Models that have seen every event publisher in the service understand the event bus convention in a way that a rules file statement cannot convey.

Same task, two outputs: AI session without vs. with convention context
Task: add a new event when an order is fulfilled

--- Without convention context ---
AI session has: the current file, some TypeScript knowledge, no codebase grounding

AI generates:
  async function fulfillOrder(orderId: string) {
    await db.orders.update({ id: orderId, status: 'fulfilled' })
    await redis.publish('order:fulfilled', JSON.stringify({ orderId }))
    logger.info('Order fulfilled', { orderId })
  }

Problems introduced:
  -> Direct redis.publish instead of EventBus (bypasses circuit breaker)
  -> Inline logger instead of structured captureServiceEvent
  -> No retry wrapper on the DB update
  -> Event name is a string literal instead of a typed event constant

Code is correct. Linting passes. Tests pass. Review approves it.

--- With convention context (Kognita MCP) ---
AI session has: same file + semantic index of how events are published everywhere else

AI generates:
  async function fulfillOrder(orderId: string) {
    await withRetry(() => db.orders.update({ id: orderId, status: 'fulfilled' }))
    await eventBus.emit(OrderFulfilledEvent, { orderId })
    captureServiceEvent('order.fulfilled', { orderId })
  }

Matches the pattern used in every other order lifecycle event in the service.
No convention violations. No silent divergence.

When AI sessions are grounded in a managed semantic index — the kind Kognita maintains across the entire codebase, not just whatever files are open in the editor — the model has access to behavioral patterns, not just file content. A query about "how should I publish this event?" returns context about every other event publisher in the system: how they are structured, which abstractions they use, what the event payload conventions are. The model generates code that matches the team's actual patterns because it has seen those patterns, not because it was told about them.

The Jira MCP integration extends this to the planning layer. When a product manager writes a ticket for a new order lifecycle event, the Kognita-grounded AI can surface the relevant conventions before a single line of code is written. The ticket can reference how similar events are implemented, which modules will be affected, and what the testing pattern for this type of change looks like. Convention awareness moves earlier in the development cycle, from code review to specification.

The other key property of a managed index is that it stays current. Rules files go stale. CLAUDE.md files go stale. Documentation goes stale. The codebase does not go stale — it is the source of truth. A semantic index that is automatically re-indexed on every merge always reflects the current state of the team's conventions, including conventions that were established last week by a refactor that moved utility functions to a new shared module. No engineer needs to remember to update the rules file. The index updates itself.

Final take

Conventions are the most important code standards and the hardest to enforce. AI tools that do not know your conventions will violate them correctly and confidently. The PR will pass linting. The tests will pass. The reviewer will approve. The violation will land in production and stay there until it combines with other violations to create an incident nobody can immediately diagnose.

The fix is not more rules files, more thorough review, or more documentation. Those are the right instincts applied to the wrong layer. The fix is grounding — giving AI sessions access to the behavioral patterns that exist in the codebase itself, not a text description of those patterns. A model that has seen how your team handles retries, events, errors, and testing will write code that matches those patterns. A model that has only been told about them will miss the nuance.

The convention violations your team is seeing in AI-generated code are not a temporary problem that will be solved when AI models get better. They are a context problem that requires a context solution. The model needs to know your codebase — specifically, precisely, and always — not just the language it is written in.