Software Architecture: The Vocabulary AI Agents Don't Have
TL;DR
- An AI agent can generate a working file in seconds, but it has no memory of your system’s boundaries from one prompt to the next. Architecture is the vocabulary you use to hold that line — in your prompts, and in your reviews.
- This post assumes you already know Layered, MVC, and Event-Driven architecture at a working level — it’s a fast recap, not a tutorial, before getting to what AI does with (and to) them.
- Use AI to draft architecture diagrams and scaffolding from plain English, then validate every arrow and every box against the actual code.
Why This Post Exists in the AI-Assisted Development Series
This is the first post in the Software Architecture module — so it’s worth being explicit about why architecture belongs in a series about building software with AI agents.
An AI coding agent is extremely good at producing a plausible-looking function, class, or route handler on request. What it is not good at, by default, is remembering that your data-access code is never supposed to talk directly to your presentation layer, or that ServiceA already depends on ServiceB and must not depend on it the other way around too. Each prompt is answered mostly in isolation, drawing on patterns that are statistically common in training data — not necessarily the patterns your codebase has committed to.
That means the architectural boundaries in your system now depend on something that used to be implicit (a senior engineer “just knowing” the layering) becoming explicit: written down, prompted for, and checked. Three practical consequences follow, and the rest of this module builds on all three:
- You need the vocabulary to specify structure in a prompt. “Add a function that fetches the price” is ambiguous about which layer it belongs in. “Add a method to the
StockRepository— do not add business logic to it” is not. - You need the vocabulary to review what comes back. If you can’t name the layering violation, you can’t ask for it to be fixed — you’ll just feel a vague unease about a diff and approve it anyway.
- You need diagrams that are cheap to produce and easy to compare against reality, because AI-generated code drifts from any diagram that isn’t kept in the loop.
Everything else in this post — the recap, the AI-prompting examples — is in service of those three points, and this framing carries through the rest of this module.
Prerequisites
- Standard design-pattern vocabulary — SOLID, Dependency Inversion, and the Gang of Four catalogue (Factory, Strategy, Adapter, and the rest). This is assumed, not taught here; Refactoring Guru’s catalogue is a fast refresher if any of it’s rusty.
- Working familiarity with Layered, MVC, and Event-Driven architecture — this post recaps them in a few sentences each rather than teaching them from scratch. If any of the three is new to you, Martin Fowler’s Patterns of Enterprise Application Architecture (in References) covers the ground this post assumes.
Concept Explanation
Software architecture is the set of high-level decisions that define a system’s structure: which components exist, how they communicate, and how responsibilities are divided. Unlike design patterns (which solve object-level problems), architecture operates at the system level.
Bad architecture couples everything together so tightly that a change in one place breaks three others — and an AI agent, asked to make that one change, will happily make it and quietly break the other three, because it has no way of knowing they were coupled unless the code (or you) tells it so.
Analogy: Architecture is the floor plan of a building. An AI agent handed a request to “add a window” will cheerfully cut through a load-bearing wall if nothing tells it which walls those are.
The Three Patterns, in One Diagram Each
Three foundational patterns cover most cases: Layered (Presentation → Business Logic → Data Access → Infrastructure, each layer only talking to the one directly below it), MVC (Model holds data and rules, View renders, Controller orchestrates), and Event-Driven (publishers emit events, subscribers react, neither side knows about the other).
graph TD
subgraph Layered["Layered Architecture"]
P[Presentation] --> BL[Business Logic] --> DA[Data Access] --> DB[(Database)]
end
subgraph MVC["MVC Pattern"]
M[Model] -->|notifies| V[View]
C[Controller] -->|updates| M
V -->|user input| C
end
subgraph ED["Event-Driven Architecture"]
Pub[Publisher] -->|emit event| EB[Event Bus]
EB -->|dispatch| Sub1[Subscriber A]
EB -->|dispatch| Sub2[Subscriber B]
end
The one rule an AI agent breaks most often: a presentation-layer function must never write a query directly, and a data-access function must never apply business rules. Crossing layers is the most common architectural sin — and, because it’s the path of least resistance for an autocomplete-style suggestion, it’s also the most common thing an AI agent will do if you don’t stop it. Worked Example 2 below shows exactly this failure.
For the code-level shape of a layered service — repository, service, route handler — see the StockRepository / ValuationService pair used in Worked Example 2; the pattern is the same one summarized in the diagram above, just with names attached.
Communicating Architecture to a Team (and to an Agent)
A good architecture diagram answers three questions at a glance: what are the components (boxes), how do they communicate (arrows with labels), and what crosses a boundary (dashed lines between layers or services). Those are also, not coincidentally, the three things an AI agent needs pinned down before it can generate code that respects your structure. A diagram is a compact, unambiguous artifact you can paste straight into a prompt.
The one-minute rule: if a team member can’t understand the diagram within one minute, it has too much detail — the same rule applies to what you paste into a prompt. Mermaid Live Editor is worth calling out specifically here: it’s inline in Markdown, version-controlled, and something most AI agents can both read and write directly, which is why every diagram in this series uses it.
AI in Development
Draft with AI, validate with code. The workflow is always the same shape: prompt for a draft, then check the draft against what actually exists.
Worked Example 1 — Generating an Architecture Diagram
The prompt:
1
2
3
4
5
6
7
8
9
Draw a Mermaid diagram for the following system:
- A backend API that receives HTTP requests
- A ValuationService that applies business rules
- A relational database accessed through a repository layer
- A cache for frequently-accessed stock prices
- A notification service (email + SMS) triggered on price alerts
Use a layered architecture. Show data flow direction with arrow labels.
Use subgraphs to group layers. Keep it under 20 nodes.
What the agent produced:
graph TD
subgraph Presentation
API[API Route Handler]
end
subgraph Business
VS[ValuationService]
AlertEngine[Price Alert Engine]
end
subgraph DataAccess
Repo[(Stock Repository)]
CacheLayer[(Price Cache)]
end
subgraph External
Notify[Notification Service]
Analytics[Analytics Dashboard]
end
API --> VS
VS --> Repo
VS --> CacheLayer
CacheLayer --> Repo
VS --> AlertEngine
AlertEngine --> Notify
VS --> Analytics
The review pass — apply the four checks from this post:
- Paste it into a Mermaid renderer, does it render? Yes.
- Does data actually flow this way in the code? No —
CacheLayer --> Repois backwards. The repository is the source of truth; the cache reads from the repository on a miss, it doesn’t feed data into it. - Does every box actually exist? No — there is no
Analytics Dashboardanywhere in this codebase. The agent added it because “analytics” is a common neighbour of “cache” and “database” in training data, not because it was asked for. - Remove anything aspirational. Delete the
Analyticsbox and its edge, and flip the cache arrow.
Corrected diagram:
graph TD
subgraph Presentation
API[API Route Handler]
end
subgraph Business
VS[ValuationService]
AlertEngine[Price Alert Engine]
end
subgraph DataAccess
Repo[(Stock Repository)]
CacheLayer[(Price Cache)]
end
subgraph External
Notify[Notification Service]
end
API --> VS
VS --> CacheLayer
CacheLayer -->|on cache miss| Repo
VS --> AlertEngine
AlertEngine --> Notify
This is the pattern to internalise: the first diagram is a draft, not a deliverable. The value you added was two small, specific corrections — and you could only make them because you knew what “the cache reads from the source of truth” and “don’t keep aspirational boxes” actually mean.
Worked Example 2 — Catching a Layering Violation in Generated Code
The prompt:
1
2
Add a method to ValuationService that returns the average P/E ratio
across all stocks priced above 1000.
What the agent produced (pseudocode, reflecting the actual generated shape):
1
2
3
4
5
6
7
8
class ValuationService:
...
def average_pe_above(self, threshold):
# directly queries the database, bypassing the repository
rows = self.database.execute(
"SELECT pe_ratio FROM stocks WHERE price > ?", [threshold]
)
return average([r.pe_ratio for r in rows])
The review pass: this compiles, passes a quick manual test, and looks reasonable in a diff — which is exactly why it’s dangerous. It violates the layering rule from earlier in this post: business logic must never write a query directly. Concretely, this breaks two things the layering was protecting:
- The in-memory test repository is now bypassed — this method will fail or misbehave under test doubles.
- Swapping the persistence layer later (a new database, a new ORM) now means hunting through business logic for stray queries instead of changing one repository class.
The fix to request:
1
2
3
4
Rewrite average_pe_above so it goes through StockRepository instead of
querying the database directly. Add a find_above_price(threshold) method
to the repository interface and its in-memory implementation, and have
ValuationService call that instead.
The lesson isn’t “AI agents write bad code.” It’s that an agent optimizing for “does this one function work” has no visibility into “does this respect the layer boundary two files away” unless the boundary is named in the prompt or caught in review — which is precisely the skill this post is building.
Pro Tips
- Architecture Decision Records (ADRs). When you make a significant architectural choice, write a 1-page ADR: context, decision, and consequences. Future-you — and any agent you point at the repo later — will be grateful.
- Dependency direction is non-negotiable. Business logic must never depend on infrastructure (database, HTTP clients). Violating this makes testing impossible, and it’s exactly the shape of violation an agent produces when it takes the shortest path to “it works.”
- Diagrams rot — faster now than before. Code changes; diagrams get forgotten, and an AI agent can now change a lot of code in one sitting. Add diagram review to your sprint retrospective, or regenerate diagrams from code on a schedule rather than trusting a diagram nobody has looked at in months.
Common Mistakes
- Skipping the data access layer. Putting queries directly in business logic functions is the #1 architectural sin in AI-generated code (see Worked Example 2 above). Always add a repository/adapter boundary.
- AI generating circular dependencies.
ServiceAimportsServiceB, which importsServiceA. Watch for this in AI-generated multi-file outputs. Fix by extracting a shared interface. - Over-architecturing small scripts. A 50-line data processing script does not need a layered architecture with repositories and service classes — and an agent asked to “make this more professional” will sometimes add one anyway.
References
- Martin Fowler — Patterns of Enterprise Application Architecture — for the Layered/MVC/Event-Driven concepts this post assumes
- Mermaid live editor
- C4 Model for architecture diagrams
- Architecture Decision Records (ADRs)
Next Steps
Continue to Data and AI Architectures →
