本节摘要:一场 Boss 战的掉落物不会自动进包——复盘把它捡回家。本节讲复盘的四问结构、把重复错误转化为流程改进的方法,以及用一段可跑的指标脚本追踪自己的成长曲线。
Boss 战收官。前几节解决了"怎么打",本节解决"打完之后怎么变强"。没有复盘的战斗经验会蒸发:下次遇到同类问题,你只记得"见过",不记得"当时怎么赢的"。
战斗结束(问题解决)后,趁记忆还热,用四问过一遍,全程十五分钟:
四问的产物按去向归档:第一问进排错手册;第二问写给未来的自己;第三问写成规则,重复出现的规则升级为个人工作流;第四问进卡片库。每问都有明确去向,复盘才不会变成感想的堆放处。
单次复盘捡的是单件装备,更大的收益在纵向:把多次复盘放在一起看,找出重复出现的模式。当"假设没验证就信了"这条拖慢原因出现第三次,它就不再是粗心,而是流程缺陷——需要一条硬规则堵住,比如"战役日志里每个假设必须配一行对照实验,否则不许开下一个"。
这就是个人改进的层次:错误 → 记录 → 模式 → 规则 → 流程。跳过中间环节的改进(犯错后直接发誓"下次小心")没有信息支撑,必然复发。
用脚本让模式显形。下面这段程序统计各拖慢原因的出现频次与平均耗时损失:
from collections import Counter from statistics import mean # 复盘记录: (拖慢原因, 该战役浪费的小时) reviews = [ ("没读报错就动手", 1.5), ("假设没做对照验证", 3.0), ("知识缺口: 并发概念", 2.5), ("没读报错就动手", 0.8), ("假设没做对照验证", 2.0), ("环境差异没先对照", 4.0), ] freq = Counter(r[0] for r in reviews) loss = {} for reason, hours in reviews: loss.setdefault(reason, []).append(hours) print(f"{'拖慢原因':<12}{'次数':>4}{'平均损失小时':>10}") for reason, n in freq.most_common(): avg = mean(loss[reason]) print(f"{reason:<14}{n:>4}{avg:>10.1f}")
拖慢原因 次数 平均损失小时 没读报错就动手 2 1.2 假设没做对照验证 2 2.5 知识缺口: 并发概念 1 2.5 环境差异没先对照 1 4.0
按"平均损失乘次数"排序,改进优先级自动排好——上例中最该先堵的是"假设没做对照验证"(累计损失近五小时),它对应的规则(假设必配对照实验)就是本月唯一要重点执行的流程改动。一次只推一条规则,推稳再推下一条;一次铺十条规则等于没有规则。
学习编程最磨人的心理问题是"感觉没进步"。感觉不可靠,指标可靠。三个适合个人追踪的指标,采集成本都极低:
第三个指标的数据来源是项目日志,前两个直接从提交历史里数。坚持记录三个月,把这些数字画成趋势,"感觉没进步"的焦虑会被真实的曲线取代——即使曲线平缓,明确知道"平缓在哪"也远胜于模糊的自我怀疑。
背景:小何修好了报表脚本里"偶发数字翻倍"的故障,兴奋之余差点合上电脑继续下一件事——按四问复盘的习惯把她拦住了。
操作:四问各写了答案:症状是统计值偶发翻倍,根因是同一条记录被两个数据源各喂了一次;拖得最久的环节是"一直以为是计算错误,没怀疑数据源头",浪费三小时;下次的第一步规则是"结果异常先查输入有无重复,再查计算";知识缺口是数据合并时的去重键意识,记成卡片进排程。一周后她跑模式统计,发现"没怀疑输入侧"已是第二次出现,于是把那条规则升级进自己的调试工作流:任何"输出值不对"类问题,先打印输入条数做基数核对。
结果:一个月后同族问题(报表多算了一名退课学生)再出现时,她按新流程第一步就定位到输入侧,二十分钟结案——上一场花了三小时。
解读:注意复盘产物是怎么"复利"的:单次复盘给出一条规则,模式统计把规则升级成工作流,工作流在下次实战里兑现为二十比一百八十分钟的时间差。这条链路的起点只是十五分钟的四问——不捡,装备就永远躺在地上。
变式:团队场景的复盘形态是"故障复盘会",结构同样是对应四问的改写(时间线、根因、行动项、预防项),个人练熟的四问就是职场复盘的预演。
⚠️ 常见坑:复盘变成自我批评现场。四问里没有一问是"谁的错"——复盘的对象是流程与知识,不是人。情绪化的复盘坚持不了三次,能长期执行的复盘必须是工程式的:记录、统计、改流程、验证。
复盘的对象也包括"能跑但平庸"的代码。性能优化是第五阶段最常见的实战场景,姿势错了会白忙一场。三步走:
第一步,先测量再动手。感觉慢的地方常常不是真正的热点——程序员的直觉在性能问题上的命中率出了名的低。给可疑段落计个时:
import time t0 = time.perf_counter() result = total_of(rows, 1) # 被怀疑的段落 elapsed = time.perf_counter() - t0 print(f"耗时 {elapsed * 1000:.2f} 毫秒")
耗时 1.85 毫秒
两毫秒的段落不值得优化,无论它看起来多"笨"。测量的意义就是把有限的优化时间押在真热点上。
第二步,找出量级问题。热点段落里找循环的嵌套层数、循环内有没有重复计算(同一份数据每轮重算一遍是经典浪费)。5.2 节的两数之和改版、集合化改版,走的都是这一步。
第三步,改一处测一次。每次只动一个改动,跑同样的测量,对比数字。没变快的改动回滚——它增加了代码复杂度却没换来收益,是负资产。
三步的本质仍是控制变量与数据说话,与调试持久战同源。没有测量的优化不是工程,是占卜。