


























你让 AI 做一个"用户管理模块"。它很听话,一个小时就写完了——用 Express + MongoDB。你发现不对,你项目用的是 Next.js + PostgreSQL。你让它重写,它改成了 Prisma + Postgres。但这次,它把用户表里的字段名风格从 camelCase 改成了 snake_case。你之前写好的代码,全要跟着改。
你让 AI 做一个"用户管理模块"。它很听话,一个小时就写完了——用 Express + MongoDB。你发现不对,你项目用的是 Next.js + PostgreSQL。你让它重写,它改成了 Prisma + Postgres。但这次,它把用户表里的字段名风格从 camelCase 改成了 snake_case——你之前写好的代码,全要跟着改。
问题出在哪?不是 AI 不听话,而是你没有给它一张"地图"。你给的是一个目的地("用户管理模块"),但没告诉它路怎么走、什么路不能走。AI 只能凭直觉选路,而直觉在工程中是最不可靠的。
在传统开发中,架构设计的意义不言而喻——它决定了系统的可维护性、可扩展性和性能表现。但在 AI 编码中,蓝图还有一层更关键的作用:它是 AI 编码工具唯一的工作上下文。
AI 没有长期记忆。每次对话,它看到的是一张白纸。如果你不给它上下文,它就只能"猜"——猜技术栈、猜命名风格、猜数据结构。而"猜"在工程中是最昂贵的,因为不同对话中的猜测结果不同。
没有蓝图时,AI 的"默认行为"是这样的:它从训练数据中选择概率最高的方案。训练数据中什么最多?React + Node.js + MongoDB 的 CRUD 示例最多。所以如果你不说清楚,AI 默认就会用这三件套。但你的项目可能用的是 Vue + Go + PostgreSQL,或者你是一个 Java 团队、一个 .NET 团队——AI 的"默认猜"几乎一定不是你想要的。
更隐蔽的问题是命名风格。AI 在第一次对话中用了 camelCase,因为"你给的示例代码是 camelCase"。但在第二次对话中(你重置了上下文),AI 没有看到之前的示例代码,它用了 snake_case——因为训练数据中 snake_case 也很常见。两个对话生成的代码,字段名风格不一致,数据模型冲突了。
蓝图的本质不是文档,是"约束器"。它把 AI 的可能性空间从"无限"压缩到"你的项目范围内"。当蓝图说"使用 Prisma ORM、camelCase 命名、统一 AppException 异常处理"时,AI 无论开多少次对话,都会遵守这些约定,因为它每次看到蓝图时,这些"规则"都是固定的。
蓝图不是可选的开销,而是 AI 编码的必备基础。
一份完整的蓝图(CONTEXT.md)应包含以下部分。
# 项目名称
一句话描述:这是一个什么系统,解决什么问题。
## 核心价值主张
这个系统存在的根本原因是什么?用户为什么选择它而不是替代方案?
术语表是蓝图中最容易被忽视但又最重要的部分。 在讨论技术方案之前,先确定核心领域术语的定义。术语模糊意味着架构从一开始就是模糊的。
为什么术语表这么重要?因为术语模糊的破坏性远超你的想象。一个"订单"可能被 3 个人理解成 3 种不同的东西:销售部说的"订单"是"客户提交的购买请求"(包含未支付的),财务部说的"订单"是"已经支付的交易记录"(不包含未支付的),仓库说的"订单"是"需要发货的工作单"(包含已支付和部分发货的)。这三个理解会导致完全不同的数据模型、状态机和 API 设计。
有一个真实的案例:某项目在开发用户系统时,"客户"和"用户"两个术语混用。AI 在功能 A 中创建了"客户表"(customer),在功能 B 中创建了"用户表"(user),两个表存储了几乎相同的数据,但字段名不同、关联关系不同。后期发现需要把两个表合并时,已经产生了 20 多个关联查询,改了 3 个月。
术语表的维护纪律很简单:发现新术语立刻定义,不等到"设计阶段"再补。 在需求分析阶段,听到业务人员说了一个新词,立刻问"这个词是什么意思?"然后把定义写进术语表。不要等到设计数据库表时再回头问——那时候你可能已经忘了。
## 技术栈
| 层 | 技术 | 版本 | 备注 |
|:---|:---|:---|:---|
| 前端框架 | Next.js | 14+ | App Router |
| 样式方案 | Tailwind CSS | 3.x | — |
| 数据库 | PostgreSQL | 15+ | 通过 Prisma 连接 |
| ORM | Prisma | 5.x | — |
| 部署 | Vercel | — | 自动部署 |
## 数据模型
### User(用户)
- id: String (UUID) — 主键
- email: String — 唯一,登录使用
- name: String — 显示名称
- role: Enum(ADMIN, USER) — 角色
- createdAt: DateTime
- updatedAt: DateTime
### Order(订单)
- id: String (UUID) — 主键
- userId: String — 外键,关联 User
- status: Enum(...) — 订单状态
- totalAmount: Decimal — 总金额
- createdAt: DateTime
## API 接口
### GET /api/orders
获取订单列表。
参数:
- page: number(默认 1)
- size: number(默认 20)
- status: OrderStatus(可选,按状态筛选)
返回:
{
data: Order[]
total: number
page: number
size: number
}
## 目录结构
src/
├── app/ # Next.js App Router 页面
│ ├── api/ # API 路由
│ ├── orders/ # 订单相关页面
│ └── ...
├── components/ # 共享组件
│ ├── ui/ # 基础 UI 组件
│ └── features/ # 业务组件
├── lib/ # 工具函数和配置
└── types/ # TypeScript 类型定义
## 里程碑
Phase 1: 基础架构
1.1 项目初始化 → 1.2 数据库搭建 → 1.3 用户认证
Phase 2: 核心功能
2.1 订单列表(依赖 1.3)
2.2 创建订单(依赖 1.3)
2.3 订单详情(依赖 2.1)
Phase 3: 增强功能
3.1 订单搜索(依赖 2.1)
3.2 订单导出(依赖 2.2)
这是一个看起来很简单的原则,但执行起来极其困难。因为它违背了我们作为"开发者"的本能——我们习惯问问题,习惯收集信息后再做判断。
但在架构设计中,"询问"是最危险的沟通方式。原因有二。
第一,用户的信息不对称。你让用户选择"用 MySQL 还是 PostgreSQL",用户可能只知道 MySQL 是免费的,不知道 PostgreSQL 的 JSONB 支持对业务的价值。你让用户做他无法做的决策,得到的答案往往是随机的、不可靠的。
第二,AI 的"默认答案"陷阱。如果你问 AI"用什么数据库",AI 会给出一个最"常见"的答案——因为常见=训练数据中的高概率。但这个"常见"不一定适合你的项目。比如一个数据量很小、需要零运维的内部工具,AI 可能会推荐 PostgreSQL(因为它是"主流"),但 SQLite 才是更合适的选择。
正确的做法是"提议"。你作为架构师,基于用户的需求做调研、分析、权衡,然后给出一个明确的推荐,附带理由和替代方案。用户只需要做一件事:确认或调整。
这里有一个反直觉的洞察:提议不是"替用户做决定",而是"让用户能做决定"。 当你给出"推荐 SQLite,理由:零部署、满足你的数据量(<10 万行)、不需要 DBA。如果你预计数据量会超过 100 万行,PostgreSQL 是更好的选择,但需要额外部署。"用户看到后,可以立刻做出判断("我的数据量不会超过 10 万行"),而不是在"我该用什么数据库"的焦虑中随机选一个。
在询问任何细节之前,先生成一个完整的骨架蓝图——大部分字段用占位符填充。让用户看到最终产物的全貌,而不是在一张白纸上逐项询问。
为什么"骨架+占位符"比"白纸一张"更有价值?因为人(和 AI)对空白有恐惧,对填充有本能。给你一张白纸让你画房子,你可能会纠结"房子应该画多大""风格是什么""颜色怎么选"。但如果给你一张已经画了轮廓的素描让你上色,你可以立刻开始工作。
同样的,在架构设计中,给用户一个完整的骨架蓝图(大部分用占位符),用户看到后能立刻理解"哪些信息我需要补充",而不是在空白中茫然。
一个带占位符的骨架,比一张白纸更有价值。 用户看到骨架后,对自己需要补充什么信息一目了然。
在讨论技术栈、数据模型、API 之前,先确定核心领域术语。
一个常见的错误做法是:用户说"我要做一个订单管理系统",你直接开始设计数据库表。但"订单"这个词在不同业务场景下含义完全不同——电商的订单(包含商品、物流、退款)、餐厅的订单(包含桌台、菜品、厨房打印)、企业的采购订单(包含审批、对账、付款),三者差异巨大。在术语对齐之前设计数据库表,几乎必然出错。
正确的做法是:先和用户对齐——你说的"订单"到底是什么?包含什么状态?"取消订单"和"退货"是同一个概念吗?
领域术语是架构的第一份蓝图。数据模型、API 命名、代码结构都从术语表衍生而来。
好的架构师能同时做两种思考:
对于新项目使用自上而下——从需求出发,逐步推导出架构。对于已有项目,从下而上开始——先扫描代码,识别出"实际架构"(不是"理想架构"),然后提炼出蓝图,再自上而下调整。
举个例子:接手一个 3 万行的老项目,没有文档。如果直接从上而下设计,你设计出来的"理想架构"可能和实际代码差异巨大,无法落地。正确的做法是:先自下而上——扫描文件结构、识别模块划分、理解数据流——提炼出"当前架构"的蓝图,然后在这个基础上,自上而下地设计改进方案。
ADR 是用来记录那些"难以逆转"的架构决策的。但重要的是知道什么时候需要 ADR、什么时候不需要。
以下三个条件都成立时才需要 ADR:
如果缺少任何一点,就跳过 ADR。比如"选择 React 作为前端框架"——如果团队已经用了 5 年 React,这不是一个"真实权衡",不需要 ADR。但"选择 Prisma 而不是 Drizzle"——两者都是优秀的 ORM,选择其中一个需要权衡,这就是 ADR 的场景。
错误一:过度设计。 为"未来可能的需求"做设计,引入了不必要的复杂性。一个只有 10 个用户的内部工具,设计了微服务架构——"万一以后用户量大了呢?"但"以后"可能永远不会来。正确做法:为当前需求做设计,记录未来可能的扩展点,但不要为了实现这些扩展点而增加复杂度。
错误二:术语模糊。 团队对同一个术语有不同的理解,导致数据模型、API 命名、代码结构不一致。有人说"订单"指客户的购买请求,有人说"订单"指已经支付的购买记录——这两个理解会导致完全不同的数据模型和状态机。正确做法:在架构设计的第一步就创建术语表并确认。
错误三:忽略数据流。 架构设计只关注"有什么模块",不关注"数据如何在模块之间流动"。结果模块划分合理,但数据流混乱。A 模块和 B 模块划分清晰,但数据流需要 A→B,而 A 没有暴露数据接口,B 直接读了 A 的数据库——架构设计废了。正确做法:在架构设计中画出数据流图,明确数据从哪里来、经过什么处理、存到哪里、被谁消费。
架构设计是把需求转化为可执行蓝图的过程。蓝图的本质不是文档,而是约束器——它把 AI 的可能性空间从"无限"压缩到"你的项目范围内"。术语表是蓝图中最重要的部分,因为命名即架构,数据模型和 API 都从术语派生。四大原则——提议而非询问、骨架先行、术语先行、双向推演——是架构设计的核心方法论。ADR 记录难以逆转的决策,三个常见错误(过度设计、术语模糊、忽略数据流)是架构设计中最需要警惕的陷阱。下一章,我们将学习自动工作流的完整机制。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。