Files.md架构决策记录ADRs精读:本地优先笔记应用中30多个决策背后的完整思路
【免费下载链接】files.md🌱 Private, quiet space for thinking. Simple app for .md files.项目地址: https://gitcode.com/GitHub_Trending/fi/files.md
Files.md 是一款以纯.md文件为唯一数据格式的本地优先笔记应用:笔记、日记、任务、清单全部以纯文本形式保存在你的设备上,无需安装、完全离线可用。这个开源项目在 README.md 中维护了一段ADRs(Architecture Decision Records,架构决策记录),用日期、理由甚至事后修正,记录了 40 多个关键决策。本文按主题精读这些记录,梳理出每个决策背后的完整思路,不需要看代码也能读懂 📌
ADR 是一种轻量文档实践:每个重要架构选择写一条,记录"决定了什么、为什么、代价是什么"。读一个项目的 ADR,往往比读它的代码更有价值——你看到的是作者真实的决策过程。
ADR速览地图:40多个决策一图看懂
| 主题 | 代表决策(日期) | 核心动机 |
|---|---|---|
| 数据格式 | 放弃 wikilinks,回归标准 Markdown 链接(11.04.2026) | 知识库要跨平台,在任何 Markdown 环境都能打开 |
| 同步 | 内容同步改用 mtime(08.07.2025) | 比 ctime 更可靠,且能从 git 存档恢复 |
| 本地存储 | 移除 WASM(21.09.2025) | 组件与不确定性太多,单个 wasm 还约 8MB |
| 交互流程 | 合并 Inbox 与 Today(02.05.2026) | 减少概念数量,让默认流程尽量简单 |
| 性能 | Mermaid 懒加载(22.05.2026) | 3MB 对一个小应用来说太重 |
| 并发 | 每用户串行处理更新(26.10.2024) | 消除并发写文件带来的竞态条件 |
原始记录全部在 README.md 的ADRs小节,格式是"日期 + 一句话决策 + 理由",部分条目还带Added later/PATCHED事后补丁——记录是活的文档,不是化石。
数据可移植性:为什么一切都是纯 .md 文件
这是 Files.md 所有决策的地基,README 里写得很直白:以可移植性为先,一切存储在纯.md文件里。围绕这条底线,有多个 ADR:
- 限制文件名(16.06.2025):禁止
: ? < > *等字符,让文件在 Windows、PWA 等任何环境下都合法; - 根路径是
/(08.07.2025):文件用路径唯一定位,且只支持一层目录嵌套——整个知识库可以用一句话讲清; - 链接语法走了三次弯路:
- 最初支持 wikilinks
[[...]]; - 21.09.2025 换成了极简
[link]语法,因为完整路径"太笨重、太碍眼"; - 11.04.2026 又回到标准 Markdown 链接,理由是知识库必须跨平台,在任何 Markdown 环境都能读;
- 最初支持 wikilinks
- 自动写入回链(20.06.2026):当你链接某篇笔记时,反向链接立刻写进目标文件,而不是渲染时动态计算——这样在任何查看器里回链都真实存在。
目录约定可参考 web/AGENTS.md:一个文件一个想法,文件名即标题,只有一层嵌套。
把简单当功能:一组"做减法"的决策
Files.md 的贡献原则是:理想情况下每个 PR 都应当删除或简化代码,而不是增加。ADRs 里几乎全是这类"减法":
- 放弃 AST 解析(08.07.2024):AST 有太多边角情况、代码复杂得多,改用直白解析后代码量少了 3 倍,理解起来轻松得多。给 Telegram 发的 MD→HTML 转换也是手写的;
- 全部依赖 vendor 进仓库(09.09.2024):依赖很少,全部放进仓库自给自足,不再担心上游删除或封锁包;
- 移除 fyne 桌面框架(06.10.2024):实现了 80% 的 bot 功能后,剩下滚动、emoji 渲染、链接选择等细节需要巨大投入,不如回到 web 技术栈;
- 统一术语(09.07.2024):"note" 太含糊,统一改叫 "file",少一层抽象;
- 更严格的格式(13.07.2024):采用 gofumpt,减少格式选择;
- 并发也做减法:用细粒度锁替代每用户一把全局锁;userconfig 每次访问都从磁盘重新读取,避免网络延迟后写回陈旧数据(20.08.2024)。
这些决策指向同一句话:整个项目要能装进一个人的大脑里,低认知负荷本身就是产品特性。
同步机制:最"技术流"的一批决策
同步是 ADRs 里"为什么"写得最多的一条线,理解它只需要三个关键概念:
- 内容同步用 mtime(08.07.2025):Dropbox 会改动新建文件的元数据,导致 ctime 不可靠;mtime 不受改名、权限变化影响,还能从 git 存档恢复。ctime 只保留给 append-only 改名日志;
- 微秒级时间戳(24.06.2025):两次连续写文件的时间差足以区分先后;不选纳秒是因为 JavaScript 对 int64 精度不友好;
- 无状态内容同步 + 追加日志(04.06.2025):纯内容同步时服务器不存任何状态,只比对 hash 和最后 mtime;改名/删除写入追加日志(fslog),同步响应里会告诉其他客户端"这些文件在 T 时刻被删了"——否则别的设备会把已删除的文件重新上传,让文件"复活"。
后来还补了一条每用户串行处理更新(26.10.2024):同一用户的消息严格按顺序处理,从根上消除并发写竞态。完整请求流程见 docs/sync-flow.md,服务端代码在 server/sync/。
本地优先:从 WASM 到 OPFS 的浏览器存储抉择
Files.md 是一个免安装的 PWA,"文件存在浏览器哪里"有两个关键决策:
- 移除 WASM(21.09.2025):作者最初(14.06.2025)把 bot 逻辑用 Go 编成 WASM 在浏览器里复用,确实能用,但一个难复现的 bug 暴露了 JS 与 Go 之间冗长易错的调用链,加上 ~8MB 的体积,最终决定用 JS 重写、整套复杂度直接删掉;
- 默认 OPFS(11.07.2025):浏览器支持更好、用户折腾更少;需要时再切换为打开本地文件夹(File System API),文件夹句柄存入 IndexedDB 复用。
再叠加"没有构建系统"的约定——web/index.html 就是入口,打开即用——本地优先的目标非常清晰:10 年后这个应用依然能直接打开。
聊天流:从 Inbox 到 Chat.md 的概念减法
最"用户驱动"的一组决策都围绕那个聊天窗口:
- Telegram bot 是无干扰的只写入口(12.06.2025):随时丢一句话进去,不切上下文;
- 默认流程是"一个大文件"(27.06.2025、29.06.2025):所有消息先追加到同一个文件,不强制立即归类——"真正简单、好理解的默认流程";
- 目录按需创建(26.06.2025):不预建所有文件夹,避免知识库一上来就被塞满;
- Inbox 与 Today 合并(02.05.2026):"Inbox" 名字太抽象、太效率工具味,作者要的是平静与简单;
- Today 又改名为 Chat(06.05.2026):用户访谈发现"today"这个概念难以把握,而"打开聊天"在 bot 和网页应用里含义一致;
- 列表项用稳定内容 hash 定位(22.04.2026):按钮指向内容 hash 而不是行号,中间增删、勾选条目也不会指错行;
- 砍掉"to inbox / to chat"两个按钮(22.04.2026):作者发现多出来的那一次点击,就足以让人讨厌新增任务——删掉概念,一切直达收件箱。
规律很清晰:凡是让用户犹豫的概念,概念本身就是问题,删掉它。
Telegram 机器人入口:最"功能化"的 ADRs
bot 是这个项目的前门,代码在 server/bot.go,架构见 docs/bot.md。相关决策包括:
- 先转义 HTML 再把 Markdown 转 HTML(13.06.2024):用户笔记可能包含非法 Markdown,直接发 Telegram API 会失败,于是手写了一个小转换器;
- 按字素簇处理 Unicode(07.07.2024):Go 里字符串是字节序列,像"⚪"这样的字符其实是两个 rune,引入 uniseg 按"用户感知到的字符"切分;
- 用户输入一律哈希化(13.06.2023):Telegram 按钮的 callbackData 上限 64 字节,长文件名会直接被拒。
bot 主界面的功能组织方式,可以感受一下它的功能广度:
渲染与媒体:成本可解释的功能取舍
- 加入 LaTeX(20.05.2026):作者虽不情愿新增 20 个字体文件,但判断"值得"——数学是纯文本、对 LLM 友好,文本 + 公式几乎覆盖一切表达;
- 加入 Mermaid 并懒加载(22.05.2026):mermaid.min.js 有 3MB,同步加载对一个小应用太重,脚本按需加载;
- 视口内即时展开全部内容(24.05.2026):图片、公式、图表立即渲染,无闪烁、无性能代价;
- 支持音频视频(01.06.2026):作者相信这类媒体能让日记服务于"情绪疗愈"。
套路同样是:新功能必须有可解释的成本、可对齐的价值。
从这40多个架构决策里能学到什么
- 被用户的困惑推着改——Today 改名 Chat 直接来自访谈反馈;
- 跨平台是底线——文件名限制、链接语法,全部为"任何环境都能打开"让路;
- 简单即功能——做减法的决策(AST、WASM、fyne)远多于加功能的决策;
- 记录"为什么",并允许事后补丁——带
PATCHED标记的条目说明记录是活的,随项目继续演化; - 量化成本——3MB、8MB、64 字节回调上限,作者对数字非常诚实。
想继续深入,建议按顺序阅读:docs/sync-flow.md(同步流程)、docs/bot.md(bot 架构)、docs/your-own-server.md(自建同步服务器),最后带着上下文回头重读 README.md 的ADRs小节。
40 多个决策,指向同一个方向:让文件比软件活得久,让软件简单到你能永远拥有它。💡
【免费下载链接】files.md🌱 Private, quiet space for thinking. Simple app for .md files.项目地址: https://gitcode.com/GitHub_Trending/fi/files.md
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考