文章指出 AI 提升的是实现产能,而非架构质量;缺乏边界和一致性时,会放大逻辑重复、昂贵查询、任务重入及测试维护问题。核心建议是先建立清晰架构,再让编码 Agent 扩大产出。
AI 已经改变了软件构建的速度。
开发者只需描述一个 API 端点,就能在几秒钟内获得具体实现。AI 编程智能体可以检查代码仓库、修改多个文件、生成测试、运行命令、调试故障,并准备拉取请求。
这是开发生产力的一次显著提升。
但它也带来了一个新问题。
更快地生成软件,并不意味着生成可扩展的软件。
事实上,AI 可以帮助你以前所未有的速度创建一个设计糟糕的系统。
让 AI 智能体“构建一个完整的 SaaS 后端”,它可能会欣然生成控制器、模型、迁移、服务、后台任务、身份验证、缓存、队列,以及数十种抽象。
应用程序甚至可能真的可以运行。
然而六个月后:
修改一个功能会破坏另外三个功能
数据库查询的开销越来越大
业务逻辑散落在五个不同的位置
后台任务会执行两次
API 变得不一致
每个开发者都用不同的方式实现功能
测试变得难以维护
由于架构不清晰,AI 智能体会做出越来越危险的修改
其中的根本教训很简单:
AI 提升的是实现能力。架构决定了这种能力能否产出一个可扩展的系统。
因此,使用 AI 构建可扩展软件,需要一种不同于单纯使用 AI 编写代码的思维方式。
你需要设计 AI 编写代码时所处的环境。
你需要架构边界。
你需要自动化验证。
你需要机器能够理解的文档。
而且,你越来越需要将代码仓库视为人类开发者和软件工程智能体共同使用的运行环境。
本文将说明如何做到这一点。
开发者经常用“可扩展性”来表示:
“系统可以承载更多流量。”
这只是可扩展性的一种形式。
生产系统必须能够在多个维度上扩展。
系统能否承载:
100 users
1,000 users
100,000 users
1,000,000 users
连接管理
orders = 10,000
orders = 500,000,000
开发阶段看似无害的查询,可能会成为严重的性能瓶颈。
二十名开发者能否共同维护代码库,而不会不断破坏彼此的修改?
一个系统即使能处理数百万个请求,其工程可扩展性仍然可能非常糟糕。
你能否在不修改半个应用程序的情况下添加新功能?
良好的架构会尽可能减少一次变更所影响的不相关组件数量。
运维可扩展性
生产环境出现故障时,你的团队能否理解正在发生什么?
Why is checkout slow?
Which service is failing?
Which request triggered this exception?
How many jobs are stuck?
Which deployment introduced the regression?
如今,还有另一个值得考虑的维度。
多个 AI 编程智能体能否安全地为代码仓库作出贡献?
这一点正变得越来越重要。
Codex 和 GitHub Copilot 等编程系统支持代码仓库级指令,可以持续向智能体提供有关项目结构、约定、测试和验证的信息。OpenAI 建议使用 AGENTS.md 提供持久化的代码仓库上下文,而 GitHub Copilot 也支持用于类似目的的代码仓库指令和 AGENTS.md 文件。
这意味着,现代软件架构必须越来越多地同时为两类开发者进行优化:
Human developers
+
AI engineering agents
幸运的是,让软件更容易被 AI 理解的做法,通常也同样会让人类更容易维护它。
AI 辅助开发中最危险的提示词之一,大致是这样的:
Build the complete backend architecture for my SaaS.
问题不在于 AI 无法产出架构。
问题在于,架构是一系列依赖上下文的决策。
以支付系统为例。
monolith?
modular monolith?
microservices?
event-driven architecture?
serverless?
这里不存在普遍正确的答案。
具体决策取决于:
工程团队规模
事务要求
数据一致性要求
运维能力
预期的产品演进方向
集成要求
AI 不会自动了解这些约束。
如果你让它在没有获得这些信息的情况下作出决策,它就会用自己的假设填补缺失的上下文。
生成的代码在技术上可能是合理的,但对于你的产品来说却可能完全错误。
更合理的协作关系应该是:
Human:
defines architecture
AI:
explores options
implements components
writes tests
performs refactoring
finds inconsistencies
reviews code
generates documentation
investigates failures
把 AI 看作一支速度极快、在你所定义的边界内工作的工程团队。
假设我们正在构建一个项目管理 SaaS。
一种简单粗糙的架构可能如下所示:
Frontend
|
Backend API
|
Database
从技术上讲,这确实是架构,但它几乎没有向 AI 智能体提供任何有用信息。
相反,你应该定义重要的边界。
┌──────────────────┐
│ Web App │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ API Layer │
└────────┬─────────┘
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Projects │ │ Billing │ │ Accounts │
└─────┬────┘ └────┬─────┘ └────┬─────┘
│ │ │
└─────────────┼──────────────┘
│
▼
┌──────────────────┐
│ Database │
└──────────────────┘
Background Work
│
▼
┌──────────────────┐
│ Queue │
└──────────────────┘
接下来定义各部分的职责。
不负责处理业务规则。
这样就建立了 AI 智能体可以遵循的边界。
AI 让微服务变得异常诱人。
Create an authentication microservice.
Create a billing microservice.
Create an order service.
很快,你就拥有了十二个服务。
但不幸的是,你同时也会面临:
重复的身份验证逻辑
异步一致性问题
复杂的本地开发环境
你解决了一个很可能根本不存在的扩展性问题。
对于许多 SaaS 应用程序来说,模块化单体通常是更好的起点。
src/
Modules/
Accounts/
Projects/
Billing/
Notifications/
Reporting/
每个模块都包含自己的:
Controllers
Services
Models
Repositories
Events
Jobs
Policies
Tests
这样既能获得服务拆分的许多好处,又不必承担分布式系统的复杂性。
将来,如果计费模块的计算成本变得很高,或者在组织层面需要独立出来,你可以再将其拆分。
Modular Monolith
↓
Identify scaling boundary
↓
Extract Billing Module
↓
Billing Service
在这一拆分过程中,AI 会非常有用,因为清晰的边界能让它识别依赖关系,并有条理地完成迁移。
关键在于尽早建立这些边界。
在 AI 辅助开发中,你能做出的最大改进之一,就是创建代码仓库级的工程指令。
OpenAI 特别建议使用 AGENTS.md 为 Codex 提供持久化上下文。OpenAI 的指导说明,这些文件可以记录命名约定、业务逻辑、依赖关系,以及智能体无法仅凭源代码可靠推断出的代码仓库特有信息。
GitHub Copilot 同样支持代码仓库级指令、针对特定路径的指令,以及 AGENTS.md 文件。
这改变了我们看待文档的方式。
文档不再只面向开发者。
它还可以充当工程智能体的可执行上下文。
一个实用的文件可以如下所示:
# Architecture
This application uses a modular monolith.
Modules:
- Accounts
- Projects
- Billing
- Notifications
- Reporting
Modules must not directly access another module's database models.
Cross-module communication must use application services or domain events.
# Controllers
Controllers are responsible only for:
- request validation
- authorization
- calling application services
- returning responses
Never place business logic inside controllers.
# Database
All list endpoints must support pagination.
Avoid queries inside loops.
Use eager loading when relationships are required.
All new queries on high-volume tables must consider indexes.
# Jobs
除非需要立即响应,否则外部 API 调用应异步执行。
在可能的情况下,任务必须能够安全重试。
所有业务逻辑都必须有单元测试或集成测试。
每次修复缺陷都必须添加回归测试。
完成任何任务前,请运行:
npm test
当存在公共 UUID 时,绝不能暴露数据库 ID。
绝不能记录密码、令牌、密钥或支付凭证。
现在,假设有五个 AI 智能体同时处理应用程序的不同部分。
如果没有这份文档,每个 AI 智能体都可能自行发明一套约定。
有了它,架构一致性将得到显著提升。
比较下面两个提示词。
创建一个用于创建订单的端点。
不同次运行可能会生成完全不同的解决方案。
实现 POST /api/orders。
要求:
第二个提示词完成了一件重要的事情。
它缩小了 AI 的决策空间。
这是实现可扩展 AI 辅助开发的最大秘诀之一。
你将越多的架构决策编码进系统,AI 需要自行创造的架构决策就越少。
强健的系统建立在不变量之上。
不变量是指必须始终保持为真的条件。
对于电商平台:
stock >= 0
对于金融软件:
total debits = total credits
对于订阅系统:
除非明确允许,否则客户不能同时拥有两个 相同套餐的有效订阅
对于多租户 SaaS:
租户 A 的用户绝不能访问租户 B 的数据
AI 应该知道这些规则。
仓库中可以包含:
这样一来,修改系统的 AI 智能体获得的有效上下文,将远多于仅仅阅读数据库模式的 AI 智能体。
AI 编码工具让生成数据库模式变得极其方便。
这种便利可能会掩盖糟糕的数据库设计。
为电商系统创建数据表。
users products orders order_items payments
一切看起来都没有问题。
但可扩展的数据库设计需要提出更深入的问题。
例如,对于订单:
这张表可能包含多少行数据?
订单将如何被查询?
付款后订单还能更改吗?
是否应该为客户信息创建快照?
如何表示退款?
如何处理货币?
需要哪些索引?
ID 是顺序 ID 还是公共 ID?
是否需要按租户分区?
记录必须保留多长时间?
重要的洞见是:
数据库模式编码了产品假设。
AI 可以快速生成迁移,但这些假设仍然需要由你来确定。
一种典型的 AI 生成实现可能如下所示:
$orders = Order::all();
foreach ($orders as $order) { echo $order->customer->name; }
当只有二十个订单时,它运行得非常完美。
当有 100,000 个订单时,情况就不同了。
良好的架构应该明确要求 AI 智能体考虑以下代码:
Order::all();
Order::query() ->with('customer:id,name') ->latest() ->paginate(50);
但即便如此,最终可能仍然需要优化。
可扩展的工程工作流会要求 AI 检查实际的查询模式,而不是盲目照搬 ORM 惯例。
假设你的应用程序经常执行:
SELECT * FROM orders WHERE tenant_id = ? AND status = ? ORDER BY created_at DESC LIMIT 50;
当规模达到一定程度时,索引就会成为功能的一部分。
CREATE INDEX idx_orders_tenant_status_created ON orders (tenant_id, status, created_at);
重点并不是具体使用哪个索引。
重点在于,应该要求 AI 提出以下问题:
这些数据将如何被访问?
这张表应该包含哪些列?
开发者经常将可扩展性与微服务联系起来。
应用程序 | ├── 用户服务 ├── 产品服务 ├── 订单服务 ├── 支付服务 ├── 通知服务 └── 分析服务
这种架构可能具有出色的扩展能力。
它也可能变成一场噩梦。
每个网络边界都会引入以下潜在问题:
超时 重试 部分失败 版本不匹配 延迟 身份认证失败 消息重复 部署协调
分布式系统需要明确设计失败处理机制。
例如,AWS 当前的可靠性指南建议让变更型操作具备幂等性,从而避免重试产生非预期的重复效果。它还建议使用指数退避、抖动和重试次数限制等技术来控制重试。
AI 可以在几秒钟内生成 REST 客户端。
但它无法消除网络的不确定性。
因此,只有在有明确理由时才拆分服务。
充分的理由包括:
独立的扩展需求
清晰且稳固的团队所有权边界
本质上不同的工作负载
独立部署需求
技术约束
“微服务具有可扩展性”这个理由并不充分。
假设用户上传了一段视频。
一种简单粗暴的请求处理方式可能如下:
上传 ↓ 生成缩略图 ↓ 转码视频 ↓ 分析元数据 ↓ 发送通知 ↓ 返回 HTTP 响应
用户必须等待所有步骤完成。
可扩展的设计则有所不同。
上传 ↓ 保存元数据 ↓ 将处理任务放入队列 ↓ 返回响应
后台工作进程 | ┌──────┼───────┐ ▼ ▼ ▼ 缩略图 转码 分析
一旦架构确定,AI 在实现工作进程和后台任务方面尤其高效。
开发者负责决定:
哪些操作必须同步执行? 哪些操作可以异步执行? 哪些操作可以独立失败? 哪些操作必须重试?
AI 负责实现这些决策。
这一点值得特别关注。
class ChargeCustomer { public function handle() { PaymentGateway::charge( $this->customer, $this->amount ); } }
支付成功 ↓ 工作进程崩溃 ↓ 任务仍未得到确认 ↓ 队列重试 ↓ 客户被再次扣款
代码看起来是正确的。
但系统并不正确。
可扩展的系统会假设同一项工作可能执行多次。
这个任务可以改为使用幂等键:
PaymentGateway::charge( customer: $this->customer, amount: $this->amount, idempotencyKey: $this->paymentAttemptId );
AWS 明确描述了这一原则:当同一个请求被再次处理时,可重试操作应避免产生额外的副作用。
因此,你给 AI 的指令中应该包含如下规则:
所有支付任务都必须具备幂等性。
所有 Webhook 处理程序都必须能够容忍重复投递。
所有外部 API 重试都必须考虑操作是否可以安全地重复执行。
这些简短的指令可以防止代价极其高昂的缺陷。
面对性能问题时,AI 的另一个常见回答是:
添加 Redis 缓存。
这并不是缓存策略。
在缓存某些内容之前,需要回答:
我们要缓存什么?
为什么缓存?
缓存多长时间?
什么操作会让缓存失效?
能否返回过期数据?
如果 Redis 不可用会怎样?
假设下面的查询会持续频繁运行:
SELECT COUNT(*) FROM orders WHERE tenant_id = ? AND status = 'pending';
tenant:42:pending_orders_count
应该在什么时候让它失效?
创建订单后?
状态变更后?
取消后?
导入后?
付款后?
缓存失效机制是架构的一部分。
AI 可以快速实现失效钩子,但一致性预期需要由人来定义。
良好的可扩展性在很大程度上依赖契约。
假设计费模块公开了:
interface BillingService { public function subscribe( User $user, Plan $plan ): Subscription; }
其他模块依赖这个接口。
它们不应该依赖:
StripeSubscriptionRepository StripeWebhookParser StripePaymentIntentService StripeCustomerModel
因为实现细节会扩散耦合。
如果每个模块都直接访问其他模块的内部实现,AI 生成的变更就会变得危险。
一个很小的实现变更都可能引发整个代码库范围内的修改。
稳定的契约可以缩小影响范围。
OpenAI 针对 Codex 提出的一项建议是:采用类似 GitHub Issue 的方式组织任务,并在相关时引导 AI 智能体参考现有实现。
实现密码重置速率限制。
遵循与 LoginRateLimiter 相同的架构。
需要检查的文件:
src/Auth/LoginRateLimiter.php src/Auth/LoginController.php tests/Auth/LoginRateLimiterTest.php
这比下面这种提示安全得多:
添加速率限制。
第一个提示为 AI 智能体提供了一个本地架构参考。
第二个提示则要求它自行设计架构。
由此可以得出一条非常有力的规则:
为每一种重要模式创建一个优秀的实现,然后让 AI 遵循它。
CreateOrder UpdateSubscription UploadFile HandleWebhook ProcessPayment ExportReport SendNotification
一旦这些模式建立起来,开发就会从架构设计转变为模式复用。
人们常常会陷入一种误区:
代码是 AI 写的。 代码也是 AI 审查的。 所以代码很可能是正确的。
AI 可以生成一个实现,然后自信地认可自己所做的错误假设。
可扩展的做法是自动化验证。
可以这样理解它们之间的关系:
AI 速度提升 ↓ 变更数量增加 ↓ 验证要求提高
如果一个工程团队每天的变更数量从十次增加到五十次,人工验证会变得越来越不够用。
一个可扩展的系统应该具备多个验证层。
验证独立的业务规则。
public function test_discount_cannot_exceed_order_total() { $order = new Order(total: 100);
$this->expectException( InvalidDiscountException::class );
$order->applyDiscount(120); }
验证组件之间的交互。
数据库 队列 缓存 外部服务适配器
验证实际契约。
POST /api/orders
201 { "id": "...", "status": "pending" }
验证关键用户流程。
注册 ↓ 创建组织 ↓ 订阅 ↓ 创建项目 ↓ 邀请团队成员
AI 可以生成以上所有内容。
关键在于将测试设为必须交付的成果。
实现这个功能。
实现这个功能。
为业务规则添加单元测试。
为持久化添加集成测试。
为授权和验证添加 API 测试。
运行相关测试套件,并在完成任务前修复所有失败项。
这是你可以引入的最有效规则之一。
假设生产环境暴露出以下问题:
当两个请求同时到达时, 用户可以重复兑换同一张优惠券。
修复优惠券重复兑换问题。
使用一个失败的测试复现优惠券重复兑换问题。
然后修复实现。
修复后,该测试必须通过。
这样,你的代码仓库就获得了永久性的知识。
生产事故 ↓ 回归测试 ↓ 架构知识
随着时间推移,测试套件会成为一套记忆系统。
这对于没有参与过早期事故处理的 AI 智能体尤其有价值。
AI 智能体非常擅长生成代码。
静态分析器非常擅长拒绝特定类别的不良代码。
TypeScript compiler PHPStan Psalm ESLint Ruff mypy SpotBugs SonarQube ArchUnit
假设你的架构规定:
领域代码不能依赖 HTTP 控制器。
不要只把这条规则写在文档里。
只要有可能,就应该强制执行它。
Domain X Controllers
如果 AI 智能体违反了这条规则,CI 就应该失败。
终极的可扩展 AI 工作流不是:
告诉 AI 如何正确完成任务。
而是:
告诉 AI 要做什么 + 自动拒绝无效的解决方案。
开发流程应该大致如下:
AI 智能体
|
▼
代码变更
|
▼
┌─────────────────────┐
│ CI │
├─────────────────────┤
│ 格式化 │
│ 代码检查 │
│ 静态分析 │
│ 单元测试 │
│ 集成测试 │
│ 架构测试 │
│ 安全检查 │
│ 构建 │
└──────────┬──────────┘
│
通过
│
▼
审查
流水线越强大,AI 就越能安全地自主运行。
假设 AI 帮助你构建了一个每分钟处理 20,000 个请求的 API。
随后,客户开始反馈:
“有时结账需要 12 秒。”
如果应用程序只记录:
Something went wrong
那么它并不足以支撑可扩展系统。可扩展系统需要可观测性。
至少应该考虑:
Payment failed
记录类似下面这样的结构化信息:
{ "event": "payment_failed", "payment_id": "pay_123", "order_id": "ord_456", "provider": "stripe", "error_code": "timeout", "duration_ms": 5021 }
每秒请求数 错误率 延迟 数据库连接数 队列深度 任务失败率 缓存命中率 CPU 内存 外部 API 延迟
一个请求可能会经过:
API ↓ 订单服务 ↓ 支付服务 ↓ 数据库 ↓ 消息队列
分布式追踪有助于识别哪个阶段消耗了时间。
关键在于,不应该等系统变得庞大之后才添加可观测性。
应该在架构仍然容易理解时就将其构建起来。
当获得以下信息时,AI 可以帮助调查生产环境问题:
日志 堆栈跟踪 指标 查询计划 部署差异
在 2026-08-07 部署之后,API 的 p95 延迟从 180 毫秒 增加到了 1.8 秒。分析这些追踪数据,并找出 可能的性能回退。
这比下面这种提示有效得多:
我的应用程序很慢。修复它。
当 AI 获得证据时,它会变得非常强大。
这一通用原则适用于所有场景:
上下文质量决定 AI 工程的质量。
假设一个应用实例可以处理:
500 个请求/秒
服务器 A 服务器 B 服务器 C 服务器 D
这些服务器位于负载均衡器之后。
因此,应用程序应该避免将关键状态存储在本地进程内存中。
服务器 A 的内存: user_123_session
如果下一个请求到达服务器 B:
会话丢失
负载均衡器
|
┌────────────┼────────────┐
▼