๐Ÿค– AI ่ต„่ฎฏ

ยท ยท โ†—
← ่ฟ”ๅ›žๅˆ—่กจ

Your Architecture Is Why Your Coding Agent Keeps Writing Bad Code

Hacker Noon2026-09-15 20:15:26ๅคงๆจกๅž‹,AIๅบ”็”จ,Anthropic,ไปฃ็ ็”Ÿๆˆ,Agentๆ™บ่ƒฝไฝ“,ๆŽจ็†ๆ€่€ƒ,ๆœ็ดขRAG,ๆ‰ฉๆ•ฃๆจกๅž‹,ๅผบๅŒ–ๅญฆไน ,ๆ็คบๅทฅ็จ‹,ๆ‹›่˜HR,ๅผ€ๅ‘่€…็”Ÿๆ€ๅŽŸๆ–‡ โ†—

A few months ago I realized that as the code and modules inside the structure weโ€™d built kept growing, it was starting to become a problem, and developing with AI was getting really hard.

Iโ€™d ask Cursor or Claude Code to make a very simple change to a frontend module. At first glance it looked great. Then Iโ€™d open the PR and inspect it: it had done state management differently from the rest of the app, it had written copies of helper functions that already existed, it had pulled some random third-party package we never use into the project, and it had completely ignored the configs weโ€™d added for design.

At first I thought it was the LLMโ€™s fault, because everyone around me was complaining about this too โ€” model A is terrible, model B is too expensive, that kind of thing. I looked for the problem in the small context or in the model hallucinating, but the problem was entirely in our architecture.

If I had written the code myself, I would have known easily what goes where, since I set up the architecture, and I could apply everything in line with it. But if I donโ€™t explain these things to the AI, how is it supposed to know, right? If it inspected all the code, the context would bloat, and this time we might get worse results than the ones we got without following the architecture at all. If our codebase is complex, the agentโ€™s context bloats and its reasoning breaks down fast.

If we want AI agents to write clean, production-ready code, just writing better prompts or waiting for the next model update isnโ€™t enough. We need to build agent-native frontend architectures.

Here are the ways to make our architecture ideal for AI agents.

1. Monorepo as a Context Management Strategy

Monorepos used to be used for trunk-based development โ€” to shorten development processes when multiple teams were developing a single platform, and so on. But now thousands of agents are developing for a single platform. Where it used to make three teamsโ€™ work easier, now thousands of agentsโ€™ work gets easier.

If youโ€™ve split your micro frontends into separate repos, then to use a util from repo A โ€” or something similar to it โ€” in repo B, you have to download both repos and include them in the context. In that case, even for a very simple task, you lose time, you spend tokens, and sometimes you canโ€™t even reach a result.

In a monorepo (we use Turborepo with pnpm workspaces), everything lives under one roof, but with explicit boundaries:

repo/
โ”œโ”€โ”€ apps/
โ”‚   โ”œโ”€โ”€ shell/            # Host application (thin orchestration only)
โ”‚   โ”œโ”€โ”€ module-orders/    # Independent micro frontend
โ”‚   โ””โ”€โ”€ module-catalog/   # Independent micro frontend
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ ui/               # Shared component library (shadcn/tailored)
โ”‚   โ”œโ”€โ”€ utils/            # Pure helper functions (formatters, calculations)
โ”‚   โ”œโ”€โ”€ analytics/        # Single source of truth for tracking schema
โ”‚   โ””โ”€โ”€ config/           # Base tsconfig, eslint, tailwind presets
โ”œโ”€โ”€ turbo.json
โ””โ”€โ”€ package.json

When I ask an agent to work on module-orders, it doesnโ€™t scan the whole repository. It reaches only the shared packages/ folder it actually uses, directly through local workspace references (workspace:*).

On a single repository, the agent can move around freely and reach what itโ€™s looking for easily.

2. Independent Micro Frontends: Enforcing Strict Boundaries

This is actually the hardest part of a monorepo โ€” that the boundaries arenโ€™t drawn with strict rules. If we donโ€™t separate the modules and packages from each other properly, development can happen in places we donโ€™t want during the process, and that development can make it all the way to prod, and as you know, the result is an incident.

The solution to this problem is completely independent micro frontends and packages.

The core rule is strict: modules donโ€™t connect horizontally to each other. An agent working on module-orders should never need to know anything about module-catalog or import from it. They connect only vertically, through shared packages and the shell application.

        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚           shell             โ”‚  โ† Orchestration & routing only
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                โ”‚            โ”‚
      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
      โ”‚    orders    โ”‚   โ”‚  catalog    โ”‚  โ† Completely isolated modules
      โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
            โ”‚                   โ”‚
            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ–ผ
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚ packages/  โ”‚  โ† Shared, versioned foundation
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Version Decoupling Trick

To prevent a change in a shared helper from breaking all modules at once, we decoupled our internal packages from each other.

When packages/utils ships v2.1.0 with a new formatCurrency helper:

module-orders moves to โ€œutilsโ€: โ€œ2.1.0โ€ right away.

module-catalog stays on โ€œutilsโ€: โ€œ2.0.0โ€ until we explicitly decide to update it.

As for why this matters for AI: when I tell Cursor or Claude โ€œwork only inside apps/module-orders,โ€ the boundaries are physically real. The agent canโ€™t break the catalog module, because thereโ€™s no horizontal import path connecting them.

This way, we ended up using isolation as a security mechanism at the same time.

3. Tiered AGENTS.md: Preventing Convention Drift

If you donโ€™t give an AI agent explicit rules, it falls back on the average of its training data, which usually means generic, Stack Overflow-style code from 2024. It writes raw fetch calls instead of your custom API wrapper, or inline Tailwind classes instead of your design configs โ€” maybe it even writes CSS.

As a solution, AGENTS.md (or .cursorrules) is used. But thereโ€™s a point to watch out for: not every rule should be always-allow. Only the ones you want to run every single time should be.

If you make all the rules always-allow, youโ€™ve bloated the context before you even give your own prompt, and as a result youโ€™ve dropped your efficiency quite a bit. Research shows that after roughly 3,000 tokens of context load, reasoning quality drops noticeably.

We solved this with tiered rule management:

.rules/
โ”œโ”€โ”€ always/
โ”‚   โ””โ”€โ”€ global-conventions.md     # Universal rules (naming, tech stack, base TS config)
โ”œโ”€โ”€ modules/
โ”‚   โ”œโ”€โ”€ orders.md                  # Specific ONLY to the orders domain
โ”‚   โ””โ”€โ”€ catalog.md                 # Specific ONLY to catalog behavior
โ””โ”€โ”€ packages/
    โ”œโ”€โ”€ analytics.md               # Event tracking contracts
    โ””โ”€โ”€ utils.md                   # Helper function usage rules

An example of a strict package rule (analytics.md):

markdown

# packages/analytics usage rule
- NEVER call `window.dataLayer.push` directly.
- ALWAYS use the exported `trackEvent()` utility from `packages/analytics`.
- Event names MUST be defined in `packages/analytics/events.ts`.
- Required fields for every event: `event_name` (snake_case), `category`, `module`.

When an agent edits an analytics file, it loads analytics.md. When it edits a CSS theme, it ignores the analytics rules completely.

General rule: write the rule the moment you establish a new pattern, not six months later. If you forget, you wonโ€™t remember it again โ€” proven by experience :) Pair this with strict linters so the agent gets immediate feedback when it violates a rule.

How I Prompt Agents Now

When your architect