Skip to content

Enforcing Architecture and Developer Taste

Enforcing Architecture and Developer Taste

Section titled “Enforcing Architecture and Developer Taste”

Imagine hiring an incredibly fast, highly enthusiastic junior developer who has memorized every programming language but has never actually maintained a codebase for more than a week. If you leave them alone in your repository, they might build exactly the features you asked for, but the resulting code will likely resemble a massive bowl of spaghetti. In AI Harness Engineering, we face the exact same challenge. This chapter explores how to build mechanical boundaries to keep your AI agents disciplined and ensure they write code that aligns with your specific architectural vision.

AI agents are optimization machines. When given a task like ‘add a user login feature,’ they will instinctively look for the path of least resistance to achieve that goal. Because they lack a human’s intuitive understanding of long-term maintainability, they are prone to architectural violations.

Without strict boundaries, an autonomous agent might commit several ‘crimes’ against your codebase:

  • Layer Bypassing: Writing raw SQL queries directly inside a React UI component instead of calling an API.
  • Monolithic Functions: Generating massive 500-line functions instead of breaking logic into smaller, testable helpers.
  • Library Sprawl: Importing three different HTTP client libraries to solve similar problems across different files.
  • Inconsistent Naming: Mixing camelCase, snake_case, and PascalCase depending on whatever code snippet the AI saw last in its training data.

You cannot prevent these issues by simply asking the AI nicely in its initial system prompt. System prompts get forgotten or ignored as the context window fills up. Instead, you must enforce your architecture mechanically.

Before you can enforce rules, you must define your ‘developer taste.’ Taste is the subjective preference of your engineering team made objective. It represents the design patterns, coding paradigms, and structural choices that make your codebase feel cohesive.

To build an effective harness, you must translate these subjective tastes into black-and-white rules that a machine can evaluate. For example:

  • Subjective Taste: ‘Keep UI components dumb and stateless.’ -> Objective Rule: React components in the /components/ui folder are not allowed to import from the /services folder.
  • Subjective Taste: ‘Use clean error handling.’ -> Objective Rule: Every asynchronous function must use a standard Result<Success, Error> return type wrapper.
  • Subjective Taste: ‘Keep files small and readable.’ -> Objective Rule: No file shall exceed 300 lines of code.

Boundaries as Code: Custom Linters and Structural Tests

Section titled “Boundaries as Code: Custom Linters and Structural Tests”

To enforce your taste, you need tools that run autonomously every time the agent attempts to save or commit code. Standard linters (like ESLint for JavaScript or Flake8 for Python) are great for catching syntax errors, but you must go further by building structural tests.

Structural tests utilize Abstract Syntax Trees (ASTs) or dependency graphing tools to map out how different parts of your code communicate. If an agent tries to wire a database connection directly to a web route, the structural test will fail, blocking the change.

Test TypeWhat It ChecksExample Scenario
Standard LintersSyntax, style, and basic bugsMissing semicolons, unused variables
Dependency RulesAllowed import paths between modulesPreventing the UI layer from importing the Database ORM
AST Custom RulesSpecific code structures and patternsEnsuring every API route contains a logging statement
Size ThresholdsComplexity and length limitsFailing the build if a function exceeds 50 lines

The Magic of Automated Remediation Prompts

Section titled “The Magic of Automated Remediation Prompts”

Stopping the agent from committing bad code is only half the battle. If a test fails with a cryptic error like Error: Dependency violation in file auth.ts, the agent might not know how to fix it. It might start guessing, leading to a downward spiral of chaotic, trial-and-error code changes.

The secret weapon of AI Harness Engineering is injecting helpful remediation instructions directly into the prompt context. When an agent breaks a rule, your harness should not just yell ‘Error!’ It should gently guide the agent back to your preferred architectural path.

An effective remediation prompt includes three things:

  • The Exact Error: What specifically failed.
  • The Architectural Rule: A reminder of why this rule exists based on your ‘taste’.
  • The Correct Pattern: A quick code snippet or instruction showing the AI exactly how to do it right.

Let us look at a conceptual TypeScript example of how a harness intercepts a structural test failure and formats it into a highly effective learning moment for the agent.

// Example of a Harness Remediation Loop
function evaluateAgentCode(agentCommitFiles) {
const validationResult = runArchitectureLinters(agentCommitFiles);
if (!validationResult.passed) {
// The agent broke an architectural rule
const errorDetails = validationResult.errors[0];
// Construct a specialized remediation prompt
const remediationPrompt = `
Your recent code changes failed the architectural validation.
Error: ${errorDetails.message} in file ${errorDetails.file}
Rule Reminder: In this project, we enforce a strict separation of concerns.
UI components are NEVER allowed to import directly from the database layer.
How to fix this:
Instead of calling the DB directly, please import the appropriate function
from the 'services/' directory. If the service function does not exist,
create it there first, and then call it from your UI component.
Please rewrite your code and try again.
`;
// Feed this directly back to the agent as its next instruction
sendPromptToAgent(remediationPrompt);
}
}

By wrapping your AI agents in this tight, feedback-rich harness, you mechanically enforce your architectural boundaries. The agent learns your ‘taste’ on the fly, transforming from a chaotic code-generator into a disciplined software engineer that respects your project’s structural integrity.