CASE STUDY
2025.08 起步 → 2026.04–05 修订 · 个人项目 · AI 协作Started Aug 2025 → revised Apr–May 2026 · Personal project · AI-assisted
最早的备忘录:读不出的数据,为什么不能当成空列表?
本地提交历史里,Note 最早的 Flask 备忘录版本可以追到 2025 年 8 月 7 日。到 2026 年春天,它增加了标签、搜索、置顶和备份。我借助 AI 编程工具继续修改,也开始接触那些界面不容易显示出来的问题。
这篇把早期项目和后来的修订放在一起。故障与修复主要来自 2026 年 4 至 5 月,不能把它们都记到项目第一天。
读失败后返回空列表,下一次保存会发生什么
原来的读取逻辑遇到 JSON 损坏时返回 []。单看这一步,页面还能打开;再接上“写入前先备份”,后果就变了:应用可能把空状态继续保存,并覆盖原本的备份。
修复让读取失败先尝试备份;没有可用恢复数据时抛出明确错误,不再把损坏状态当成没有笔记。保存入口还增加列表类型检查。
这个修改保留了可供人工处理的失败,但不能推出完整的数据安全保证。当前备份与加锁顺序、整个读改写过程的并发保护,还需要单独验证。本文没有把一次修复写成所有掉电和并发情况都已覆盖。
两个排序条件,不能一起反转
另一个问题更小,却容易直接看到:想让置顶在前、同组按时间倒序,使用元组排序后再设置 reverse=True,连置顶维度也一起反转了。
修复拆成两次稳定排序,先排时间,再把置顶项放前。测试也分别检查置顶优先和同组新旧顺序。这样能够指出究竟是哪一个要求没有满足。
自动测试不该碰到自己的笔记
应用使用本地 JSON,测试如果沿用默认路径,也可能改到真实笔记或备份。现有测试 fixture 因此创建临时目录,替换数据、备份、锁和密钥文件的路径。
这让我开始把测试环境当成需要设计的部分。看到命令通过以后,还要问它读写了哪里、是否真正走过失败路径,以及是否留下运行产物。加入 CI 后的格式和静态检查修复,也都有单独提交,没有把“写了配置”当成已经跑通。
本轮只核对数据恢复修复、固定版本实现和测试,没有打开或发布真实笔记,也没有重新运行破坏性恢复实验。若继续维护,应先在隔离目录覆盖损坏、缺失和并发场景,再决定文件存储是否仍适合使用规模。
The early memo app: unreadable data must not become an empty notebook
The local history traces Note’s first Flask memo app to August 7, 2025. Tags, search, pinning, and backups followed in spring 2026. I continued with AI coding tools and began encountering failures that the interface did not expose clearly.
This article connects that early project to later revisions. The fixes below belong mainly to April–May 2026, not its first day.
Follow a failed read into the next save
The old loader returned [] when JSON could not be read. The page could still open, but the next save’s backup step could overwrite a recovery opportunity while persisting the empty state.
The fix tries the backup after a failed read and raises an explicit error when no recovery data is usable. Saving also checks that its input is a list.
This preserves an inspectable failure. It does not establish complete data safety: backup ordering, lock scope, and concurrent read-modify-write behavior still need separate checks.
Keep sorting requirements independent
A second bug combined pinned-first and newest-first in a tuple sort, then reversed both dimensions. Pinned items could end up behind other notes.
Two stable sorts now order by time first and then pin status. Tests check those requirements separately, making failures easier to interpret.
Keep tests away from real notes
With local JSON storage, default test paths could touch personal notes and backups. The existing fixture allocates a temporary directory and replaces data, backup, lock, and secret-file paths.
This made test-environment design part of the work: which files did the command touch, which failure path did it exercise, and what did it leave behind? CI setup also needed follow-up formatting and static-check fixes rather than merely a configuration file.
I reviewed the recovery fix, fixed-version implementation, and tests without opening or publishing real notes or rerunning recovery experiments. Further maintenance should first cover corruption, missing files, and concurrency in isolation before deciding whether file storage still fits the intended scale.