CCDV-F 备考手册 · Claude Certified Developer – Foundations
Chapter 05 · Domain 2(本章 skill 权重合计 13.6%)

工程基础三件套
从 requirement 推导到可运营、可演进的 change

一项业务诉求不会自动变成可交付的软件。本章沿一条完整链路走:先把 business requirement 推成 functional 与 infrastructure requirements,再把它落实为 REST / JSON / async 合约,最后用 systems life cycle、版本控制、code review 与分阶段 refactoring 守住演进质量。

一、先拆业务句子:目标、约束、验收证据

设想团队要给客服工作台接入一个由 Claude 应用调用的内部 case service。原始需求只有一句:「大促期间把退款案例的中位处理时间从 9 分钟降到 3 分钟,同时不能重复退款,所有决定可追溯。」这句话混着三种不同东西:

层从原句抽出的内容可判断的问题
业务结果处理时间 9 分钟 → 3 分钟上线后的 lead time 是否真的下降?
不变量同一退款意图不能产生两笔资金动作客户端超时重试、worker 重投时是否仍只执行一次?
治理约束每次决定与人工覆盖都可追溯能否从 case ID 还原请求、结果、操作者与时间线?

推导的第一步不是立刻选数据库或 queue,而是把自然语言变成可观测的结果、不可破坏的不变量、可验收的边界。诸如「系统要快」「架构要 scalable」无法直接验收;把它们改成带 workload、percentile、failure mode 与观察窗口的条件,后续设计才有裁决标准。

判断点 — 题干若给的是业务结果,正确选项通常先补齐可测试的 requirement;直接宣布技术栈,等于在不知道成功标准时优化实现。

二、Functional requirements:描述可见能力,不偷渡实现

Functional requirement 回答「系统对外能做什么」。对上述 case service,可以写成一组可验证的 capability:

  1. 接收带稳定 request ID 的退款案例,并返回 case ID;
  2. 查询 case 的当前状态、决定与失败原因;
  3. 在满足授权条件时提交人工覆盖,并记录操作者与理由;
  4. 同一 request ID 与同一 payload 重放时返回同一业务结果;
  5. 同一 request ID 搭配不同 payload 时拒绝歧义,而不是执行第二次。

这组写法没有指定 PostgreSQL、Kafka 或某个 framework,因为那些是候选实现。反过来,若业务真的要求「调用方在 300 ms 内拿到最终决定」,同步完成就是功能边界的一部分;若允许稍后查询,那么「提交 + 状态资源」才是合理合约。同步还是异步不是偏好,而是 response-time requirement 推出来的。

弱表述可验收表述为什么更好
支持重试相同 request ID 重放不新增资金动作把「支持」变成可测不变量
错误友好错误响应含稳定 code、可读 message 与 correlation ID调用方可分支,运维可追踪
支持人工介入有权限的 reviewer 可覆盖决定,且覆盖记录不可与原决定混淆定义 actor、动作与审计结果

三、Infrastructure requirements:从 solution architecture 反推运行条件

Solution architecture 给出组件与数据流,infrastructure requirement 则说明这些组件在目标 workload 下需要什么运行条件。假设架构是「API → durable queue → worker → ledger adapter」,推导链应逐项落地:

架构事实推导出的 infrastructure requirement验收证据
API 与 worker 解耦queue 在 worker 重启期间保留未完成 job,并支持受控重投故障注入后 job 不丢失,重复投递不重复副作用
峰值为平时 10 倍入口限流、queue 容量与 worker 并发有明确上限峰值压测下 backlog 可见且恢复时间达标
跨服务追踪request ID / case ID 在 API、queue、worker 与 ledger 日志间贯通用一个 ID 能重建端到端时间线
资金副作用不可重复幂等记录与资金状态变更位于同一可靠一致性边界在超时、进程崩溃点重放仍只有一个结果

这里最容易犯的错是把产品清单当 requirement:写「需要 Kubernetes」只说了工具,没有说负载、故障或恢复目标。更好的写法先声明 throughput、latency、durability、security、observability 与 recovery 约束,再由团队比较 managed service、自托管组件或更简单的进程内方案。

四、REST contract:先建模资源,再选择 method 与 status

REST API 的核心不是把函数名搬进 URL,而是给资源、状态转换与错误语义一个稳定合约。异步 case 可建模成:

POST /refund-cases
Idempotency-Key: req-8f2…

{ "order_id": "o-1842", "amount": "49.90", "currency": "USD" }

202 Accepted
Location: /refund-cases/case-731

{ "id": "case-731", "status": "queued" }

202 Accepted 表示请求已接收、处理尚未完成;调用方随后 GET /refund-cases/case-731 读取状态。若服务器已经同步创建完整资源,201 Created 更贴切。选择 status 的判断轴是此刻已经发生了什么,而不是团队习惯统一返回 200。

取舍更稳的判断常见失误
资源 vs. action URL有长期身份与状态的对象用资源;无法自然表达的 domain command 才考虑 action把每个 service method 暴露成 RPC 风格路径
PUT vs. PATCH完整替换与部分更新的 contract 分开让同一路径随 payload 猜语义
错误 status区分客户端输入、冲突、授权、限流与服务器失败所有失败都返回 500,调用方无法决定是否重试

五、幂等与重试:把「至少一次到达」变成「一次业务效果」

网络超时只说明客户端没有看到响应,不说明服务器没有执行。若调用方在超时后重试,资金、发信、建单等副作用就可能重复。幂等设计要覆盖完整竞态:

  1. 客户端为一个业务意图生成稳定 key,重试沿用它;
  2. 服务器把 key、规范化请求摘要、处理状态与最终响应关联起来;
  3. 首次执行与幂等记录写入处在能共同成败的一致性边界;
  4. 相同 key + 相同请求返回既有结果;相同 key + 不同请求返回冲突;
  5. 处理中重入有明确行为:等待、返回 processing,或让调用方稍后查询。

Retry policy 与幂等合约要成对设计。临时网络错误或服务不可用可以采用有上限的 exponential backoff 加 jitter;校验失败、权限不足、schema 不兼容通常需要改请求,机械重试只会制造流量。即便 operation 可幂等,无界重试也会把局部故障放大成 retry storm。

判断点 — 题干出现「客户端超时,但服务端日志显示已提交」时,根因不是 timeout 数值太小,而是调用方与服务端缺少端到端幂等协议。

六、JSON schema 演进:缺失、null 与类型是三件事

JSON 的 object、array、string、number、boolean 与 null 看似简单,跨版本时真正危险的是双方给同一字段赋了不同语义。以退款结果为例:

{
  "status": "needs_review",
  "risk_score": null,
  "reasons": [],
  "schema_version": 2
}

兼容演进通常优先 additive change:新增 optional 字段、consumer 忽略未知字段、producer 不随意改变既有字段类型或语义。删除 required 字段、收窄可接受值、把 string 改成 number 都可能破坏旧 consumer。若系统承诺 Semantic Versioning,MAJOR.MINOR.PATCH 的含义应与公开 contract 的兼容性绑定,而不是与改动行数绑定。

变更先问什么安全动作
新增字段旧 consumer 会拒绝 unknown field 吗?先验证容忍策略,再逐步发送
字段改名能否先双读、再双写/回填、最后删除旧名?用兼容窗口分阶段迁移
枚举加值consumer 是否把未知值当 fatal error?先更新 consumer 的 fallback,再扩 producer

七、异步编程:并发不是无限并行

async/await 让程序在等待 I/O 时把执行机会交回 scheduler;它不会自动让 CPU-heavy 工作并行,也不会自动限制同时在途的请求。一个看似简洁的实现:

results = await gather(*(process(case) for case in cases))

若 cases 有 50,000 个,这行代码可能同时建立过多 connection、占满 memory,并把下游压垮。生产设计需要把 concurrency 当资源预算:

  1. 用 semaphore、worker pool 或 queue 设置 bounded concurrency;
  2. 用 timeout 与 cancellation 释放调用方已经放弃的工作;
  3. 用 backpressure 让 producer 在 consumer 落后时减速,而不是只扩 backlog;
  4. 区分 task 失败策略:fail-fast、收集部分结果、重试单项或送 dead-letter queue;
  5. 保留 correlation ID,使并发日志仍能还原单个 case。

选择 concurrency 的依据是下游配额、连接池、单任务内存、CPU/I/O 比与目标 latency。把并发从 20 调到 200 可能提高吞吐,也可能只提高 timeout 与 retry 数;答案要看 bottleneck 的测量结果。

八、Systems life cycle:develop / implement / operate / maintain

Systems life cycle 不是「开发完再交给运维」的流水线。四个阶段各有不同决策证据,并通过反馈闭环连接:

阶段核心问题退出前应拿到的证据
develop需求与设计是否能被实现、测试?可追溯 requirement、contract test、failure-path test
implement如何安全配置、迁移与 rollout?部署计划、兼容性证明、rollback / roll-forward 路径
operate系统在真实 workload 下是否健康?SLI/SLO、dashboard、alert、runbook、事件记录
maintain如何修缺陷、还技术债并演进 contract?升级策略、deprecation 窗口、复盘进入 backlog

例如「压测通过」只覆盖 develop/implement 的部分风险;它不能替代生产中的 latency、error rate 与 backlog 观测。反过来,operate 发现 queue age 持续增长,应回流到 requirement 与 capacity model,而不是永久把 alert threshold 调高。

九、版本控制与 Code review:让风险按 diff 暴露

版本控制的价值不只是保存文件,而是把 change 的意图、边界与验证证据组织成可审查单元。短生命周期 branch、小而自洽的 commit、可复现的 CI 结果,能让 reviewer 判断「为什么改、改了什么、失败时怎么退」。若一个 commit 同时改 API contract、数据库迁移、UI 文案与格式化,任何回退都会带走无关工作。

Code review 应把注意力放在自动化工具看不懂的风险上:

应拦住可交给自动化reviewer 要问
contract 破坏、竞态、权限越界、数据迁移不可逆、失败路径缺失format、lint、机械 style、一部分静态分析哪个 requirement 被满足?失败后能检测、恢复吗?
测试只覆盖 happy path、指标无法区分故障模式单测执行、类型检查、依赖扫描证据是否覆盖题干给出的风险?
大 diff 混入无关重排generated file 一致性能否先做无行为变化的准备 commit?

Review comment 也要区分 blocker 与 preference。会导致数据错误、安全问题、兼容性破坏或无法运维的缺陷应阻断;纯个人风格若已有 formatter 或团队没有约定,不宜掩盖真正风险。

十、小重构与大重构:保持行为,分阶段换结构

Refactoring 的目标是在保持可观察行为的前提下改善内部结构。rename、extract function、消除重复通常可在现有测试保护下做成小 change;跨 service、schema 或数据所有权的大重构,则需要先创造兼容 seam。

小重构大重构
主要风险局部回归新旧 contract、数据与 rollout 状态不一致
起手证据focused test 已覆盖原行为characterization test + consumer inventory + migration rehearsal
切分方式单个可回退 commitexpand → migrate → contract,或 branch by abstraction
上线策略常规 CI兼容期、观测、分批 rollout、明确删除旧路径的条件

以 customer_id 改名为 account_id 为例,直接一次性改 producer、queue schema、数据库与所有 consumer,会把部署顺序变成隐含前提。更稳的 sequence 是:先让 consumer 双读;再让 producer 写新字段并保留旧字段;回填与观测;确认旧字段无读者后再删除。每一步都可部署、可验证、可回退。

把三条主线收在一起:business requirement 给出成功标准,life cycle 给出每阶段证据,software engineering practices 把每次 change 缩成可审查、可运营、可演进的单元。题目问「下一步」时,不要选最终最宏大的架构;选当前证据下最小、完整、能降低主要风险的那一步。

本章题组

QUESTION 01 · TS 2.1 · REQUIREMENT DERIVATION
A team is building an internal refund-case API for a Claude-powered support application. The business requires a submission acknowledgment within 250 ms at 40 requests per second, a final decision within 3 minutes, no duplicate refund when a request is retried, and a complete audit trail by case ID. The approved architecture is API → durable queue → workers → ledger. Which requirement set is best derived from both the business need and the architecture?
正确 A这组 requirement 把题干四条约束逐项落到了可验收结果:入口 latency 与 throughput、queue 的 durability、资金副作用的幂等性、跨组件的 traceability;同时尊重了题干已经批准的异步架构。它没有提前钉死产品,而是说明每个组件必须证明什么。按 P1,先把约束逐条对表,才有资格比较实现。
D2 无关杠杆违反 P1B:产品名和副本数不是成功标准。三个 replica 可能够,也可能不够;安装完成更不能证明 40 requests/s、250 ms、故障保留、幂等或审计链。这个选项动了实现杠杆,却没有覆盖题干给出的目标变量。
D6 半对半错违反 P1C:submit/status endpoints 与时延要求是对的,后半句却把 durable queue 偷换成了「自动保证业务幂等与审计传播」。queue 允许重投时,恰恰更需要服务端幂等边界;correlation ID 也要显式贯通。它只写完了 functional requirement,漏掉了关键 infrastructure constraints。
D4 换模型当解法违反 P1D:模型档位作用不到 queue retention、ledger 原子性、request ID 或日志关联。题干没有指出推理能力不足;把模型升档,四项工程约束仍然一项都不会自动成立。
QUESTION 02 · TS 2.4 · REST IDEMPOTENCY
A Claude-powered support application sends POST /refunds. At 12:00:00 the ledger commits a USD 80 refund, but the connection drops before the client receives the response. At 12:00:02 the client retries the same business intent; production data shows 1.8% of such timeouts create a second refund. What is the best contract change?
正确 Ctimeout 造成的是「结果未知」,不是「执行失败」。稳定 key 把多次网络尝试绑定到同一业务意图;request digest 防止同 key 被拿去做另一笔退款;幂等记录与 ledger outcome 共同成败,才能在进程崩溃与响应丢失之间仍返回同一个结果。这是把至少一次到达收敛成一次业务效果。
D2 无关杠杆违反 P1A:延长 timeout 也许降低重试频率,却无法消除「ledger 已提交、response 仍丢失」这个窗口。只要窗口存在,重复退款不变量就没有被保证;backoff 调的是时间,不是业务幂等。
D5 错层承载违反 P4B:防重复能力被放在了单个 client process 的短期内存里。重启、扩容、另一客户端重放、cache eviction 都会绕过它,而真正产生资金副作用的是 refund service / ledger 边界。能力住错层,就无法覆盖全体调用方。
D3 措辞冒充控制违反 P3D:response 没回来时,客户端本来就无法从网络现象确定 ledger 是否提交;让模型「确认」不会创造这份事实。涉及资金重复执行时,需要服务端可强制的幂等闸门,prompt 里的禁止重试只是请求。
QUESTION 03 · TS 2.4 · JSON SCHEMA EVOLUTION
A refund-result producer must add an optional risk_reason string. It has 23 independently deployed consumers; compatibility tests show that 6 legacy consumers reject any unknown JSON field. An all-at-once deployment is not possible. Which rollout is safest?
正确 B题干已经证明 6 个 consumer 不具备 additive change 所需的容忍性,所以先扩展 reader,再逐步扩展 writer。这个顺序让新旧 payload 都有兼容窗口,并用 reject metric 验证迁移;只有在全体 consumer 完成迁移后,才讨论收紧为 required。它把 schema 演进变成可观测的 expand → migrate → contract。
D6 半对半错违反 P1A:显式版本字段可以帮助支持多个 contract,但它不会改写旧 decoder 的行为。题干已说 6 个 consumer 会拒绝任何 unknown field;这 6 个在读到 schema_version 或 risk_reason 时仍会失败。前半句给了版本,后半句却宣称版本标签顺带实现了兼容。
D2 无关杠杆违反 P6C:compatibility test 已经定位源头是 deterministic schema rejection,不是瞬时 transport failure。重试同一份不兼容 payload 只会重复同一结果;加 timeout 也不会让 decoder 突然接受未知字段。
D7 过度工程违反 P10D:一个 optional field 可以用 reader-first 的兼容窗口完成,题干没有证据表明必须复制整套基础设施与历史数据。直接并行一套 service、queue、worker 和 database 增加了双写、迁移、运维与一致性风险,却没有先用最轻的有效演进手段。
QUESTION 04 · TS 2.4 · ASYNC CONCURRENCY
A batch worker launches 30,000 API calls with one unbounded gather. Memory rises from 1.4 GiB to 11.8 GiB, the downstream service returns 429 for 27% of calls, and canceling the parent job leaves many requests running. Which redesign best addresses the observed failure modes?
正确 D四个动作分别对应四个观测:bounded pool 控制 memory 与连接数,按下游额度定上限可降低 429,backpressure 防止 producer 无界堆积,structured cancellation 停掉已失去调用方的子任务;per-item outcome 则让 retry 只作用于可恢复失败。它没有把并发误当成越大越好。
D2 无关杠杆违反 P6A:trace 指向的是在途数量、下游限流与 cancellation 传播。延长 timeout 会让 30,000 个 task 占资源更久,立即重试 429 还会继续冲击已经限流的服务;两个旋钮都没有处理已定位的源头。
D6 半对半错违反 P6B:隔离单项失败可以是合理策略,但返回 null 抹掉了 error type,也没有限制 30,000 个并发任务或传播 cancellation。它只让表面上的 parent exception 消失,memory、429 与遗留请求三类故障仍在。
D4 换模型当解法违反 P1C:题干所有指标都来自 integration concurrency:连接、内存、限流、取消。没有证据指向模型能力或输出质量;换模型后同样的 30,000 个 coroutine 仍会以同样方式压垮下游。
QUESTION 05 · TS 2.2 · SYSTEMS LIFE CYCLE
During a 10% canary, API p95 latency is 180 ms against a 250 ms target and the error rate stays below 0.2%. However, input is 24 jobs/s, completed throughput falls from 30 to 18 jobs/s, and queue age exceeds its 10-minute alert for 35 minutes. What should the team do next?
正确 Cqueue age 连续越线且 throughput 低于 arrival rate,说明 backlog 会持续增长;入口 latency 正常并不能抵消这个事实。正确动作把 operate 阶段的真实证据送回 implement/develop:先限制暴露面,定位 worker capacity,恢复稳态,再更新 capacity requirement、告警与 runbook。life cycle 是闭环,不是阶段间甩手。
D2 无关杠杆违反 P6A:提高 alert threshold 只让故障更晚被看见,不会改变 24 jobs/s 进入、18 jobs/s 完成的净积压。数据已经把源头指向消费能力;拿入口 latency 覆盖 backlog 指标,是在修一个正常组件并忽略异常组件。
D6 半对半错违反 P6B:CI、latency 与 error rate 确实是有效证据,但它们只覆盖了部分 failure modes。queue age 与吞吐差已经证明另一类故障;用三项通过掩盖一项持续恶化,是典型「半对半错」。
D7 过度工程违反 P10D:backlog 证明 capacity 有问题,不证明 queue 产品本身是根因。profile、worker concurrency、下游 service time 或资源配额都还没有测;在这些轻量诊断与容量调整之前重写平台,扩大了 change surface 与迁移风险。
QUESTION 06 · TS 2.4 · CODE REVIEW
A 1,200-line pull request changes risk_score from optional to required, backfills missing database values with 0, and updates unrelated formatting. Fourteen consumers deploy independently. The PR has lint and happy-path tests, but no compatibility test, migration rehearsal, or rollback plan. What should block approval?
正确 Areview 的 blocker 是可观察 contract、数据语义与恢复路径:0 是否等同「未知」、14 个 consumer 能否读 required field、migration 失败后如何恢复。拆掉 formatting 可让风险按 diff 暴露;compatibility test 与 rehearsal 给出合并证据;rollback / roll-forward 让实施不是单向门。formatter 绿灯替代不了这些判断。
D3 措辞冒充控制违反 P3B:major version 与 warning 可以传达意图,却不会阻止旧 consumer 读到不兼容 payload,也不会让 backfill 可逆。数据与 contract 的失守代价需要 rollout gate、compatibility check 和迁移机制;给 change 改名不是闸门。
D2 无关杠杆违反 P1C:命名、函数长度与 formatting 都没有覆盖题干指认的 schema、数据语义、独立部署和恢复风险。选项把 review 注意力耗在已由自动化覆盖的变量上,却没有回答任何一条关键约束。
D6 半对半错违反 P1D:feature flag 只有在它包住风险动作时才有用。选项自己说明 migration 在 flag 之前执行;一旦 required-field 或 backfill 破坏 consumer,关掉应用路径也回不去。前半句用了真实手段,后半句却让它承担覆盖范围之外的恢复能力。
QUESTION 07 · TS 2.4 · LARGE REFACTORING
A team must rename customer_id to account_id across a producer, a queue schema, a database, and 14 consumers owned by different teams. Deployments cannot be coordinated into one maintenance window. Which plan creates the safest large refactor?
正确 D这是 expand → migrate → contract:先扩 reader,再扩 writer 与数据,观测 consumer inventory,最后才收缩旧 contract。每一步都兼容前后相邻状态,能独立部署、验证与回退;「旧字段 usage 为零」还是删除动作的客观 gate。大重构的安全来自分阶段 seam,不来自一次性协调。
D7 过度工程违反 P10A:字段 rename 可以在现有 contract 上用双读、双写与兼容窗口完成,题干没有证据表明必须复制 API、queue、database 和全部历史数据。双平台运行一年会新增同步、回填、对账与运维风险;更轻的 additive migration 尚未失败。
D2 无关杠杆违反 P1B:retry 解决瞬时投递失败,不会给旧 consumer 补出它需要的 customer_id。同一份 schema-incompatible payload 投递再多次仍不兼容;选项没有满足独立部署期间新旧版本共存这一核心约束。
D6 半对半错违反 P1C:新增 account_id 看似 additive,但同一 release 删除旧字段立刻破坏 14 个尚未迁移的 reader。「新字段先出现」只有在旧字段继续保留时才成立;选项用前半句掩盖了后半句的 breaking change。
QUESTION 08 · TS 2.4 · SMALL REFACTORING
A pull request is titled “behavior-preserving parser extraction.” It extracts a JSON parser, but the same commit also changes invalid JSON from HTTP 422 to HTTP 500, causing clients to classify the response as retryable. Tests cover valid payloads only. What is the best next step?
正确 Bextract parser 可以是小型、无行为变化的 refactor;422 → 500 却是外部 contract change,还会改变 client retry 与负载。先用 characterization test 钉住现状并拆开 commit,reviewer 才能分别判断结构改善与行为变更,且可单独回退。PR 标题不能把后者变成前者。
D6 半对半错违反 P1A:给 helper 加单测能保护内部逻辑,却没有覆盖调用方可见的 status code 与 retry classification。前半句是合理的局部验证,后半句却从「函数被测」跳到了「全部可观察行为不变」,遗漏了题干最关键的 contract 风险。
D4 换模型当解法违反 P1C:问题不是生成代码的能力,而是一个 diff 混合了两种 change、测试又没覆盖 observable behavior。换模型不会自动决定 422 还是 500,也不会替团队评估 client retry 和 rollout contract。
D7 过度工程违反 P10D:题干只需要拆分 change 并补 contract tests,没有证据表明 parser 必须拥有独立部署、网络边界与运维面。为小重构新增服务,会引入 latency、可用性、版本与部署问题,反而扩大风险。