调试与重构 — 把报错当线索,把测试当保险
调试与重构 — 把报错当线索,把测试当保险
这一章讲什么: 代码总会错,而且 AI 写得越快,错来得越快。这一章给你两条纪律: 调试时,把报错当程序在跟你说话——错误分三类,问法各不同,配一套六步流程; 改代码时,先写测试锁住行为再动手——重构不是可选项,是职业卫生。 读完你会拿到一套可重复的排错仪式,和一条「测试先行」的安全绳。
1. 这一章讲什么
第三章结束时你拥有了一个能跑的程序——它是结对生成的,你懂每一行,但它还没出过事。 这一章处理出事之后怎么办。
书里给这一章的定位很直白:调试一直是编程里最不 glamorous(不光彩)却最锻炼人的部分, 而 ChatGPT 这类工具已经从「帮你写代码的助手」变成了「调试与重构的伙伴」, 它能照亮连老手都会忽略的盲区1。但书里同时立了一个全书反复出现的规矩: AI 可以提方案,正确、高效、符合你设计意图的最终裁判是人2。
这一章在全书链条里是「保险层」:第五到九章的项目都会出错, 到时候你是慌,还是按流程走,取决于这一章练没练。
2. 顶层全景:两条纪律,一个循环
纪律一:调试是沟通
报错信息 → 判断错误类型(语法/运行时/逻辑) → 带上下文问 AI
→ 拿到解释与候选修复 → 应用 → 测试确认
纪律二:重构是预防式调试
先写测试锁住现有行为 → 小步改结构(拆函数/去重复/模块化)
→ 每步跑测试 → 行为不变、结构变好
│
▼
共同底座:pytest 失败测试(把「我以为」变成「我验证过」)
图说:两条纪律在「测试」处汇合——调试靠失败测试复现,重构靠测试网兜底。3. 核心原理
3.1 报错信息是程序在跟你说话
书里用一个场景开场:花一小时写完脚本,期待第一次成功运行, 结果控制台刷出一墙红字。新手在这里卡住,不知道发生了什么、从哪下手; 连老手有时也需要一点时间解码 traceback 到底在说什么3。
先解释三个词。 报错信息(error message) 是 Python 告诉你的「哪里不对」; 堆栈追踪(stack trace,也叫回溯) 是出错时「程序从入口一路调用到出错点的完整路径」, 像程序留下的脚印;traceback 是 Python 打印堆栈追踪的固定格式(最下面一行是错误类型, 往上依次是每一层调用及其源代码行)4。书里把它们统称为 Python 在「跟你谈论什么出了错、在哪里」5。
Python 的错误分三大类,这是本章的第一个承重结构6:
| 类型 | 什么时候出现 | 书里的例子 |
|---|---|---|
| SyntaxError(语法错误) | 代码连 Python 的文法都过不了,根本没开始跑 | 函数定义后忘了冒号 |
| RuntimeError(运行时错误) | 程序跑起来了,执行中途撞墙 | 拿空列表求平均 → 除零 |
| Logic Error(逻辑错误) | 不崩,但结果不对 | 折扣该减 20%,代码却算出 20 |
主线例子第一圈:一个 TypeError 的完整解剖。 这是本章的主线例子,从一段报错走全流程:
Traceback (most recent call last):
File "expense_tracker.py", line 12, in <module>
add_expense("100", "Food", "2025-11-11")
File "expense_tracker.py", line 5, in add_expense
writer.writerow([amount, category, date])
TypeError: unsupported operand type(s) for +: 'int' and 'str'读法自上而下三层7:
- 最底层那行是错误类型与内容:
TypeError: unsupported operand type(s) for +: 'int' and 'str'—— 你试图对整数和字符串做加法(这里其实是字符串拼接撞上了类型不匹配)。 - 往上是出错的那行代码及其所在函数:
writer.writerow(...)在add_expense里。 - 再往上是你的入口调用:
line 12调了add_expense("100", ...)——注意传进去的是字符串"100"。
第三层正是破案关键:amount 是字符串 "100"**,而写 CSV 的行需要类型兼容。 修复:写之前转成数,writer.writerow([float(amount), category, date])`8。
书里特别标了「为什么重要」:你不但修了 Bug,还理解了根因**——你的数据类型和 Python 预期之间的那个错配9。
这个读法可以直接交给 AI,而且要给上下文。 书里给的提示样板:
Context: 我有一个 calc.py,里头的 average(values) 现在是 sum(values)/len(values)。 Problem: 我的 pytest 在空列表上失败(ZeroDivisionError)。 Constraint: 我希望空列表返回 0(不抛异常),其他行为保持不变。 Goal: 给一个小的、地道的修复和一两句解释;兼容 Python 3.8+。10
注意这就是第三章的 CCG——背景、问题(约束)、目标——调试提示是 CCG 的第一个实战场所。 AI 的典型回答是加一个 guard clause(卫语句,函数开头先拦住非法输入的早退判断):
def average(values):
if not values:
return 0
return sum(values) / len(values)3.2 三类错误,三种问法
SyntaxError:最好抓,最常烦。 书里两个例子覆盖了从浅到深:
- 浅:
def greet(name)后面漏了冒号 →SyntaxError: expected ':'。 贴给 AI 问「这函数我哪里语法错了」,秒回11。 - 深:
data = [1, 2, 3, 4列表没闭合 →SyntaxError: '(' was never closed。 嵌套深的时候报错行号可能指错地方,AI 的价值在于看整体结构找不配对12。
书里点了一句:AI 不只是修语法,它解释原因——这就是调试直觉的来源13。
RuntimeError:跑起来才炸的。 书里演示了三种典型:
ZeroDivisionError(空列表求平均)、IndexError(取了列表里不存在的下标)、
KeyError(字典里没有那个键)14。这类错误 AI 特别有效,因为它能同时读你的代码和报错信息,
推理出可能的诱因。书里还演示了「时灵时不灵」的隐蔽运行时错误:
expenses.csv 是空文件时,读取的循环会因为没有行而失败——
AI 看到函数加样例输入,会主动建议加空文件检查,把你的思维从「修当前错」推向「预想到边的错」15。
LogicError:最阴的——程序不崩,结果撒谎。 书里的例子:
def calculate_discount(price, discount):
return price * discount # 想打八折,却算出了折扣额本身
print(calculate_discount(100, 0.2)) # 输出 20.0,期望 80.0贴给 AI:「这个函数应该对价格打 20% 折扣,但输出不对,哪错了?」 它指出逻辑写成了「价格 × 折扣率」而不是「价格 × (1 − 折扣率)」16。 书里把这类调试的要害说成一个短语:the intention gap(意图鸿沟)——你写的和你想的之间的缝, 看见这条缝,是调试思维的关键跃迁17。
人机分工的对照表(书里给了,值得贴墙):
| 人 | AI | |
|---|---|---|
| 传统做法 | 读报错 → 搜 → 翻文档 → 试 → 测 → 循环 | —— |
| 结对做法 | 判断修复是否正确、高效、没引入新问题 | 读代码+报错,给解释与候选修复18 |
一句话:AI 加速理解,但不替代谨慎思考。
3.3 六步调试流程:把排错变成仪式
书里反对「调试是混乱冲刺」的说法,给出六步可重复流程(reproducible workflow), 每步配具体动作和提示词19:
① 复现并记录失败 ② 缩小范围(隔离出最小的错) ③ 写失败测试
④ 带上下文问 AI ⑤ 应用候选修复,跑测试 ⑥ 重构并加固① 复现并记录。 书里把这条称为「最被低估的一步」:如果 Bug 不能可靠地复现,你就无法证明修好了20。
记录三样东西:触发它的确切命令、完整堆栈、输入数据。
主线例子的第二步:average 在空列表上炸——先建 runner.py 调用 average([]) 复现。
② 缩小范围。 找到能复现错误的最小代码块。这里就是 average([]) 本身。
然后建一个针对性的 pytest 文件:
# test_calc.py
from calc import average
import pytest
def test_average_normal():
assert average([10, 20, 30]) == 20
def test_average_empty():
assert average([]) == 0 # 我们决定:空列表该返回 0pytest -q 一跑,一个过一个挂——你现在有了一个可复现的失败测试,完美21。
③ 写失败测试(把期望说死)。 这一步强迫你先决定期望行为:空列表该返回 0、None, 还是抛自定义错误?在问 AI 之前把这个决定写进测试。 书里强调:这个清晰度决定了你后面能不能评判 AI 的建议22。
④ 带上下文问 AI。 用 §3.1 末尾那个 CCG 样板提示。AI 给了 guard clause 版本, 并解释为什么(卫语句挡住除零),顺带提边界情况23。
⑤ 应用候选修复并跑测试。 更新 calc.py,pytest -q 全绿。
还失败?把新的 traceback + 更新后的代码 + 你试过什么,贴回同一个对话:
"I applied your suggestion, but pytest still fails with this traceback [贴]. 这是我的更新后代码 [贴]。请找出测试仍失败的原因,或提议替代修复。"24
书里点出这个提示为什么有效:它让对话保持聚焦和建设性——在同一线程里迭代, AI 保有全部上下文。
⑥ 重构并加固。 测试绿了也别停,花两分钟加固:加 docstring; 加非数字输入的测试;加类型守卫或抛出信息更清楚的 ValueError。 书里给出的「抛光版」average 长这样:
def average(values):
"""计算一组数字的平均值。
- 空序列返回 0
- 输入不可迭代抛 TypeError
- 任一项非数字抛 ValueError
"""
if not hasattr(values, "__iter__"):
raise TypeError("Input must be an iterable.")
total, count = 0.0, 0
for value in values:
try:
total += float(value)
count += 1
except (TypeError, ValueError):
raise ValueError("All items must be numeric.")
if count == 0:
return 0
return total / count再补两个测试:average(["1", "a"]) 应抛 ValueError;average([5]) == 5.025。
从「能跑」到「耐造」,就差这一步。
更难的一圈:归一化函数的数值陷阱。 「归一化」指把一组数按比例缩放,让不同量级的数值变得可比; 书里的 normalize 例子是把每个值除以平均值。这一圈演示流程在性能/数值 Bug 上的走法:
def normalize(values):
mean = sum(values) / len(values)
return [v / mean for v in values]问题:均值是 0 怎么办?均值是接近 0 的浮点数怎么办(结果爆炸)?
流程照旧:写失败测试 normalize([0,0,0]) 期望 [0,0,0];隔离;
然后带约束问 AI——「均值为 0 或接近 0 时结果无效;希望 mean == 0 时返回原列表,
否则安全归一化;给个数值健壮的实现并解释取舍」。
AI 的典型建议:判断 abs(mean) < 1e-12(一个极小的数,epsilon,数值计算里挡除零的垫子)就返回原列表;
或用 epsilon 防除法不稳;或用 statistics.mean 加回退26。
取舍(transparency of tradeoffs)是这一步的关键词:好的 AI 回答不只给代码,还说清每种做法的代价。
3.4 重构:先锁行为,再动结构
重构(refactoring)= 不改变外部行为地重写代码结构——让它更干净、更模块化、更好维护。 书里给了一个亲历证据:作者曾加入一家每改一处都要先读一周代码的公司, 原因是代码里全是重复、隐式副作用、一个函数干六件事;重构后「Bug 率骤降,新人入职从以天计变成以小时计」。 这就是重构为什么不是可选装修,是职业卫生27。
工具箱(一次装好,全程复用):
pip install pytest black flake8 mypy| 工具 | 干什么 |
|---|---|
| pytest | 跑测试 |
| black | 统一格式化(代码排版) |
| flake8 | 基础 lint(静态检查常见坏味道) |
| mypy | 可选的静态类型检查 |
它们的共同作用:验证你的重构保住了行为、提升了清晰28。
主线例子第二圈:报告生成函数的两阶段重构。 书里的脏函数 generate_report(file_path)
一口气干了四件事:读 CSV、解析、聚合、格式化输出。毛病清单29:
- 一个函数同时干 IO、解析、聚合、格式化——没法单独测;
- 重复:
r[2]转 float 散在三处; - 魔法下标:
r[0]、r[1]、r[2]是什么意思?得靠猜; - IO 与逻辑纠缠,单测无从下手。
动刀前的一步(本章第二重要的习惯):先写测试锁住行为。
def test_generate_report():
data = "2025-01-01,North,100\n2025-01-02,North,50\n2025-01-01,South,200\n"
with tempfile.NamedTemporaryFile("w+", delete=False) as tf:
tf.write(data); tf.flush()
output = generate_report(tf.name)
assert "North: total=150.00" in output
assert "South: total=200.00" in output测试通过 = 行为已被捕获,「现在可以安全地重构了」30。
阶段一:小步提取函数。 只把「解析一行」抽出来,引入一个 dataclass(数据类, 一种专门用来装数据的类,声明字段就自动获得初始化、打印等功能):
@dataclass
class SaleRow:
date: datetime
region: str
amount: float
def parse_row(row):
return SaleRow(date=datetime.strptime(row[0], "%Y-%m-%d"),
region=row[1], amount=float(row[2]))generate_report 里循环体换成 rows.append(parse_row(r))。跑测试,还绿。
这一步消掉了重复(转换只发生一次),解析变显式31。
阶段二:让 AI 做更大的拆分(带硬约束)。 书里的提示值得照抄:
Context: 我有个报表函数,读 CSV、解析行、聚合总额、算平均、格式化文本报告。 我已把解析拆成 parse_row;剩下的 generate_report 仍混着 IO 和聚合,用循环算平均。 Constraint: 保持文本输出格式不变(兼容其他系统),只保留正金额的行为;只用标准库。 Goal: 把读、聚合、格式化拆成三个函数——
read_sales(file_path) -> List[SaleRow]、aggregate(sales) -> Dict[str, Dict[str,float]](返回总额与计数)、format_report(aggregation) -> str。只返回代码。32
拆完的结构:
def read_sales(file_path): # 只管读文件 → SaleRow 列表
def aggregate(sales): # 只管聚合 → {region: (总额, 计数)}
def format_report(aggregation): # 只管拼文本
def generate_report(file_path): # 三行,把上面三个串起来变好的四句话(书里的原话,逐条对应第二章的四个词):单一职责——每个函数一件事,好测; DRY——float 转换不再重复,总额与计数一起算;模块化——aggregate 可以被别的数据源复用; 类型注解加 dataclass 让代码自我说明33。
验证清单(重构后的规定动作): pytest -q 全绿;black . 统一格式;
flake8 查坏味道;mypy report.py 查类型——只修代表真实问题的警告,不为风格洁癖打架34。
然后给三个新函数各补一个单测(读文件用临时文件、聚合用手造的 SaleRow、格式化查字符串精确),
把网织密35。
加固与演进(书上叫 Evolve without Breaking): 加边界测试(空文件、负金额、畸形行);
从多处收 CSV 的话,把 parse_row 换成返回 Optional[SaleRow] 的 safe_parse_row,坏行记警告跳过;
打印换成 logging(日志库,生产代码的标配);文件大就用**生成器(generator,一次产一个值、
不一次性全装进内存的迭代方式)**流式处理36。
什么时候不该重构(书里诚实地列了三条红线):
| 陷阱 | 说明 |
|---|---|
| 过早泛化 | 别为不存在的需求抽象;保持 KISS(Keep It Simple, Stupid,怎么简单怎么来) |
| 破坏公共 API | 别的系统依赖函数签名或文件格式时,保向后兼容,或给兼容垫片 |
| 过早重度类型化 | 没有测试兜底的激进静态类型会拖慢进度;mypy 检查逐步加37 |
重构期间的实用提示三则(书里的原样): 「把 CSV 读取逻辑抽成 read_sales,保持行为,只显示改动」 「给 aggregate 函数建议单测,含空输入和负金额的边界」 「我想把报告输出改成可选的 JSON,加个 format 参数,要最小改动且向后兼容」。 共同特点:范围小、要改动清单、要求向后兼容38。
3.5 动手前的一次性准备(书里的 Setup Checklist)
六步流程每次都要走,但有三件事是每个项目只做一次的。书里的清单39:
# 建虚拟环境(项目开始时一次)
python -m venv .venv
source .venv/bin/activate # macOS/Linux;Windows 用 .venv\Scripts\activate
# 装测试工具(一次)
pip install pytest pytest-cov外加一条容易忽略的:为实验开一条 Git 分支:
git checkout -b debug/<short-description>书里给的理由只有一句,但分量很重:AI 建议的修复可能把整个项目炸掉, 分支是你的安全绳40。这和第四章「先写测试锁行为」是同一类纪律的两种形态: 一个保代码,一个保结构。
3.6 章末练习:KeyError 的完整排障圈
原书章末的练习是一道刻意埋了 typo 的题,正好把六步流程再走一圈:
expenses = [{"amount": 100, "category": "Food"}, {"amount": 50, "category": "Transport"}]
total = 0
for item in expenses:
total += item["amounts"] # ← typo:键名多了个 s
print(total)跑起来必炸 KeyError: 'amounts'。按流程走:
- 复现:运行,记录报错全文——
KeyError: 'amounts',File 行号指向total += ...一行; - 缩小:问题就一句,无需再缩;
- 失败测试:为这个函数写测试:传入上面的 expenses 列表,期望 total == 150;
- 问 AI:"I got a KeyError: 'amounts'. Explain why this happened and how to fix it."41
AI 会指出:字典里的键叫
amount,代码写的是amounts——KeyError 的全部含义就是 「你问的键不在字典里」; - 修复:改成
item["amount"],测试转绿; - 加固:书里建议接着做变体实验——把数字换成字符串、删掉某个键—— 每次让 AI 解释「为什么会报错、怎么优雅处理」,把 KeyError/TypeError/缺键的防御写法各见一遍42。
这个练习教的不只是修 typo,而是建立「报错类型 → 病因」的条件反射。 书末 Key Terms 把本章的八个词收了尾43:stack trace(错误足迹)、logic error、 runtime error、syntax error、refactoring、DRY、modularization、AI-assisted debugging—— 八个词里前四个是「读错」,后四个是「改码」,正好对应本章的两条纪律。
3.7 重构前后的对照总表:一个函数的四代
把本章的重构主线收进一张表——同一个报表函数,从「能跑」到「耐造」走了四代, 每一代动一刀,每刀都有测试兜底:
| 代 | 形态 | 动了哪一刀 | 验证 |
|---|---|---|---|
| 0 | generate_report 一大坨 | ——(起点,行为未知) | 先写测试锁住它 |
| 1 | 抽出 parse_row + SaleRow | 解析与聚合分离;float 转换从三处收一处 | pytest -q 绿 |
| 2 | read_sales / aggregate / format_report 三函数 | 单一职责;DRY;模块化 | 测试绿 + black/flake8/mypy |
| 3 | 加类型守卫、卫语句、logging、流式处理 | 加固:边界、异常、性能 | 新增边界测试全绿 |
这张表是「渐进重构」的操作定义:不是重写,是一串小步; 不是「改完再测」,是「每步必测」;不是追求完美,是「下一步比上一步好一处」44。
3.8 对话式重构的提示库(书里的 Practical Prompts)
§3.4 给了三条,这里把本章出现过的重构提示全收进来,按用途分类—— 它们有一个共同的配方:小范围 + 保行为 + 要改动清单:
抽取: "Given the code below, extract the CSV reading logic into a
read_sales(file_path) function that returns List[SaleRow].
Preserve current behavior. Show changes only."
补测: "Suggest unit tests for the aggregate function above.
Include edge cases for empty input and negative amounts."
加参数: "I want to change the report output to JSON as an optional
argument format='text'|'json'. Show a minimal and
backward-compatible change."书里的使用纪律:提示保持紧凑范围;打算贴了就跑的时候要明确要 code-only 的回答45—— 否则 AI 会在代码前后裹大段解释,粘进文件还得手动剥。
3.9 成绩函数的双重 Bug:一个例子的两层修复
§3.2 末尾的人机分工表之后,书里还有一个更立体的例子,值得单独走一遍——
grade_student 函数,它身上叠着两层问题:
def grade_student(scores):
average = sum(scores) / len(scores)
if average > 80: return "A"
elif average > 60: return "B"
elif average > 40: return "C"
else: return "D"
print(grade_student([80, 90, 70])) # 正常运行
print(grade_student([])) # ZeroDivisionError:除零第一层(显性的):空列表炸除零。第二层(隐性的,书里的 AI 回答主动指出的):
边界用 80 分到底算 A 还是 B? average > 80 意味着 80 整只算 B——
这究竟是设计如此还是 off-by-one(差一错误:边界本该含 80 而漏了)?书里让 AI 给出的
安全版本加的是输入验证(if not scores: return "No scores provided"),
并补一句「也可以考虑加 logging,或在数据缺失时抛自定义错误」46。
这一例的教学点:好的调试回答不只修你问的,还把你没问的边界一并照亮。 「average 恰好 80 该算什么」这个问题,在原代码里没有任何报错信号—— 它是逻辑层面的潜在争议,只有「带着测试思维读代码」才会浮现。 这就是「人负责裁判」的具体内容:AI 照亮盲区,你决定 80 算不算 A。
3.10 防御式编程的谱系(书里散落的写法聚成一族)
本章出现了好几种「预先拦住坏输入」的写法,它们是一个家族,按轻重排开:
| 写法 | 重量 | 什么时候用 |
|---|---|---|
guard clause(if not values: return 0) | 最轻 | 单个已知坏条件,函数开头早退 |
默认值/温柔取法(.get("city", "Unknown")) | 轻 | 缺键是常态而非错误 |
抛带信息的异常(raise ValueError("All items must be numeric.")) | 中 | 坏输入是调用方的错,必须让它知道 |
| try/except 兜底转日志 | 重 | 边界情况不该中断主流程(如批处理跳过坏行) |
书里没有给这张谱系,但 normalize 例的 eps 垫子、average 例的 guard clause、
第六章 safe_parse_row 的「返回 None + 记警告」,分别落在谱系的不同档上。
选哪档,取决于坏输入是「预期之外」(选重的)还是「预期之中」(选轻的)——
这个判断书里交给读者,我们把它显式化了。
3.11 重构提示的「黄金三句」再扩展(书外的实操补充)
§3.8 的提示库收了三条书里的。实战里还有三条高频句式,书里散落出现、值得聚拢 (出处随句附在后):
「保持行为,只显示改动。」——治 AI 重写过度的特效药。 没有这句,AI 常把无关行也重排,review 时你得逐行找真改动。 (同源提示见书里的 "Show changes only."47)
「先别改代码,先告诉我你会怎么拆。」——把重构方案的决定权抢回来。 AI 先说拆法,你同意,它才动手;不同意,改方案比改代码便宜得多。 (与书里「先要结构再要代码」的脚手架思路一致48)
「这个改动会让哪个测试变红?」——反向使用测试网。 不是改完再跑,而是改前预测:答得上来,说明你和 AI 都真懂这个代码库; 答不上来,说明测试覆盖有洞,先补洞。
3.12 本章的两个「为什么」收尾
为什么调试是「沟通」而不是「修理」? 书里的结语给了解释: 每个报错信息、每条堆栈、每次意外输出,都是程序在试图告诉你什么—— 你听得越好,进步越快49。AI 在这个比喻里的角色是「同声传译」: 把程序的方言翻成人话,但对话的双方仍然是你和程序。
为什么重构是「预防式调试」? Points to Remember 里有一句最短的话点了题50。 逻辑:混乱的结构是 Bug 的温床——重复代码让改一处漏一处,长函数让边界看不见, 隐式副作用让「谁改了状态」成谜。重构把这些温床拆掉,未来的 Bug 自然少了。 调试治已病,重构治未病;本章把两者放进同一章,就是这个因果。
3.13 normalize 例的完整决策记录(书里第三圈走查)
§3.3 的 normalize 例子是本章流程走得最完整的第二例,值得把决策链单独存档—— 它展示了「写失败测试」那一步里期望是怎么定下来的:
- 先造一个注定失败的测试:
normalize([0, 0, 0])期望返回[0, 0, 0](均值是 0,没有任何归一化可做,原样退回就是合理期望)。 当前实现:mean = 0 → 每个元素除以 0 → 崩或出 inf(无穷大)51; - 隔离:问题锁定在
v / mean这一行; - 带约束问 AI:「mean 为 0 或接近 0 时结果无效;期望 mean == 0 时返回原列表, 否则安全归一化;给数值健壮的实现并解释 tradeoffs」52;
- AI 的三种候选解法(书里的原样列举):
- 判断
abs(mean) < 1e-12就返回原列表; - 用一个小的 epsilon 垫子避免除法不稳;
- 用 statistics.mean 加回退;
- 判断
- 采纳与验证:选带 eps 参数的写法,补 eps 行为的测试,
pytest全绿再迭代53。
第 4 步是本章最重要的一瞥:AI 给了三个解法,不是一个。 「解释 tradeoffs」 这个 Goal 部件起了作用——它逼 AI 把取舍摊开,你才能按自己的场景选。 如果提示里没有这四个字,AI 通常只给一个「标准答案」,而标准答案未必适合你的数据。
3.14 一章两份的读物结构
最后说明本章在全书里的读法:调试与重构被放进同一章,是原书的结构选择, 也是合理的——它们共享同一套底座(测试、小步、验证),只是方向相反:
调试:行为不对 → 找到哪一步错了 → 改到对(测试从红到绿)
重构:行为已对 → 让结构更好 → 保持对(测试一直绿)
共同底座:pytest / 小步 / 每步必验 / AI 只出建议读的时候别只学一头:会调试不会重构,你的代码会越修越乱; 会重构不会调试,测试红了不知道哪一步打破的。两头都练,第四章才算读完。
3.15 动手机之前的十分钟仪式(本章流程的浓缩版)
把全章压成一个可执行的仪式——每次遇到 Bug,先花十分钟走这套,再决定要不要更深挖:
第 1 分钟:复现。 exact 命令 + 完整堆栈 + 输入,复制进笔记
第 2 分钟:读最底层那行错误类型,判断三类(语法/运行时/逻辑)
第 3 分钟:缩小。能造出最小复现吗?(一行调用最好)
第 4–6 分钟:写失败测试。把「期望」从脑子里搬到 pytest 文件里
第 7–9 分钟:CCG 问 AI(代码 + 报错 + 期望 + 你试过的)
第 10 分钟:应用、跑测试。绿了 → 加固;红了 → 带新报错回第 7 分钟这个仪式的每一分钟都对应书里的一步,没有一分钟是我们加的。 它的价值在于「把 panic 变成 procedure(把恐慌变成程序)」: 报错来了不再是从零开始的慌乱,而是十条已经踩熟的路。
3.16 两个工具的一句职责(别再用混)
本章装了四件套(pytest/black/flake8/mypy),新手最常见的混用是把它们当一个东西:
pytest 答:行为对不对?(跑测试,红的绿的都是答案)
black 答:长得整不整齐?(只排版:缩进、空格、换行,不改逻辑)
flake8 答:有没有坏味道?(未用变量、未定义名、行太长——可能暗藏真 Bug)
mypy 答:类型标得对不对?(int 传给 str 参数?提前在跑之前抓)分工的记忆钩:pytest 管「对不对」,其余三个管「清不清楚」。 四者跑的顺序书里有暗示:先 pytest(行为保住了没),再 black(排版), 再 flake8(坏味道),最后 mypy(类型)54——顺序的理由很朴素: 行为都还没保住时,讨论排版是奢侈;类型是锦上添花,放最后。
3.17 六步流程的两个变体(书里没写、从流程推出来的)
掌握六步主流程后,两种常见变体可以从流程本身推出来(我们的推演,挂在流程出处下55):
变体一:「热修复」版——测试在修复之后补。 线上着火时,顺序反转: 先最小改动止血(第④⑤步先行),测试后补(第③步移到第⑤步后)。 代价是止血改动没有测试网保护,所以止血后必须回到主流程补完第③步—— 热修复的债,当天还。
变体二:「探索」版——没有测试框架时。 接手一个还没有 pytest 的老项目, 先不建框架,用第①②④步 + print 验证跑通;攒下三个以上同类 Bug 后, 再一次性把测试框架立起来,把探索期手验过的 case 固化成测试。 书里的 setup checklist 默认项目从零开始56;这个变体是「从零项目」到「遗留项目」的桥。
3.18 本章与第三、九章的两条回路
回路一(与第三章): 调试提示就是 CCG——§3.1 的样板(Context/Problem/Constraint/Goal) 与第三章的四件套同构。第四章是 CCG 的第一个高频实战场:每修一个 Bug, 就是一次小型的「分解 + 结构化表达」练习;提示写得越熟,第三章越不需要回头。
回路二(与第九章): 第九章的「故意发坏请求」「错误四分类」是本章内容在 Web 上的重排: 脚本的「终端红字」换成应用的「状态码与 JSON 报错」,pytest 换成 curl, 六步流程换成「设计契约 → 测好/坏路径」。读完第九章再回看,本章的每个概念都有网络版。
3.19 调试的「提问进化」:同一报错的三代问法
同一类报错,随着本章方法的内化,你的问法会进化三代——把 TypeError 当标本:
第一代(贴报错):「TypeError: unsupported operand type(s) for +: 'int' and 'str',怎么修?」 → AI 给通用解释,你自己还要翻译回自己的代码。
第二代(贴报错 + 代码):「这个函数在写 CSV 时报 TypeError: int + str,代码如下……」 → AI 定位到 amount 是字符串,给转换修复——本章 §3.1 的常规动作。
第三代(贴报错 + 代码 + 期望 + 测试):「test_add_expense 在 amount 传字符串时期望 自动转 float,现在抛 TypeError。这是期望行为写错了,还是实现缺了转换?」 → AI 被迫在「测试与实现谁对」之间表态,你拿到的是设计判断,不只是修复57。
三代的差别在「你给了 AI 多少自己的思考」。第三代把「期望」交出去, 等于把裁判席的一半让给 AI 来质询——这正是第四章「测试先行」的自然延伸。 问法的进化,就是调试能力的进化。
3.20 本章的一句话:Bug 是信,不是敌
最后收一句。本章教的所有流程,归根到底是同一个姿态的转变: 报错不是敌人打过来的一拳,是程序寄来的一封信。 信要拆(读堆栈)、要回(带上下文问)、要归档(测试固化)—— 书里的结语原话:调试不是惩罚,是沟通;你听得越好,进步越快49。
带着这个姿态学后面的章节:第五章的日志是程序的日常信件, 第七章的准确率是模型寄来的成绩单,第九章的 404 是路由寄回的退信—— 全书的工程实践,都是「读信」艺术的不同文体。
3.21 本章的最短版本(给时间紧的人)
整章可以压成五行,已经会 pytest 的人只看这五句:58
① 报错三层读:类型 → 出错行 → 谁调起来的
② 三类错误三种问法;逻辑错误要找「意图鸿沟」
③ 不能复现 = 不能修
④ 期望先写进测试,再问 AI——否则你无法评判它的建议
⑤ 重构前先锁行为;每步必测;AI 只出建议,测试说了算五行之外的所有内容,都是这五句的展开、例证与边界。时间紧时按此行; 有闲时回到各节,把五行在 average 与 normalize 两个例子上各走一遍—— 两个例子走熟,五行就长在手上了。
3.22 与第二章的分工澄清(两章都有「重构」,有什么不同?)
第二章 §3.7–3.10 也讲重构,与本章的分工容易混,这里钉一下:59
第二章的重构:读得懂的副产物——读懂遗留代码后,顺手让它更可读
本章的重构:工程纪律——测试先行、小步验证、红线三陷阱第二章练「眼光」(看得出哪里该改),本章练「手法」(改得安全)。 眼光没有手法,改动是赌博;手法没有眼光,你在给不烂的代码抛光。 两章合起来才是一个完整动作;顺序上先二后四——先用第二章的读法 找到真正该动的地方,再用本章的流程安全地动。
3.23 最后一格台阶:从「会修」到「会防」
本章把调试的六个动作都给了,还缺最后一步的自觉:每次修完,问一句「这类 Bug 怎么防再犯?」
书里散落的答案可以收成三类:同类错误写进测试集(第 ③ 步的固化); 共性的输入错误在入口处加校验(§3.10 的谱系);重复踩的坑写进团队 checklist (§3.5 的安装清单就是这种东西)。调试的终点不是绿灯,是这类红灯不再亮。 到那一刻,第四章才真正读完——你手里的不再是排错流程,是一套防错系统。
判断(我们的,不是书里的): 这一章对「AI 结对」的态度是全书最克制的一章—— 它把 AI 放在「解释器」的位置,把「裁判」的位置留给了测试。 这个分工是全书工作法里最重要的一条暗线:凡是能写成断言的期望,都不要交给 AI 评判。 原因很实际:AI 的解释可以错得理直气壮,而断言只认真假。 所以本章六步流程里最不能被省掉的是第 ③ 步——失败测试是整条流程里唯一不依赖 AI 品味的环节, 省掉它,六步就退化成「问 AI、信 AI」的两步,前面五章攒下的判断力无处安放。 如果错,会错在: 有些期望很难写成断言(「这段代码好不好读」), 那一类仍然只能交给评审与品味——本章的「AI 能解释,但你必须推理」 在这些地方依然是最后防线,测试盖不到所有判断。
4. 作者的判断与证据
有证据的判断:
- 「不能可靠复现就无从证明修复」——软件工程的通行公理,书里用在六步流程的第①步,正典20;
- 「重构前先锁行为」——以测试通过作为「行为已捕获」的判据,这是行业最佳实践30;
- 「六步流程 + CCG 调试提示」——本章论证最实的部分,有完整双例子(平均数、归一化)演示19。
经验之谈:
- 「重构让 Bug 率骤降、入职以小时计」——作者亲历,单一样本,方向可信、幅度不可外推27;
- 「mypy 逐步加」——稳妥但非唯一策略,有团队从第一天就全量类型化。
推测:
- 「AI 照亮老手的盲区」——本章开头的卖点句,无测量支撑,当作愿景读1。
5. 边界与局限
- 不覆盖并发与异步调试:多线程、异步代码的 Bug 特征(时序、竞态)完全不同,本章方法只覆盖单线程;
- 不覆盖调试器工具:pdb、断点、IDE 调试器都只在开头一句带过(「pdb 或 print 仍然有效」), 教学全押在「读报错 + 问 AI」上——快,但遇到「程序不崩只是慢」这类问题会不够用;
- 测试 Coverage 只字未提:没有讲覆盖率指标与「测试是否足够」的判据;
- flake8/mypy 的误报处理靠手感:「只修真问题」没有可操作标准,新手容易在两个极端(全修/全忽略)之间摇摆;
- AI 可能给出「让报错消失但不解决问题」的修复(比如把异常整个吞掉)——本章只在 §3.2 提了一句 「人负责裁判」,没有给出识别这类「静默失败」的具体手法(我们的补充:改完后问一句 「这个修复有没有掩盖错误而不是解决错误?」)。
6. 可带走的
本章主线例子,一行写完: average 空列表炸 ZeroDivisionError → 复现记录 → 缩小到 average([]) → 写失败测试(test_average_empty) → CCG 提示问 AI → 加 guard clause → pytest 全绿 → 加 docstring、非数字守卫、两个新测试 → 从「能跑」到「耐造」。
- 报错是程序在说话:先读最底层错误类型,再看出错行,再看出错行是被谁调起来的;
- 错误三类三种问法:语法(看结构配对)、运行时(代码+报错一起贴)、逻辑(说清期望输出,找意图鸿沟);
- 六步流程:复现 → 缩小 → 失败测试 → CCG 问 AI → 修复跑测试 → 加固;不能复现 = 没法证明修好;
- 期望行为先写死在测试里,再去问 AI——否则你无法评判它的建议;
- 修复后迭代提示:「我照做了但还失败,这是新报错+新代码+我试过的」——同一线程,上下文不丢;
- 重构是预防式调试:先写测试锁行为,再小步拆;每个函数一件事;
- 工具四件套:pytest / black / flake8 / mypy——重构后全跑一遍;
- 重构红线:不过早泛化、不破坏公共 API、不激进类型化;
- 拒绝「静默修复」:AI 的修复若只是吞掉异常,不算修复;
- 一句话收尾(书里的原话):「AI 能解释,但你必须推理。」60
7. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 调试是沟通、堆栈三层读法 | Debugging and Refactoring with AI | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:74(搜「talking to you」) · text/16-ch04-4-debugging-and-refactoring-with-ai.txt:166(搜「walk me through」) |
| 错误三类 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:87(搜「Syntax Errors」) |
| TypeError 解剖与根因 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:112(搜「simple terms」) · text/16-ch04-4-debugging-and-refactoring-with-ai.txt:239(搜「root cause」) |
| 上下文调试提示样板 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:815(搜「Context: I have」) |
| 逻辑错误的意图鸿沟 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:525(搜「intention gap」) |
| 人机分工对照 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:272(搜「Human Approach」) |
| 六步流程 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:688(搜「Reproduce and record」) |
| 复现是最被低估的一步 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:713(搜「underrated」) |
| 失败测试锁期望 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:800(搜「desired behavior」) |
| 修复失败后的迭代提示 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:882(搜「still fails」) |
| 归一化数值陷阱 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:995(搜「normalize」) · text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1048(搜「eps」) |
| 重构=职业卫生 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1072(搜「professional hygiene」) |
| 工具四件套 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1081(搜「pytest black flake8 mypy」) |
| 脏函数四大毛病 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1186(搜「Single function doing」) |
| 动刀前先锁行为 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1247(搜「captured behavior」) |
| dataclass 提取 parse_row | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1300(搜「class SaleRow」) |
| 三函数拆分提示 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1399(搜「read_sales(file_path)」) |
| 变好的四句话 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1547(搜「Single Responsibility」) |
| 验证清单与补测试 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1566(搜「faith-based」) |
| 红线三陷阱 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1672(搜「Premature Generalization」) |
| 章末 KeyError 练习 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1767(搜「KeyError: amounts」) |
| 实验分支安全绳 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:681(搜「safe rollback」) |
| Key Terms 八词 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1781(搜「trail your program leaves」) |
| 成绩函数两层修复 | 同上 | text/16-ch04-4-debugging-and-refactoring-with-ai.txt:537(搜「grade_student」) |
| 防御写法一族 | (我们的归纳) | — |
Footnotes
-
出处:「Debugging and Refactoring with AI」第 24 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:24,搜「blind spots」)。 ↩ ↩2 -
出处:「Debugging and Refactoring with AI」第 27 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:27,搜「human judgment」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 53 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:53,搜「wall of red text」)与第 74 段(text/16-ch04-4-debugging-and-refactoring-with-ai.txt:74,搜「experienced developers」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 74 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:74,搜「talking to you」)。Key Terms 一节对 stack trace 的定义见第 1781 段(text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1781,搜「trail your program leaves」):「出错时你的程序留下的足迹。」 ↩ -
出处:「Debugging and Refactoring with AI」第 74 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:74,搜「crucial」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 87 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:87,搜「Syntax Errors」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 166 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:166,搜「walk me through」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 203 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:203,搜「trying to record」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 239 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:239,搜「root cause」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 815 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:815,搜「Context: I have」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 300 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:300,搜「Missing Colon」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 363 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:363,搜「never closed」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 396 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:396,搜「debugging intuition」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 249 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:249,搜「IndexError」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 262 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:262,搜「expenses.csv is empty」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 501 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:501,搜「apply a 20% discount」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 525 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:525,搜「intention gap」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 272 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:272,搜「Human Approach」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 688 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:688,搜「Reproduce and record」)。 ↩ ↩2 -
出处:「Debugging and Refactoring with AI」第 713 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:713,搜「underrated」)。原文:「Reproducing the bug reliably is the most underrated step. If it will not fail reliably, you cannot prove a fix.」 ↩ ↩2 -
出处:「Debugging and Refactoring with AI」第 47–48 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:764,搜「test_calc.py」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 800 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:800,搜「desired behavior」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 51–52 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:858,搜「guard clause prevents」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 882 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:882,搜「still fails」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 913 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:913,搜「Calculate the average of an iterable」)与第 970 段(text/16-ch04-4-debugging-and-refactoring-with-ai.txt:970,搜「raises ValueError」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 61–63 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:995,搜「normalize」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1072 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1072,搜「week of code studying」)。 ↩ ↩2 -
出处:「Debugging and Refactoring with AI」第 1081 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1081,搜「pytest black flake8 mypy」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1186 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1186,搜「Single function doing」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1247 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1247,搜「captured behavior」)。 ↩ ↩2 -
出处:「Debugging and Refactoring with AI」第 1381 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1381,搜「reduced duplication」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1399 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1399,搜「read_sales(file_path)」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1547 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1547,搜「Single Responsibility」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1566 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1566,搜「faith-based」)与第 1577 段(text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1577,搜「black .」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1591 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1591,搜「test_read_sales」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1640 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1640,搜「guardrails」)与第 1664 段(text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1664,搜「streaming」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1672 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1672,搜「Premature Generalization」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1689 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1689,搜「Show changes only」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 40–42 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:645,搜「Quick Setup Checklist」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 681 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:681,搜「safe rollback」)。原文:「You want a safe rollback if a suggested fix explodes.」 ↩ -
出处:「Debugging and Refactoring with AI」Hands-on Exercise(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1767,搜「KeyError: amounts」)。 ↩ -
出处:「Debugging and Refactoring with AI」Hands-on Exercise(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1773,搜「introduce strings」)。 ↩ -
出处:「Debugging and Refactoring with AI」Key Terms(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1781,搜「trail your program leaves」至:1802,搜「AI-Assisted Debugging」)。 ↩ -
出处:「Understanding Python Code with AI」第 1763 段(
text/14-ch02-2-understanding-python-code-with-ai.txt:1763,搜「experiment safely」)。原文:「The beauty of AI-assisted refactoring is not that it does the work for you, but that it lets you experiment safely.」 ↩ -
出处:「Debugging and Refactoring with AI」第 1699 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1699,搜「code-only answers」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 537 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:537,搜「grade_student」)与第 633 段(text/16-ch04-4-debugging-and-refactoring-with-ai.txt:633,搜「logging or raising a custom error」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1689 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1689,搜「Show changes only」)。 ↩ -
出处:「Building an AI-Powered Web Application」第 163 段(
text/21-ch09-9-building-an-ai-powered-web-application.txt:163,搜「explain what each file does」)。 ↩ -
出处:「Debugging and Refactoring with AI」结论第 1705 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1705,搜「trying to tell you」)。原文:「Debugging is not punishment; it is communication.」 ↩ ↩2 -
出处:「Debugging and Refactoring with AI」Points to Remember(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1724,搜「Refactoring is preventive debugging」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1011 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1011,搜「expecting [0,0,0]」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1027 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1027,搜「explain tradeoffs」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1063 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1063,搜「eps behavior」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 1576 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1576,搜「Run formatter and linters」至:1583,搜「mypy report.py」)。 ↩ -
变体是我们的推演;六步流程原文见「Debugging and Refactoring with AI」第 688 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:688,搜「Reproduce and record」)。 ↩ -
出处:「Debugging and Refactoring with AI」第 40–42 段(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:645,搜「Quick Setup Checklist」)。 ↩ -
(我们的三代问法归纳;问法演化的各代要素分别对应本章第 5、17、49 段的原文做法。) ↩
-
(我们的五行浓缩;各句原文见
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:87、:272、:713、:800、:1566。) ↩ -
(我们的分工澄清;第二章重构相关原文见
text/14-ch02-2-understanding-python-code-with-ai.txt:1484,本章流程原文见text/16-ch04-4-debugging-and-refactoring-with-ai.txt:688。) ↩ -
出处:「Debugging and Refactoring with AI」Points to Remember(
text/16-ch04-4-debugging-and-refactoring-with-ai.txt:1718,搜「AI can interpret」)。 ↩