想做这件事的起因特别简单:我们手上的项目拆成了十几个 Git 仓库,前后端、内核模块、配置中心、文档站全部分库管理,之前一直靠一个人开着 Claude Code 一个个仓库切来切去,一个上午能折腾两个仓库就算不错了。后来我把思路换了一下,用一台中枢机器做调度,拉上 10 台 Worker 机器并行开工,每个 Worker 负责一个仓库甚至一个子模块,跑完再把结果合并回来。这套体系在真实项目里跑稳定之后,原本计划两周的跨仓库功能迭代,压缩到了三天左右。今天把整套方案从架构设计、环境准备、中枢调度、Worker 执行到问题排查全部拆开写出来,供同样被多仓库开发折磨的朋友参考。
这套方案不是只适合大型团队,哪怕你只有两台电脑、两个仓库,只要需求拆分够清楚,把 Claude Code 当成可并行调度的“虚拟开发人员”,整个玩法就能跑起来。文中所有命令、脚本思路、配置项我都尽量贴近实际执行记录,你可以直接照搬再根据自己的仓库结构调整。
1. 为什么需要 1 中枢 + 10 Worker 这套架构
1.1 单机单会话开发的瓶颈
先用一个场景说明白痛点。假设你手里有用户服务、订单服务、支付服务、消息中心四个仓库,同时要加一个跨端到端的业务功能,涉及数据库表变更、接口定义、页面联调。按老办法,打开 Claude Code 进入一个仓库目录,让它改完,再切到下一个仓库。问题马上出现:Claude Code 的会话上下文是跟着当前目录走的,切换仓库意味着会话失效,之前的代码逻辑全得重新描述一遍。
更难受的是单会话有上下文窗口上限。一个仓库里改到一半,前面那些文件内容就被挤出了模型视野,AI 开始“失忆”,把改过的代码又还原一遍,或者改出风格完全不统一的实现。实测下来,一个复杂仓库单会话能稳定推进的功能量是有限的,强行塞更多需求,返工率会明显升高。
还有一个隐性成本:时间被串行吃掉了。一个仓库在等 Claude Code 跑测试、跑 lint 的时候,你在旁边干等;如果代码量大、测试跑得慢,这个空档期全是浪费。换成分发到多个 Worker 并行跑,同一个时间段内多个仓库同时推进,相当于把串行的开发时间压缩到一个时间轴上。
1.2 中枢与 Worker 的角色定位
1 中枢 + 10 Worker 的玩法,本质上就是把 Claude Code 从“交互式结对编程工具”改造成“可调度的执行单元”。我这里的角色划分是:
- 中枢节点:负责拆任务、派任务、收结果、触发合并,本身不直接盯代码细节,它更像一个调度器和状态机。
- Worker 节点:每个 Worker 是一个独立的开发执行单元,内部拉起 Claude Code 完成具体的编码任务,跑测试、跑静态检查,最后把分支推上去并上报结果。
这个分工最大的价值在于:中枢是唯一需要你人肉介入决策的地方,日常开发的大部分重复执行都交给 Worker 自动完成。10 个 Worker 同时跑,意味着同一时刻有 10 个独立的 Claude Code 会话在工作,单会话上下文不够用的问题就被自然绕开了,每个任务都有独立的上下文窗口,互不污染。
实际操作中,并不是所有任务都值得并行。我的原则是:改动面集中在单仓库、且依赖关系清晰的子任务,优先并行;涉及跨仓库接口联调、需要同时改多个仓库同一模块的任务,单独串行处理。说到底,并行开发的本质是任务解耦,不是无脑开一批 Worker。
1.3 跨多 Git 仓库的协作模型
跨多 Git 仓库与单仓库最大不同在于:每个仓库有自己的提交历史、分支策略、CI 流程,甚至权限体系。所以这套架构里,我对仓库协作做了三个约定:
- 每个仓库独立拉分支,分支名统一带任务编号,比如 feature/task-1024-user-service;
- 统一通过 push 远端分支 + 创建合并请求的方式把代码汇入主干,禁止 Worker 直接推主干;
- 涉及多仓库的配置类变更(如数据库连接串、依赖版本),集中在专门的配置仓库推进,避免改一个仓库导致其他仓库测试环境挂掉。
这套约定跑顺之后,仓库之间的关系变成了“物理隔离 + 逻辑联动”。中枢只负责下发任务、记录进度,Worker 只负责在自己负责的仓库里干活,合并动作由人或者流水线触发。这样做的好处是:任何一个 Worker 跑挂了、仓库坏了,不会拖垮整个并行批次,中枢把任务重新入队,换一个 Worker 就能继续。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手前的环境准备:Claude Code 与 Git 多仓库
2.1 Claude Code 安装与模型配置
先解决基础环境。Claude Code 目前以命令行工具为主,安装依赖 Node.js 环境,我使用的版本要求 Node.js 16 以上,建议直接装 LTS 版本。安装命令很简单:
bash复制npm install -g @anthropic-ai/claude-code
安装后运行 claude 进入交互界面,首次需要完成认证,我是直接配置 API Key 而非登录订阅账号,因为在多 Worker 场景下机器人式调用比交互式登录更好维护。密钥写入环境变量:
bash复制export ANTHROPIC_API_KEY="sk-xxx"
这里有个实际问题:如果你使用的模型服务商不是 Anthropic 官方,那么 API 地址和模型名需要改成你的服务商配置。比如我们用过的某个兼容接口就需要设置:
bash复制export ANTHROPIC_BASE_URL="https://api.example.com"
export ANTHROPIC_MODEL="deepseek-v4-pro"
注意,模型名必须跟服务商实际提供的完全一致。有段时间我们这边传的模型名写错了一位,Claude Code 启动后直接报错,提示 “xxx is not a model this version of claude code recognizes”。所以这个配置我后来全部收口到 Worker 的启动脚本里,保证 10 个 Worker 读的是同一份环境变量,避免不同机器配置漂移。
2.2 Git 免密与多仓库配置
Worker 要自动拉取和推送代码,git 凭证必须提前处理好,否则跑到一半停下来等输密码,整个并行流程就卡死了。我用的是 SSH Key 方式,做法是:在一台 Worker 上生成密钥对,把公钥加入 Git 服务商账户,然后把私钥分发给所有 Worker。
bash复制ssh-keygen -t ed25519 -C "worker@dev"
生成后把 ~/.ssh/id_ed25519.pub 的内容配到代码托管平台的 SSH keys 里。每台 Worker 上我还会写一个 SSH config,把主机别名统一起来,防止不同 Worker 连接地址不一致:
code复制Host git.example.com
HostName git.example.com
User git
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking no
全局 git 配置也要统一,包括用户名、邮箱。提交信息如果不统一,后续追踪任务来源会很痛苦。我是在所有 Worker 上执行同一份初始化脚本,强制写入:
bash复制git config --global user.name "worker-bot"
git config --global user.email "worker@dev.local"
git config --global core.autocrlf input
这里有个坑:如果你用的仓库里有 shell 脚本或者 Python 文件,换行符不统一会导致 CI 挂掉。Windows 上默认 CRLF 会造成很多莫名其妙的问题,我在所有 Worker 上统一设为 input,让 Git 在提交时把 CRLF 转成 LF,至少保证提交进仓库的文件换行风格是一致的。
2.3 Worker 节点环境一致性
10 个 Worker 环境不一致是并行开发里最隐蔽的风险。比如 3 号 Worker 的 Node.js 是 18,8 号 Worker 是 22,同一个项目装依赖都可能出现差异。我的做法是给每台 Worker 打一个“基础镜像”,本质上是一份环境初始化脚本,里面固定:
- Node.js 版本(用 nvm 锁到具体小版本)
- Python 版本(如 3.11.9)
- Go 版本(如果是 Go 仓库)
- 全局 CLI 工具(claude、git、jq、yq、docker)
- Claude Code 用的模型名称和 API 地址
脚本存到配置仓库里,每台 Worker 首次部署时执行一遍。之后做依赖升级时,也只改这一份脚本,再让 Worker 拉取更新。这一套做下来,至少避免了“在我机器上是好的”这一类问题在 10 台机器上被放大 10 倍。
当然,环境完全一致比较理想,实际中不同仓库可能依赖不同版本的工具链。所以我允许 Worker 在跑每个任务前先读取仓库里的工具链版本文件(比如 .nvmrc、.python-version、go.mod),自动切换对应版本。这里的原则是“基线一致 + 按仓库覆盖”,既保证稳定性,又保留灵活性。
3. 中枢节点落地:任务分发与全流程编排
3.1 任务拆解与分配策略
中枢的第一步是拆任务。我的做法是先把整体需求写成一个 PRD 级别的任务描述,然后按“仓库 + 模块 + 验收标准”拆成独立子任务。每个任务必须包含四个要素:
- 目标仓库地址和需要操作的分支基线
- 具体的功能描述,写清楚输入、输出和边界
- 验收命令,比如运行某组测试用例、执行 lint
- 依赖条件,明确该任务是否依赖其他任务先完成
拆好的任务存成一个 JSON 文件交给中枢调度器。这里我强烈建议不要用太复杂的任务系统,刚开始用 JSON + 文件锁就够,等规模大了再去接正式的队列系统。
json复制[
{
"id": "TASK-1024",
"repo": "git@git.example.com:user-service.git",
"base_branch": "main",
"module": "user/profile",
"description": "在用户服务的 profile 模块增加头像上传接口,接收 multipart 文件,校验格式和大小,返回 URL",
"acceptance": "cd user-service && go test ./internal/user/... && make lint",
"dependencies": []
},
{
"id": "TASK-1025",
"repo": "git@git.example.com:order-service.git",
"base_branch": "main",
"module": "order/checkout",
"description": "在订单服务 checkout 流程中接入用户服务的新头像接口,用于订单备注展示用户头像",
"acceptance": "cd order-service && mvn test -Dtest=CheckoutTest",
"dependencies": ["TASK-1024"]
}
]
分配策略上,我按优先级分了三级:无依赖且改动量小的任务优先派;有依赖的任务等前置任务完成后再派;涉及公共库或公共 API 定义的任务单独串行。这样做可以大幅减少并行分支之间的代码冲突,因为同一时刻不同 Worker 动到的文件重叠概率被压到很低。
3.2 状态管理与进度追踪
中枢需要一个状态文件来记录每个任务处于什么阶段。我用的是一份 JSON 状态文件,配合一个简单的轮询逻辑,规模不大时完全够用。
状态流转大概是这样一个闭环:
- pending:任务已拆解,等待调度
- running:已被某个 Worker 领取,记录 worker_id、开始时间、所在分支
- verify:Worker 上报完成,中枢拉取分支并触发基础校验
- merged:代码已合入目标分支,任务关闭
- failed:执行失败,记录失败原因,根据策略决定是否重新入队
调度器每次分配任务时,先扫描所有 pending 任务,再检查正在跑的 Worker 数量是不是已经达到上限。如果已满,就让新任务排队。这个机制保证了 10 个 Worker 不会因为抢任务造成资源争抢。
进度追踪我直接用一个定时任务来汇总状态文件输出成摘要。摘要里包含每个任务当前卡在哪一步、跑了多久、失败了几次。实践来看,卡住的任务大多数是依赖没装成功或者测试环境超时,而这些从摘要里一眼就能看出来,不用逐个 Worker 登录去查。
3.3 结果回收与合并触发
Worker 完成任务后不会直接合并代码,而是把分支推送到远端,然后向中枢的接口上报分支名和测试结果。中枢在收到“verify 完成”的上报后,会做两步操作:
第一步,对比该任务的改动文件列表和当前主干的最新状态,做一个冲突预检测。如果冲突范围小,中枢直接尝试 rebase;如果冲突范围大,就把任务标记为合并冲突,并收集冲突文件清单,转人工处理或者重新派发。
第二步,把验证通过的分支合并回主干。这个动作我推荐用 Git 服务商的合并请求机制完成,也就是 Worker 推分支后自动创建 MR,由流水线跑完 CI 后自动合并,而不是中枢这边直接执行 git merge。原因很简单:直接用命令行合并绕过了 CI 和评审流程,一旦有问题不好追溯。
我这边的落地方式是:Worker 推分支时附带推送选项,推送后触发一个 webhook,让服务商自动创建 Merge Request,标题里带上任务 ID。这样就算某个 Worker 的代码有问题,MR 层面的审查和 CI 也能兜底。
4. Worker 节点落地:并行执行与代码质量保障
4.1 Worker 执行循环
每个 Worker 启动后就是一个循环:向中枢请求任务、拿到任务执行、执行完上报结果、再请求下一个任务。核心逻辑用脚本控制,伪代码大概长这样:
bash复制while true; do
task=$(request_task "$WORKER_ID")
if [ -z "$task" ]; then
sleep 10
continue
fi
prepare_workspace "$task"
run_claude_code "$task"
run_acceptance "$task"
report_result "$WORKER_ID" "$task_id" "$status"
done
关键在 run_claude_code 这一步。我是把 Claude Code 以非交互模式跑起来的,提前把任务描述和约束条件写进一个提示词文件,然后类似这样执行:
bash复制claude -p "$(cat prompt.md)" --allowedTools "**"
-p 表示非交互模式,直接输出结果;--allowedTools 放开工具权限,因为编码任务需要读写文件、执行命令。执行完 Claude Code 后,Worker 还要做一次 git status 检查,看看有没有未提交的改动。如果是空提交,说明任务可能没做动,就会标记成“无改动”,让中枢重新审视任务描述是否清晰。
跑完 Claude Code 之后,还有一步很关键:格式化与代码生成物的检查。Claude Code 偶尔会生成一些不该提交的临时文件,比如日志、缓存、临时代码片段,Worker 里需要有一个清理脚本,按 .gitignore 规则过滤之后再做 git add,避免把垃圾文件推上远端。
4.2 多仓库并行开发冲突处理
并行开发最容易翻车的就是代码冲突。10 个 Worker 同时在多个仓库改代码,总有两个倒霉蛋改了同一个文件的不同区域。为了把冲突概率压下来,我做了三层防线。
第一层是“任务边界约束”:在任务描述里明确每个任务可接触的目录和文件列表,Claude Code 执行前,我还写了一个前置校验脚本,检查待修改文件集合里是否包含其他正在运行任务已经触及的文件。一旦发现重叠,就让后启动的任务等待。
第二层是“小粒度频繁提交”:我要求 Worker 每完成一个内部小步骤就做一次本地提交,最后统一 push。这样即使最终分支跟主干冲突,rebase 时冲突范围也比较小,Claude Code 拿着冲突文件清单去解决,比大块代码堆在一起时容易得多。
第三层是“核心主干隔离”:所有 Worker 的改动都基于各自仓库的 main 分支拉 feature 分支开发,绝不直接提交主干。上一批任务合并完成并且 CI 通过后,才会触发下一批任务的分支基线更新。这样每个 Worker 开发时面对的是一个相对稳定的基线,而不是一个还在不断变化的“流沙”分支。
实测下来,这三层防线用上之后,跨仓库并行开发的冲突率明显下降。剩下那些不可避免的冲突,大多是跨仓库接口定义变更,这种我会专门留一个“接口协调员”任务串行执行,避免两个 Worker 同时改 API 定义。
4.3 质量门禁与自动校验
Worker 不能只管把代码写出来,还要保证质量。我在每个 Worker 的任务收尾阶段强制跑三类检查:
- 静态检查:按仓库的语言栈执行 lint、格式检查、类型检查;
- 单元测试:执行任务描述里指定的测试范围,不要求全量,但涉及改动模块的必须跑;
- 构建验证:本地执行一次构建或编译,确认代码能正常过编译。
这三类检查的结果会作为验收报告的一部分上报给中枢。任何一类失败,任务都会被打回重新执行,并且我会在打回信息里附上失败原因和日志片段,让 Claude Code 在下一轮开发时能直接“看到”自己哪里搞砸了。
这里有一个经验值得分享:对于“测试全过”要有正确预期。Claude Code 在某些情况下会倾向于修好代码去绕过失败的用例,而不是真正解决问题。所以我要求测试命令必须是仓库里写好的固定命令,不允许 Worker 自己发明测试。而且如果连续两轮都由于同样的测试挂掉,这个任务就转人工处理,不再让 Worker 空转,防止浪费 API 调用和时间。
5. 实测中遇到的常见问题与排查实录
5.1 Claude Code 请求异常:529 与模型识别失败
并行跑起来后,遇到最多的就是 529 错误。这个数字代表模型服务的负载过高,通俗讲就是同一时刻请求太多,服务端忙不过来了。10 个 Worker 同时调用,即使每个 Worker 只发一两个请求,也容易触发限流。
我的解决方案分两层。第一层是在每个 Worker 的请求侧加重试和退避策略,遇到 529 后等待一段时间再重试,等待时间按指数退避拉开,避免所有 Worker 在同一秒重试造成新的拥塞。第二层是限流,在中枢调度器上加一个令牌桶限制,控制全局最大并发请求数在安全范围内,宁可让部分任务排队,也不要集体触发限流。
另一个常见坑是模型名不识别。这个问题在这一类工具上尤其容易遇到,因为很多人用的是第三方模型服务商,模型名和服务商实际支持的不一致。遇到报错信息提示某个模型 name 不被识别时,不要急,先检查环境变量里的模型名是否跟服务商文档完全一致,包括大小写和下划线。我们在 10 台 Worker 上曾因为模型名带了个空格,排查了很久才发现是配置脚本拼接时多了一个空格。
5.2 Git 配置混乱与凭证问题
多 Worker 并行操作 Git,最容易踩的是凭证和身份问题。有次 3 号 Worker 推送代码后,代码提交记录里的作者变成了那台机器上另一个项目的旧用户名,导致库里的提交记录五花八门。排查后发现是初始化脚本只执行了一次,而 Worker 后续安装其他工具时把全局 git 配置覆盖了。后来我把 git 身份配置写进了仓库级配置,而不是只依赖全局配置。
还有一次,多台 Worker 同时 clone 同一个仓库,因为 SSH 指纹在首次连接时没有自动写入 known_hosts,导致脚本卡在确认指纹的交互界面。这也是我在 SSH config 里加 StrictHostKeyChecking no 的原因,自动信任已配置主机,避免交互卡死。
如果你使用 HTTPS 方式拉取代码,建议配置 credential helper 保存凭证,或者直接在仓库地址里带上 token。但要提醒一句:token 别硬编码到脚本里,至少放到 Worker 的环境变量文件里,并且限制目录权限。我在一个共享脚本里明文写过一次 token,后来全部轮换重配,这类教训能省则省。
5.3 并行开发的仓库冲突与进度延迟
除了明显的技术错误,还有一类问题是执行进度的隐性问题。比如某个 Worker 表面上显示 running,实际上 Claude Code 已经卡在某个交互确认上很久了。非交互模式下这类问题不多,但一旦出现,任务状态就假死。我的办法是在任务状态里加上心跳字段,Worker 每 30 秒上报一次心跳,中枢持续监控,心跳超时超过 3 分钟就判定 Worker 失联,把任务重新入队。
仓库冲突方面,除了前面说的三层防线,实际执行时还有一类常见冲突是配置文件冲突。比如多个任务同时修改了 application.yaml 这种公共配置文件,就算改动区域不同,合并时也可能出现语义冲突。这种我的处理策略是:公共配置文件单独抽象到配置中心,仓库里只保留引用地址,让不同任务尽量不直接动同一个配置文件。
进度延迟则大多来自依赖等待。任务 A 是任务 B 的前置依赖,A 跑挂了重试,B 就一直等着。为了避免链条过长导致整体延误,我把依赖链控制在两层以内,超过两层就拆成多个批次,每批次结束统一做一次集成验证。批次之间的等待成本,远小于长依赖链中一个任务反复失败带来的连锁延误。
5.4 Worker 动作清理与仓库状态恢复
最后一个容易被忽视的环节是 Worker 的工作目录和仓库状态恢复。如果上一个任务执行失败后仓库停留在“改到一半”的状态,比如有未提交的改动、有冲突标记的文件,下一个任务在同一目录继续跑,Claude Code 会被这些脏状态严重干扰。
所以我在 Worker 的 prepare_workspace 里强制先执行一个清理函数:检查仓库是否存在,存在就强制切回基线分支、丢弃所有未提交改动、删除已合并的本地分支,确保每个任务进来时都是干净的目录。经历过一次“脏目录上开发出来的垃圾代码”之后,你就会明白这一步有多重要。
再补充一个细节:Worker 机器长时间跑任务,磁盘会被各种仓库副本占满。我加了定时任务,任务完成后自动删除临时 clone 目录,只保留一个用于复用的裸镜像仓库缓存。这样既干净又省空间,同时还能利用镜像仓库的本地对象缓存加速 clone 速度。
写在最后的实操体会
整套方案跑下来,我的核心体会是:Claude Code 本身是个效率工具,但把它放到并行开发的架构里,它才能发挥出真正的“团队效能”。单打独斗时它只是一个聪明的结对程序员,而一旦进入 1 中枢 + 10 Worker 的调度框架,它就变成了可以复制的开发产能。
如果你也想尝试,我的建议是不要一上来就上 10 个 Worker。先用 1 个中枢 + 2 个 Worker,拿两个改动面很小的仓库跑通整个流程,把任务拆解模板、心跳机制、冲突处理这些细节打磨顺,再逐步加 Worker。并行开工带来的收益是乘法级的,但踩坑成本也会同步放大,少量 Worker 先行探路,比一次推全量稳妥得多。
我个人对这套方案的下一步规划是:把任务拆解环节也半自动化,用另一个 Claude Code 会话读取 PRD 自动生成任务 JSON,中枢只做审核而不做手动编写。这个想法还在验证中,如果你也在往这个方向走,欢迎一起交流进展。
