ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Claude Code 实战:从项目架构到代码生成的高效工作流

Claude Code 实战:从项目架构到代码生成的高效工作流 这一篇是我们光子AI实战系列笔记的第五章上篇核心就两块项目架构代码生成主力工具是 Claude Code。前一阵我接了个内部工具的重构配置散落、模块职责混乱目录里光“util.py”就躺了三个。放在以前我得先花两个晚上理结构再花一整天把旧代码挪过去最后才轮到“写新功能”。这次我换了个路线先把整个仓库丢给 Claude Code 读一遍让它给我一份结构方案再按方案增量生成代码。结果从开始到第一次能跑通只花了半天其中大部分时间还是在跟业务方确认需求。写这篇的原因也很简单我身边不少朋友已经把 Claude Code 装好了但用的时候要么是“问答模式”要么是让它补一个函数从来没人系统性想过——怎么用这个工具去搭项目骨架、怎么让生成出来的几百行代码维持同一套规范。这篇就围绕这个写适合两类人刚接触 Claude Code 想做点真事的开发者以及已经用了一段时间但总觉得生成质量不稳定的朋友。1. 项目架构上Claude Code 能帮忙到什么程度1.1 它不是一个只会聊天的插件而是能读仓库的 Agent很多人对 Claude Code 的认知还停留在“编辑器里那个能聊天的侧边栏”。实际用起来差别很大——它默认就是跑在终端里的直接面对整个项目目录而不是单个文件。这意味着你和它聊“项目架构”这件事它是有上下文支撑的。我常用的开场方式很简单cd ~/work/qlog claude进入会话后第一句不是让它写代码而是先让它摸清楚现状看下 src/ 和 tests/ 的结构把现有模块的职责、文件之间的依赖关系、哪些地方有明显的循环引用或重复实现整理成一份简短报告。它会先翻阅目录浏览关键文件然后给出结论。这个过程的体验很接近一个刚入职的工程师熟悉仓库——只是速度更快。读完报告你再让它基于现状去设计目录调整方案它就不会凭空建议一个跟现有代码完全脱节的“理想结构”。这里有一个容易踩的误区不少人打开 Claude Code 就直接说“帮我重构这个项目”然后它给出的方案听起来头头是道实际落地时会发现它根本不了解仓库里那些隐藏的历史包袱。所以我的习惯是先生成“理解报告”再讨论“目标结构”最后才动手改。三步分开每一步都等它确认清楚再往下走。1.2 三种用法对应架构工作里的不同阶段架构这件事不只是一次“目录设计”它贯穿在项目出生、生长、腐化、重构的全过程。我用 Claude Code 时会按场景切换不同用法而不是只开一个会话从头聊到尾。用法适用场景我常用的命令形式交互式会话前期需求梳理、架构方案讨论、多轮权衡claude一次性执行CI 流程、批量改写、生成固定格式文档claude -p 生成 src/qlog/core/checker.py逻辑如下...带着文件上下文执行让 Claude 记住某个模块的细节防止答非所问claude --add-dir src --add-file src/qlog/config.py 基于这些文件补充...这三者的区别不只是“交互不交互”。交互式会话适合把架构思路聊透它会记住前面的对话你可以顺着上一轮的结论继续推演一次性执行适合做短期任务比如“按这份目录规划生成所有空文件”完成后即退带文件上下文的方式则适合在架构已经定下来之后生成某个具体模块时避免它“读空气”。我在项目架构阶段最常用的是第一种和第三种组合先用交互式会话把整体结构谈清楚落地某个文件时再用--add-file把关联代码喂给它。这样既保持了全局视野又避免了上下文太长导致它抓不住重点。1.3 架构决定权AI 负责提出方案人负责拍板用 Claude Code 做架构有一个前提需要想清楚它不是架构师是“方案提出者”和“执行者”。原因很简单项目架构不纯是技术问题——它要匹配团队习惯、部署方式、发布节奏、甚至老板对“分层”这个词的理解。我一般会让 Claude Code 做三件事基于输入的需求列出两种以上的目录方案并说出各自的取舍把当前代码库的坏味道标注出来给出重构优先级在方案确认后负责生成全部文件骨架方案选哪个、优先做哪块、哪些历史代码这次可以不动由我来决定。这样配合的好处是Claude Code 不会像我以前那样自己写了一个“干净”的架构后就开始疯狂重构结果把业务逻辑改出问题。它更像一个手脚麻利的助手你给它方向它给你效率和一致性。2. 从一段需求描述生成第一版仓库结构2.1 背景我给这次演示准备的项目为了不纸上谈兵后面几章的例子我都拿同一个项目来演示一个叫qlog的命令行工具。需求大概是这样读取一个config.toml里面定义若干检查任务每个任务就是一个 URL 和对应的超时时间用异步方式并发检查这些 URL记录状态码、HTTP 版本、响应耗时所有任务跑完后把结果聚合成一份带时间戳的 JSON 报告技术栈固定为 Python 3.11 httpx测试用 pytest后续会加“按周期调度”和“结果入库”两个能力所以目录不能是死的一次性结构这个项目有典型的学习价值它不复杂但涉及配置解析、网络请求、异步并发、结果序列化、命令行入口、测试六个环节正好能覆盖项目架构里最常见的问题。2.2 第一版提示词怎么给更稳当我见过太多人一上来就贴一大段“帮我把项目写了”然后 Claude 给出的目录五花八门连根目录都放什么文件都跟团队规范对不上。问题通常不在模型在提示词缺了“边界信息”。我给 Claude Code 的提示词长这样我要做一个命令行工具 qlog - 读取 config.toml得到若干检查任务每个任务是一个 URL还有 timeout 参数 - 用异步方式并发检查这些 URL记录状态码和响应耗时 - 检查完后把报告写成 JSON 文件文件名带时间戳 - 技术栈Python 3.11 httpx测试用 pytest - 使用 src 布局后续会加“按周期调度”和“结果入库”两个能力 - 请先生成目录结构和每个模块的职责说明不要急着写实现代码注意我特意在最后加了一句“不要急着写实现代码”。这不是多此一举——把“生成结构”和“生成代码”拆成两个任务是避免它把一锅粥全倒给你的关键技巧。Claude Code 收到这个提示后通常会先列出它理解的目录树再逐个解释每个文件干什么。这个阶段我会认真看尤其关注两点有没有把“后续扩展能力”考虑进结构里配置解析、网络检查、结果输出是不是被清晰地拆开了。2.3 Claude Code 生成的结构与我的调整一次实际生成的结构大概是这样qlog/ ├── pyproject.toml ├── README.md ├── config.example.toml ├── src/qlog/ │ ├── __init__.py │ ├── cli.py │ ├── config.py │ ├── models.py │ ├── core/ │ │ ├── __init__.py │ │ ├── checker.py │ │ └── reporter.py │ └── errors.py └── tests/ ├── test_config.py ├── test_checker.py └── test_reporter.py这个方案不是最复杂的但它把“未来扩展”和“当前需求”照顾得很平衡。models.py放任务和结果的类型定义core/checker.py只负责网络检查core/reporter.py只负责写报告config.py只管配置解析互相不搅合。我调整了两处。一是把errors.py单独拎出来因为这类工具后面加定时调度时很可能要统一处理任务级别的错误和配置错误二是把config.example.toml放进仓库让后面生成代码时有一个明确的输入格式可以参考。这两处改完后整个结构就算定稿了。这个“定稿”动作千万别省。如果你不明确告诉 Claude Code“结构已定下面按这个结构生成”它在后续对话里很可能自己偷偷改目录名或者往根目录扔新文件。给它一个确定的边界它后面的产出才会稳定。3. 用 CLAUDE.md 约束“生成代码的脾气”3.1 为什么代码生成需要显式规则很多人把项目架构理解为“一张目录树”但实际项目里架构还包括另一层东西约定。比如要不要类型注解、错误统一往哪里抛、依赖新增是否需要确认、测试文件跟源码文件的对应关系。这些约定如果不写下来Claude Code 每次生成的代码风格都会有一点点漂移前几次看不出来代码量上来后就会变成“每个文件长得都不一样”的尴尬局面。我之前吃过这个亏。某个项目让 Claude Code 连续生成了十几天代码结果有一天 review 时发现有的函数用def有的用async def有的错误处理会 print有的错误处理直接抛出异常还有两个文件不约而同地定义了自己的timeout常量。代码能跑但维护性已经坏了。所以我现在的做法是项目一开工就在仓库根目录放一个 CLAUDE.md把所有架构约束写进去。Claude Code 在会话启动时会把这份文件作为长期上下文的一部分之后的每一次生成都会受它约束相当于是给代码生成装了一个“方向盘”。3.2 一份能落地的 CLAUDE.md 样例下面这份是我给 qlog 项目准备的去掉了一些项目特定的细节保留了通用结构# qlog / 项目约定 ## 目录约束 - 核心逻辑只允许放在 src/qlog/core/ 下 - CLI 入口只能写在 src/qlog/cli.py不得在 core 模块里直接处理命令行参数 - 新增模块前先在本文件“目录规划”段落补充说明再开始写代码 ## 技术要求 - Python 3.11 - 所有函数和类必须写完整类型注解 - 超时、重试次数等参数不允许在代码里出现魔法数字统一收敛到常量或配置项 - 网络请求统一使用 httpx不发散引入 requests ## 错误处理 - 配置解析错误统一转换为 QlogConfigError在 CLI 层捕获后打印可读信息 - 网络检查过程中某个 URL 失败不中断整体任务而是把错误信息写回对应的结果对象 ## 测试 - 每个核心模块必须有对应的 pytest 测试文件 - 测试不允许访问外网网络部分使用 httpx.MockTransport看起来很简单但它覆盖了“目录边界”“技术栈锁定”“错误处理模式”“测试策略”四件事而这四件事恰恰是代码生成质量翻车的重灾区。我特别想提醒其中的“目录约束”——Claude Code 很擅长在一个文件里写一堆辅助函数然后告诉你“这样够内聚”。如果你不提前约定“新模块先说明再写”它会无限地把项目往单文件大泥球方向推。加一条“新增模块前先在本文件补充说明”其实是在逼它遵守架构边界。3.3 让规则随架构演进的习惯CLAUDE.md 不是写一次就完事的。项目架构会演进规则也得跟着更新。我现在的习惯是每周五项目评审结束后把当周因为“没有规则”而踩的坑主动回填进 CLAUDE.md。比如之前我在一个项目里发现 Claude Code 经常生成 Windows 和 Linux 路径混用的代码就在 CLAUDE.md 里加了一条- 路径操作一律用 pathlib禁止字符串拼接路径下一周再让它生成文件操作代码时这个问题基本绝迹。把 CLAUDE.md 当代码一样维护仓库结构和生成代码的质量才会同步稳定。4. 代码生成的实战姿势模块化提示 逐级验收4.1 一个能独立完成的模块生成示例结构定稿、CLAUDE.md 到位之后就可以进入代码生成环节了。这一步我推荐一个原则一次只生成一个模块生成后立刻验收。拿 qlog 里最核心的src/qlog/core/checker.py来说。我给 Claude Code 的提示是现在按照 CLAUDE.md 的约定生成 src/qlog/core/checker.py - 输入是 URL 列表和 timeout 参数 - 并发执行检查返回 CheckResult 列表 - CheckResult 包含 url、status、delay_ms、error 四个字段 - 网络层用 httpx.AsyncClient - 单个请求失败不要中断整体流程把错误写进 error 字段 实现完成后告诉我需要往 pyproject.toml 增加哪些依赖。它返回的实现大概是这样的import asyncio import time from dataclasses import dataclass from typing import Optional import httpx dataclass class CheckResult: url: str status: Optional[int] delay_ms: float error: Optional[str] async def check_one( client: httpx.AsyncClient, url: str, timeout: float ) - CheckResult: start time.monotonic() try: resp await client.get(url, timeouttimeout) delay (time.monotonic() - start) * 1000 return CheckResult( urlurl, statusresp.status_code, delay_msround(delay, 2), errorNone ) except Exception as exc: # noqa: BLE001 delay (time.monotonic() - start) * 1000 return CheckResult( urlurl, statusNone, delay_msround(delay, 2), errorstr(exc) ) async def check_many( urls: list[str], timeout: float 5.0 ) - list[CheckResult]: async with httpx.AsyncClient() as client: tasks [check_one(client, url, timeout) for url in urls] return list(await asyncio.gather(*tasks))这段代码几乎不用改就能用。它遵守了 CLAUDE.md 里的类型注解要求也正确处理了单请求失败的场景。这就是规则前置的价值——没有 CLAUDE.md 时同样的提示词它可能生成一个把所有异常直接抛出去的功能你还得自己回来补错误处理。4.2 大任务拆成小任务的清单模板像 checker 这样一百行左右的模块一次生成是可行的。但如果是完整的业务系统最好拆成多个小任务而不是让 Claude Code 一口气写完。我在 qlog 里把整个项目拆成了四个子任务子任务输入信息Claude Code 的产出验收方式config 解析config.example.toml 格式parse_config() 函数单测覆盖checker 核心URL 列表 timeout异步并发检查逻辑本地跑通一个样例CLI 入口argparse 参数定义main() 命令入口手动运行一次reporter 输出结果列表 目标路径JSON 报告写入逻辑目录生成文件拆分带来的另一个好处是每次验收发现问题能精确锁定到某个模块而不是在一个五六百行的对话里大海捞针。Claude Code 的上下文窗口虽然很大但跨模块长对话还是会稀释注意力拆开反而更稳。4.3 别指望一条消息打通整个项目这里我必须泼一盆冷水很多人用过 ChatGPT 生成 demo 级别的项目后就默认 Claude Code 也能一次生成一个完整系统。实际上不是这样。如果你用它一个人名项目生成“一个完整的爬虫 API 服务 定时调度”它会给你一个看起来非常完整的工程里面甚至包括 CI 配置、Dockerfile、中间件目录。但当你真正去跑时会发现模型凭记忆猜的“标准架构”可能跟你本地环境、你的部署方式根本不匹配。我之前让 Claude Code 直接生成一个带定时调度的完整项目结果它生成了 Celery、Redis、Docker Compose 三件套而我只是想写一个跑在两台机器上的轻量脚本。那次教训让我彻底放弃了“几行提示生成全项目”的幻想。所以现在的姿势很明确架构阶段可以一次聊透代码阶段必须拆开写。一次生成一个模块确认一个提交一个。5. 生成代码后的审查链路为什么每次都要做一遍5.1 导入关系与文件路径检查Claude Code 生成的代码看着再顺眼也必须过一遍审查。我自己的顺序是先检查导入关系每个 import 是否真实存在路径有没有引用到旧结构里的文件这些文件在 pyproject.toml 里有没有声明依赖举一个最容易翻车的场景重构项目时Claude Code 经常会在某个模块里 import 一个旧文件路径比如from qlog.utils import timer但utils目录早就被拆分掉了。它在生成新代码时偶尔会依据“记忆”写出这种引用而你的项目里根本没有这个模块。这类问题靠眼睛扫一遍就能发现但如果不扫一启动就是 ModuleNotFoundError。5.2 运行一次最小验证静态检查过了之后下一步不是直接把代码提交到主干而是先跑一次最小验证。对 qlog 来说最小验证就是python -m qlog --config ./config.example.toml跑通之后看三样东西CLI 输出是否符合预期、JSON 报告有没有生成、单个 URL 超时的情况下会不会把整个命令搞崩。自动化测试当然好但“不依赖 Mock 的裸运行”能第一时间暴露环境级问题——比如依赖没安装完整、本机 Python 版本跟声明的 3.11 不一致、意外读取了环境变量等。Claude Code 本身也支持直接执行终端命令所以你可以让它自己把测试跑了。但我的经验是至少第一次本地验证要手动来。这不是不信任它而是你需要亲眼看一次真实的输出建立对生成代码的直觉判断力。5.3 把测试和静态检查加进日常流程最后一步是把验证固化成流程。我在 qlog 里做了两件小事添加 pytest 测试重点覆盖 config 解析和 reporter 输出格式接入 ruff 做静态检查统一导入排序和错误处理写法生成代码的“初始质量”再高也扛不住持续变更后的腐烂。让 Claude Code 生成的代码一开始就配套测试基础设施它后续的变更也会更“老实”。我在提示词里经常加一句“在生成实现代码时同步给出对应测试的修改点。”这个简单的动作让测试覆盖率从“事后补”变成了“同步产出”省掉不少返工。6. 这套流程里我实际踩过的坑6.1 跑通 vs 完整代码“看似完整”的陷阱最典型的坑是Claude Code 生成了一个函数代码结构很完整类型注解齐全但仔细看会发现它没处理某个关键边界。我在 qlog 里遇到一次——config 解析时不检查config.toml里 URL 字段是否为空字符串空 URL 会被直接丢给 httpx 然后得到一串毫无意义的错误。这类问题的本质不是模型不行而是提示词没有把“边界行为”交代清楚。现在我生成代码后会主动问自己一个问题“这个函数的输入有哪些不可能出现的边界”然后把明确的最关键的一条写进提示词。复杂的项目里我会再加一轮追问这个实现还有哪些边界情况没有处理列出来并说明你建议怎么处理。用这种反问去逼 Claude Code 暴露它的“默认假设”比你自己一行行猜要快得多。6.2 依赖幻觉AI 认为依赖已安装第二个高频坑是“依赖幻觉”。具体表现是Claude Code 用了一个库但它并没有把它写进pyproject.toml。可能是因为它觉得这是个“标准库”但实际上不是也可能因为它只顾着写代码忘了检查依赖清单。这次生成 checker.py 时它老老实实告诉我需要加httpx那是因为我在提示词里明确要求了“告诉我需要哪些依赖”。如果不加这句它可能在代码里出现import httpx却完全不提安装的事。所以我现在的对策很简单每次生成涉及网络、数据库、外部服务的代码都会在提示词里加一句“最后单独列一份需要新增的依赖”。生成后手动核对一次pyproject.toml二十秒的事却能把“到了环境里跑不起来”这件事提前拦在源头。6.3 上下文丢失后项目架构违反之前方向第三个坑比较隐蔽发生在长会话里。项目聊到一定深度Claude Code 可能会忘记前面定下的架构边界开始提出跟 CLAUDE.md 冲突的方案。比如它明明知道core/模块不允许处理 CLI 参数却在一次新需求生成中把 argparse 逻辑写进了core/checker.py。这不是模型“笨”而是架构信息离当前任务太远没有被激活。我的应对办法是一旦发现 Claude Code 开始跑偏不继续对话而是重新打开一个会话并在启动时用--add-file CLAUDE.md把它再次拉进上下文。某些关键任务我甚至会手动在提示词里复述一遍架构约束比如记住CLI 逻辑只能在 src/qlog/cli.pycore 模块只放业务逻辑。如果同样的跑偏频繁出现那多半是规则写得还不够直白或者存在多个相近的规则文本互相打架。去调整 CLAUDE.md而不是靠每次临时提醒去补救。这套流程用到今天我最大的感受是Claude Code 在项目架构和代码生成上的价值不在于“自动完成”而在于“把重复劳动变成对话和验收”。它把目录设计、规则生成、基础模块实现这些原本要花好几小时但没什么创造性的工作压缩到一顿饭的工夫省下来的时间才真正用在了架构评审和边界设计上。qlog 这个例子跑完之后后面我会顺着这条线继续做下篇让 Claude Code 基于现有架构把调度能力、结果入库、配置校验这些更重的业务代码稳步接进来。到时候再分享。
返回列表