System Design/
Lesson

AI tools can scaffold an entire project in minutes. But the architecture they produce is only as good as the patterns they have seen most often, and the most common backend code on the internet mixes concerns freely. Understanding where AI helps and where it misleads is the difference between using AI as a productivity multiplier and using it as a bug generator.

AI strengths and weaknesses for architecture

TaskAI QualityWhy
Generating CRUD boilerplateExcellentRepetitive, well-defined, patterns everywhere
Scaffolding project structure (folders, configs)GoodCommon structures exist in training data
Writing validation schemas (Zod, Joi)GoodSchema definitions are formulaic
Repository methods (basic SQL/ORM queries)GoodStandard patterns with minor variation
Enforcing layer boundariesPoorMost training data has mixed concerns
Business logic isolationPoorAI defaults to putting everything in controllers
Complex transaction designMediocreGets the structure right, misses edge cases
Choosing between patterns (when to layer, when not to)PoorContext-dependent decisions require judgment

The pattern is clear: AI is strong at generating code within a layer, but weak at deciding which code belongs in which layer.

02

Prompt: generating a layered project structure

A vague prompt gets you a messy result. Compare:

Weak prompt: "Create an Express APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. for a task management app"

This will produce fat controllers with database queries inline. Every time.

Strong prompt:

Create an Express API for a task management app with strict layer separation:

1. Presentation layer (src/controllers/): Express route handlers that ONLY
   parse input, call services, and format responses. No database imports.
2. Business logic layer (src/services/): Domain logic classes. No req/res
   objects. No SQL. Accept and return plain TypeScript interfaces.
3. Data access layer (src/repositories/): Database operations using Prisma.
   No business rules. Return domain objects, not Prisma types.

Each layer should only import from the layer below it.
Include: TaskController, TaskService, TaskRepository.
Use Zod for input validation in controllers.

The strong prompt names the layers, specifies what each layer must NOT contain, and lists the concrete files you expect. AI follows explicit constraints better than implicit expectations.

AI pitfall
Even with a detailed prompt, AI will sometimes add "convenience" shortcuts, like importing the database client directly in a service, or accessing req.user inside a repository. Always grep the generated code for cross-layer imports before accepting it.
03

Prompt: reviewing layer boundaries

After generating code, use AI to audit it:

Review this Express route handler. For each line, identify whether it belongs
in the presentation layer (HTTP concerns), business logic layer (domain rules),
or data access layer (database operations). List any lines that are in the
wrong layer.

[paste your code here]

This prompt forces the AI to classify each line, making boundary violations obvious. It works well because classification is easier for AI than generation with constraints.

04

Prompt: refactoringWhat is refactoring?Restructuring existing code to make it cleaner, more readable, or more efficient without changing what it does. mixed concerns

When you inherit a fat controller:

Refactor this Express route handler into three layers:

1. Keep in the controller: input parsing, Zod validation, res.status/json calls
2. Extract to a service (TaskService): any business rules, conditions,
   calculations, or workflow logic
3. Extract to a repository (TaskRepository): any database queries

The controller should be under 15 lines. The service should not import
express or any HTTP types. The repository should not contain if/else
business logic.

[paste fat controller here]
05

What to verify in AI output

After generating layered code, check these specific things:

1. Business logic in controllers. Search for if statements in your controllers that are not about input validation or error handling. If a controller checks user.role === 'admin' or product.stock < quantity, that logic should be in the service.

2. Database imports in the wrong layer. Your service files should never import Prisma, Knex, pg, or any database driver. If they do, the layer boundary is broken.

3. HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. types leaking down. If your service accepts a Request object or returns a Response, the boundary is broken. Services should accept and return plain objects.

4. Unnecessary abstraction layers. When you ask AI for "clean architecture," it might create layers you do not need: an interface for every class, a factory for every service, a mapper between every layer. If a layer adds no value, remove it. Two layers are fine for small apps.

5. Repositories with business logic. Watch for repositories that contain WHERE status = 'active' AND role = 'admin', the filtering might be a business rule that belongs in the service, not a database concern.

Edge case
AI-generated "clean architecture" projects often create an interface for every single class, even when there is only one implementation. If you have IUserService and UserService with no alternative implementation planned, the interface is just noise. Only add interfaces when you genuinely need to swap implementations (e.g., for testing or multi-provider support).
06

Hybrid workflow

The most effective approach combines AI speed with human architectural judgment:

  1. AI generates the scaffold. Use the strong prompt above to get folder structure, interfaces, and basic files.
  2. You review layer assignments. Open each file and ask: does every line in this file belong in this layer? Move anything that doesn't.
  3. AI fills in the boilerplateWhat is boilerplate?Repetitive, standardized code that follows a known pattern and appears in nearly every project - like setting up a server or wiring up database connections.. Let it write CRUDWhat is crud?Create, Read, Update, Delete - the four basic operations almost every application performs on data. repositoryWhat is repository?A project folder tracked by Git that stores your files along with the complete history of every change, inside a hidden .git directory. methods, ZodWhat is zod?A TypeScript-first schema validation library that validates data at runtime while automatically inferring static TypeScript types from the schema. schemas, and error response formatting, the repetitive parts.
  4. You write the business logic. The rules that make your product unique deserve human attention. AI can draft them, but you must verify every condition.
  5. AI writes tests. After the layers are clean, ask AI to generate unit tests for the service layer with mocked repositories. AI is good at this because test structure is formulaic.

This workflow keeps AI focused on what it does well (repetitive structure) while keeping human judgment on what matters most (domain rules and layer boundaries).

Good to know
The better you understand layered architecture yourself, the more useful AI becomes. An AI that generates a fat controller is not a problem if you can instantly spot the violation and ask it to refactor. The risk is when developers accept AI output without questioning which code belongs where.