← 揭开黑盒:理解系统的调查课堂

第 04 课:什么时候值得打开源码?

本课问题:哪一层内部证据,能解决我现在还分不清的解释?
前置:已有行为记录;遇到语法时按需查工具箱。
本课交付:一张带证据位置的数据流图,以及明确的停止深挖理由。预计 35–55 分钟。

带着一个未解决的问题进去

不要为了“把所有代码都读完”打开文件。先从记录本选一个问题,例如:“C 和 D 在服务重启后为什么不同?”或者“B 保存时,文字有没有离开浏览器?”

源码是一种强有力的证据,但你仍要核对:读的是不是当前运行版本?实际调用是否走到了这条路径?依赖和配置会不会改变行为?

实验页面的“打开恰好需要的一层”能查看当前服务提供的 app.js、lab.py、storage.py。版本号和服务指纹帮助记录条件,不能当作整个系统完整性或身份的密码学证明。

第一层:先从可观察的请求开始

按 F12,打开 Network。清空列表后,只在一个样本保存一条独特文字,观察是否出现与这次保存有关的 POST。

页面初始化和“再读一次”也会发请求,不能看见任何一个请求就断言所有样本都在上传数据。要按时间、路径和请求正文,找到与你刚才操作匹配的那条。

在本实验里,C/D 的保存会出现 /api/notes/实验编号/c 或 /d。查看请求正文中的 text 和 operation;再看响应状态与正文。需要更多 HTTP 背景,可按需查 代码课堂第 02 课。

“没看到请求”目前只说明这次观察没有发现相应请求。开发者工具过滤、已有连接或其他通道可能造成遗漏;接下来读实现,和行为相互核对。

第二层:追踪这个 text

打开 app.js,搜索 saveNote,只追踪当前样本分支:

样本在这个实验版本中找到的动作与此前观察的联系
A给 state.memory 赋新列表新页面重建对象,旧页面状态结束
BlocalStorage.setItem 写入特定键同浏览器同来源共享,独立环境不共享
C/Dawait api 发起 POST数据跨过页面与服务的边界

变量名和函数名是路标。初次阅读只需标出输入、分支和保存动作,不必先掌握整个语言。函数与返回值可查 Python 工具箱第 04 课,文件与对象的区别可查 第 05 课。JS 的 localStorage 可参考 代码课堂第 03 课。

第三层:服务收到之后又做了什么

打开 lab.py,搜索 do_POST。找到请求正文解析、store.save 调用以及之后发送响应的位置。再打开 storage.py,搜索 Store.save。

对于 C,它把记录加到 self.memory 的列表里。对于 D,它通过参数化 INSERT 写入 SQLite,并在事务上下文正常退出、提交之后返回。

把同一条文字连起来:

输入框的 text
  → app.js 的请求正文
  → lab.py 的 do_POST
  → storage.py 的 Store.save
  → D 的事务提交
  → 服务响应
  → 页面显示确认,再读回列表

源码支持了本实验的具体解释,也说明了“服务回复”和“页面显示”之间还有边界。下一课会故意让这两个边界产生不同结果。

用一次小变化验证阅读理解

提出一个预测:“如果 D 已提交,再创建一个读取相同数据目录的服务实例,应该仍可读取。”对照第 03 课的重启记录,检查它是否吻合。

另一个预测:“如果只清除 B 对应的存储键,C/D 不应因此消失。”这是可选实验,务必先导出调查报告。不要随手清除整个站点数据,那会同时改变记录本等额外条件。

没有做的实验应标成“待验证”,不能因为代码看起来如此就填写成实际观察。

验收与停止条件

交付一张不超过七个节点的数据流图,每个关键箭头附上函数名、请求或观察证据。说明哪条假设得到加强,哪条被排除。

然后写清本次停止的位置。例如:“当前只要求服务正常重启后读回,本轮行为与事务代码相符;暂不继续追到 SQLite 的磁盘同步实现。如果用途变为重要长期记录,还需验证备份、异常掉电与恢复。”

能够有理由地停止,也是理解抽象边界的一部分。