一个博客最开始只需要一个 Markdown 文件和一个静态托管。文章多起来以后,后台、图片、搜索、RSS、手记和评论逐渐加入,系统也从“把页面发出去”变成了一条需要长期照看的链路。
我现在会先把它画成几层。浏览器访问 Vercel 上的前端,前端通过公开 API 读取内容,API 由主机上的 Nginx 转给 MX Space,数据落在 PostgreSQL,缓存和搜索又有各自的更新时机。两台备用服务器站在旁边,平时不接流量,只有主机故障或维护窗口才参与切换。层次写清以后,看到 500 时不必立刻重启所有服务。
日常发文也应该沿着这条边界走。先在后台或 API 创建草稿,写完以后读回标题、正文、slug 和发布状态,再让首页、详情页、归档和 RSS 各读一次。直接改数据库虽然看起来快,却会跳过缓存失效、搜索索引和内容更新事件;数据库里的值正确,页面仍可能是旧的。
日期是另一种容易被忽略的字段。文章的创建时间决定归档顺序,手记还可能有独立的公开时间;只改 publicAt 不会修复列表里的同日堆叠。没有原始证据时,我宁愿把稿件留成私有,也不为了让时间线好看而假装它在某个日期发生过。
回滚要提前存在。每次批处理前保存原正文哈希、标题、摘要、日期和发布状态,写回后逐条读回;一条失败就按照相反顺序恢复已经尝试的条目。API 的 modifiedAt 可能无法回到旧值,但内容和公开状态必须能恢复。这样的回滚包比一句“数据库有备份”更接近实际。
RSS 和前端挑战页也不能只在出问题时看。RSS route 如果把可选的 aggregate 当成必选依赖,手记为空时就可能连文章订阅一起 500;前端若出现浏览器检查页,则要分别检查 Vercel、CDN 和源站,而不是把它归咎于文章内容。每一层都有自己的验收方法。
博客的维护很少有戏剧性的瞬间,更多是一次次确认:状态码、时间、哈希、日志、回滚目录。它们不适合写成热闹的教程,却能让几年后的自己在升级前知道应该先看哪里。页面只是最外面的一层,真正属于自己的,是这套可以重新解释和恢复的边界。
因此我把运维动作分成读、写和切换三类。读操作可以随时做,写操作必须有快照和逐条回读,切换操作则要有维护窗口和观察时间。分类以后,很多看似紧急的动作其实可以先用只读证据把范围缩小。