No More Solitude

孤独终洁

Agent 参与产品开发后,项目该怎么组织?

本文目录6 节
  1. 先分清四个不同的边界
  2. Monorepo 不是少建文件夹
  3. 把上下文放进仓库
  4. 多项目最容易失控的地方是契约
  5. 不能合并仓库也能统一工作区
  6. 我会怎样开始迁移

我最近遇到一个很现实的问题。

一个真正要上线的产品,通常不会只有一个代码项目。即便只是最常见的前后端分离,也会有用户端和后端;再往后,还可能出现管理后台、小程序、移动端、桌面端、AI 服务和基础设施。

如果继续沿用“一次打开一个项目”的方式,工作会变成这样:先在后端项目里向 Agent 解释需求,做完以后切到前端,再解释一遍;切到管理后台或小程序,还要重新对齐一次。人逐渐成了几个仓库之间的传话员,接口是否一致、哪些地方需要同步修改,也只能靠自己盯着。

这显然没有发挥 Agent 的价值。

我后来意识到,真正需要调整的并不是文件夹摆法,而是开发工作的边界。以前我们习惯以项目或仓库为中心,现在更合适的单位应该是产品。

先分清四个不同的边界

讨论 Monorepo 之前,有四个概念需要分开。

过去我们很容易把它们混在一起,仿佛一个仓库就应该对应一项完整工作。但“增加优惠券”“支持用户注销”“复制历史活动”这类真实需求,天然会穿过多个项目。

以“用户注销”为例,它可能同时影响用户端的确认流程、后端接口、数据库状态、令牌失效逻辑和管理后台的展示。如果把任务拆成几个彼此隔离的项目任务,人就要负责维持它们之间的一致性。

更合理的方式是把完整的产品需求交给 Agent,由它先搜索整个产品,识别影响范围,再修改相关应用、服务和契约,最后运行受影响的测试。

也就是说,Feature 应该成为 Agent 的工作单位,Product Workspace 才是人的管理单位。

Monorepo 不是少建文件夹

对个人或小团队而言,我会优先考虑产品级 Monorepo。它大致可以这样组织:

product/
├── AGENTS.md
├── docs/
│   ├── product.md
│   ├── architecture.md
│   ├── decisions/
│   └── plans/
├── apps/
│   ├── web/
│   ├── admin/
│   └── miniapp/
├── services/
│   ├── api/
│   └── ai-worker/
├── packages/
│   ├── contracts/
│   ├── api-client/
│   ├── business/
│   └── config/
├── infra/
└── scripts/

它的价值并不只是把代码放进同一个 Git 仓库,而是让一次产品变更拥有完整视野。

假设后端把一个接口从 POST /campaign 改成 POST /campaigns。在互相隔离的仓库里,用户端、管理后台和小程序都可能不知道这件事。在统一工作区中,Agent 可以搜索全部调用位置,修改契约和客户端,再运行受影响的检查。

一次提交也能完整表达一次产品变化,而不是把同一个功能拆成几个互相等待的提交。

不过,把代码搬到一起并不会自动解决问题。如果仓库里没有清楚的产品说明、架构约束和接口契约,Agent 看到的仍然只是一大堆文件。真正重要的是产品级上下文。

把上下文放进仓库

我认为根目录的 AGENTS.md 很关键。它不应该只是某个框架的开发说明,而应该告诉 Agent 这个产品由什么组成,各部分承担什么职责,以及跨项目修改时必须遵守哪些规则。

例如,它可以要求 Agent 在实现功能前阅读产品说明和架构文档,先分析会影响哪些应用与服务;客户端不能自行复制数据结构;数据库变更必须包含迁移;完成后要运行受影响项目的测试。

根目录负责产品级规则,子目录里的 AGENTS.md 再补充局部约束。这样,Agent 从产品进入具体项目时,既知道全局目标,也知道当前技术栈的细节。

需求本身也不该只存在于某次聊天里。可以在 docs/plans/ 中为重要功能保存一份简短计划,至少写清楚目标、行为、影响范围和验收标准。

Feature: 用户注销

Goal:
用户可以主动注销账户。

Affected:
- Web
- API
- Admin
- Database

Behavior:
发起注销 → 二次确认 → 服务端校验
→ 标记账户状态 → 令牌失效 → 后台可查看

判断这套上下文是否足够,有一个很直接的标准:新开一个 Agent,不依赖昨天的聊天,它是否能通过阅读仓库理解产品,并继续完成工作?

如果不能,缺的通常不是更长的提示词,而是没有沉淀下来的产品资料。

多项目最容易失控的地方是契约

用户端、管理后台、小程序和移动端都要访问同一个后端时,最危险的做法是各自维护一份看似相同的类型。

接口字段一旦变化,几个客户端很容易出现不同版本的 UserOrderCampaign。Agent 即使能快速写出代码,也可能把这种分叉扩散得更快。

因此,产品级工作区最好有明确的契约层,例如 packages/contracts,再根据技术栈生成或共享客户端。对于以 NestJS 和 TypeScript 为主的项目,可以从 OpenAPI 生成 API Client,供 Web、Admin 和小程序共同使用。

不同端未必能共享 UI。Web、移动端和小程序的界面体系可能完全不同,但它们仍然可以共享接口契约、类型、校验规则和部分业务规则。共享到什么程度,应该由技术边界决定,不能为了 Monorepo 而强行复用。

不能合并仓库也能统一工作区

并非所有产品都适合放进一个 Git 仓库。团队权限、发布节奏、历史包袱或合规要求,都可能迫使前后端继续使用独立仓库。

这种情况下,可以采用 Polyrepo 加 Product Workspace 的方式:外层工作区保存产品文档、架构说明和公共约束,内部放置多个独立 Git 仓库。Agent 打开的是整个工作区,所以仍能看到完整产品;各团队则继续维护自己的版本控制和发布边界。

product-workspace/
├── product/
│   ├── AGENTS.md
│   ├── architecture.md
│   └── contracts/
├── repos/
│   ├── web/.git
│   ├── server/.git
│   ├── admin/.git
│   └── miniapp/.git
└── scripts/

这里最值得保留的区分是:Workspace 是产品边界,Repo 是版本控制边界。二者并不需要重合。

我暂时不会把 Git Submodule 作为个人或小团队的默认方案。它能表达仓库之间的引用关系,但也会给分支、提交、持续集成和开发环境增加额外操作。除非团队已经明确需要它,否则普通工作区往往更容易维护。

我会怎样开始迁移

如果现有项目已经分散在多个文件夹里,没有必要一开始就做大规模搬迁。更稳妥的顺序是:

  1. 先建立产品级工作区,让 Agent 能同时读取所有相关项目。
  2. 补一份根级 AGENTS.md,写清项目地图和跨项目规则。
  3. 把重要需求放进 docs/plans/,让上下文脱离聊天记录。
  4. 找出最常发生不一致的接口和类型,逐步建立契约层。
  5. 再根据团队规模、权限和发布方式,决定是否合并为 Monorepo。

如果团队以 Node、NestJS、React 或 Next.js、TypeScript 为主,可以从 pnpm workspace 和 Turborepo 这类相对轻量的组合开始。工具只负责工作区依赖和任务编排,产品规则与契约仍然要由仓库内容表达。规模和依赖关系真正复杂以后,再评估更重的方案。

我现在更愿意把多项目管理理解成一件产品设计工作,而不只是代码目录设计。代码可以拆,部署可以拆,技术栈也可以不同,但 Agent 必须能看到一份共同的产品事实,并围绕一次完整的功能变化工作。

当人的工作从“在几个仓库之间传话”变成“描述目标、确认边界和验收结果”,Agent 才真正进入了产品开发流程。

COMMAND / FIND

搜索

快捷入口

轨迹 ORBIT 归档 TIMELINE 分类 INDEX 专栏 PATHS 关于 BIO 随便走走 SHUFFLE