- Cohesion vs. Coupling: Does the module handle a single bounded context, or does it mix orchestration, validation, persistence, and UI logic?
- Type Contract Integrity: Are interfaces modeling actual domain constraints, or are they using escape hatches like
any, unknown, or forced type assertions?
- Execution Topology: Are independent asynchronous operations batched, or are they awaiting sequentially?
- Testability Boundaries: Can the logic be isolated from external dependencies, or does it tightly couple to concrete implementations?
Modern AI development environments support version-controlled rule files that dictate agent behavior. These files replace inline prompts with persistent, auditable review contracts. The configuration should separate structural criteria from cosmetic checks, ensuring the agent prioritizes architectural findings.
Step 3: Implement Structural Code Examples
Below are rewritten examples demonstrating how structural review criteria translate into actionable code patterns.
Example A: Replacing Ad-Hoc Conditionals with Policy Objects
Instead of scattering type checks across business logic, encapsulate behavior in a policy registry:
interface AccessPolicy {
canViewDashboard: boolean;
canExportData: boolean;
maxConcurrentSessions: number;
}
const policyRegistry: Record<string, AccessPolicy> = {
admin: { canViewDashboard: true, canExportData: true, maxConcurrentSessions: 5 },
partner: { canViewDashboard: true, canExportData: false, maxConcurrentSessions: 2 },
viewer: { canViewDashboard: false, canExportData: false, maxConcurrentSessions: 1 },
};
function resolveAccessPolicy(role: string): AccessPolicy {
const policy = policyRegistry[role];
if (!policy) throw new Error(`Undefined access policy for role: ${role}`);
return policy;
}
Rationale: This eliminates branching logic, centralizes policy definitions, and makes behavior predictable. Adding a new role requires updating the registry, not modifying control flow.
Example B: Parallelizing Independent I/O Operations
Sequential awaiting of independent data fetches creates unnecessary latency:
interface UserContext {
profile: UserProfile;
settings: AccountSettings;
notifications: NotificationPreferences;
}
async function fetchUserContext(userId: string): Promise<UserContext> {
const [profile, settings, notifications] = await Promise.all([
fetchUserProfile(userId),
fetchAccountSettings(userId),
fetchNotificationPreferences(userId),
]);
return { profile, settings, notifications };
}
Rationale: Independent network calls should execute concurrently. Promise.all reduces wall-clock time without altering business logic. The agent should flag sequential awaits when dependencies are absent.
Example C: Enforcing Strict Type Boundaries
Avoid artificial optionality that masks missing data:
interface Invoice {
id: string;
amount: number;
currency: 'USD' | 'EUR' | 'GBP';
issuedAt: Date;
}
function validateInvoice(invoice: Invoice): void {
if (invoice.amount <= 0) throw new Error('Invalid invoice amount');
if (!['USD', 'EUR', 'GBP'].includes(invoice.currency)) {
throw new Error('Unsupported currency');
}
}
Rationale: Required fields must remain required. Optional types should only represent genuinely optional domain concepts. Forcing optionality to bypass compiler errors spreads uncertainty and increases defensive coding overhead.
Step 4: Architecture Decisions & Rationale
- Why use declarative rule files? Inline prompts are ephemeral and inconsistent. Rule files version control review standards, enable team alignment, and allow iterative refinement based on review outcomes.
- Why separate structural from cosmetic checks? Cosmetic issues (naming, formatting) are low-risk and easily automated. Structural issues (architecture, types, testability) carry high regression risk and require explicit evaluation criteria.
- Why enforce testability gates? Code that cannot be isolated for testing inevitably couples to infrastructure. Testability requirements force dependency injection, interface boundaries, and predictable state management.
Pitfall Guide
1. Mechanical File Splitting
Explanation: Breaking files solely to meet line-count thresholds without evaluating cohesion. This scatters related logic and increases navigation overhead.
Fix: Split files based on bounded contexts. Group related responsibilities, separate orchestration from domain logic, and ensure each module has a single reason to change.
2. Abstraction Overload
Explanation: Replacing simple conditionals with heavy strategy patterns or inheritance hierarchies prematurely. This introduces indirection without reducing complexity.
Fix: Use polymorphism or policy objects only when behavior varies significantly across contexts. For simple branching, decision tables or early returns often provide better readability.
3. Type Evasion via Escape Hatches
Explanation: Using any, unknown, or forced type assertions to bypass compiler errors instead of modeling accurate contracts. This hides integration mismatches and runtime failures.
Fix: Define explicit interfaces for external data. Use branded types or validation functions to narrow unknown values. Treat type escapes as technical debt requiring immediate resolution.
4. Sequential Async Bottlenecks
Explanation: Awaiting independent operations one after another due to habit or lack of dependency analysis. This increases latency without improving reliability.
Fix: Map data dependencies before fetching. Group independent calls using Promise.all or equivalent concurrency primitives. Document dependency graphs to prevent accidental serialization.
5. Testability Neglect
Explanation: Approving code that tightly couples to concrete implementations, global state, or external services without isolation points. This makes unit testing impossible and forces integration-heavy test suites.
Fix: Require dependency injection or interface boundaries for external calls. Validate that business logic can be tested in isolation. Reject changes that add behavior without corresponding test coverage.
6. Cosmetic Priority Over Structural Findings
Explanation: Allowing AI agents to surface formatting or naming suggestions while ignoring architectural violations. This dilutes review focus and delays critical fixes.
Fix: Configure review output to prioritize structural findings. Suppress cosmetic suggestions when architectural issues are present. Enforce a severity hierarchy in review reports.
7. False Positive Tolerance
Explanation: Accepting AI-generated structural recommendations without validating domain context. LLMs may suggest refactors that ignore business constraints or legacy compatibility.
Fix: Treat AI recommendations as proposals, not mandates. Validate architectural suggestions against domain requirements. Maintain a feedback loop to calibrate agent thresholds over time.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Small feature PR | Standard AI review + structural gate | Low risk, fast iteration | Minimal overhead |
| Large refactoring | Full structural AI audit | High architectural impact | Initial setup cost, long-term velocity gain |
| Legacy module update | Conservative structural review | Preserve compatibility while improving boundaries | Moderate refactoring cost, reduced regression risk |
| New microservice | Strict structural AI review from day one | Establish clean architecture early | Higher initial discipline, lower maintenance cost |
Configuration Template
---
description: "Structural code review contract for AI agents"
version: "2.1"
alwaysApply: false
---
# Structural Code Review Contract
## Objective
Evaluate code changes against architectural integrity, type safety, execution topology, and testability. Prioritize structural findings over cosmetic suggestions.
## Evaluation Criteria
### 1. Module Cohesion
- Verify single responsibility per module
- Flag files mixing orchestration, validation, persistence, and UI logic
- Question modules exceeding 1000 lines without clear architectural justification
### 2. Type Boundary Integrity
- Reject `any`, `unknown`, or forced assertions without explicit validation
- Ensure required domain fields remain required
- Validate that interfaces model actual contracts, not compiler workarounds
### 3. Execution Topology
- Identify independent asynchronous operations
- Recommend concurrent execution for non-dependent I/O
- Flag unnecessary sequential awaiting patterns
### 4. Testability Gates
- Require isolation points for external dependencies
- Validate that business logic can be tested without infrastructure
- Reject changes adding behavior without corresponding test coverage
### 5. Structural Refactoring
- Identify small changes that eliminate entire problem categories
- Prioritize deletion of unnecessary abstractions
- Suggest policy objects or decision tables over ad-hoc conditionals
## Output Format
1. Executive summary
2. Critical structural findings
3. Type boundary violations
4. Execution topology issues
5. Testability gaps
6. Recommended refactoring paths
7. Code judo opportunities
For each finding, provide:
- Problem description
- Architectural risk
- Location reference
- Concrete remediation steps
Suppress cosmetic suggestions when structural issues are present.
Quick Start Guide
- Initialize rule directory: Create
.cursor/rules/ in your project root to store version-controlled review contracts.
- Deploy configuration template: Copy the structural review contract into
.cursor/rules/structural-review.mdc and commit to version control.
- Activate agent mode: Open your AI agent interface, reference the rule file, and submit a diff for evaluation.
- Validate findings: Review structural recommendations against domain constraints, apply approved refactors, and update test coverage.
- Calibrate thresholds: Track false positives and adjust rule parameters quarterly to align with team architecture standards.