跳到主要内容
三个六块钱的订阅,让我搭进去两个周末

三个六块钱的订阅,让我搭进去两个周末

阿舟
阿舟

· 阅读约 6 分钟

8 月底我在手机银行里往上翻月付清单,翻到第 14 条的时候手停了。

14 笔订阅。最便宜的一个月 6 块,最贵的那个一年小一千。有几条我甚至想不起来是干嘛的——点进去看 logo 才反应过来,"哦,这个,我早就不用了"。

那次我没做断舍离。我只挑了三笔最便宜的,决定自己写。一个定时抓网页变化推通知,一个把 RSS 转成消息,一个每天固定时间拉个数落进表格。这种东西,我让实习生写也就五分钟,凭什么每个月给它交钱。

第一版确实很爽。快得不正常。核心逻辑就这么点:

def handler(event, context):
    for watch in load_watches():
        html = fetch(watch["url"])
        h = sha(html)
        if h != watch["last_hash"]:
            notify(watch["channel"], diff(watch["last_hash"], h))
            save_hash(watch["id"], h)

二十来行,部署上去,跑通了。那天晚上我还有点小得意——一个订阅费被我干掉了。三个加起来一年省两百多。我在备忘录里写了句"以后这类工具先想想能不能自建",还特意没删。

然后这件事就从我的注意力里消失了。这才是关键。

三个月后它第一次坏,是我收到一封"某页面更新"的通知,点进去发现内容跟三天前一模一样。排查花了我四十分钟,因为我已经忘了它是怎么跑的了。翻函数日志,两行时间戳一模一样:

[2026-06-14 09:13:02] notify watch=42 hash=8f3a...
[2026-06-14 09:13:02] notify watch=42 hash=8f3a...

同一个 watch,同一秒,两条通知。

原因不复杂。定时任务那次跑超时了,平台按它的规矩把这次调用重跑了一遍,而重跑的时候前一次的写 hash 还没落库,于是两个实例都读到旧 hash,都判定"变了",都发了。经典的幂等缺口。我知道该怎么做,加个锁,或者加个幂等键,就完事了。

但我盯着那两行日志的时候,脑子里冒出来的不是"加个锁",是"我本来不用管这个"。原来那个付费工具,是别人替我把这把锁加好了的——加了三年,还顺便处理了时区、处理了对方站点 502 时候的退避、处理了我某天手滑把定时任务开了两个实例的情况。

这段我没截图,没什么好截的。就是坐那儿愣了会儿,然后开始改。

def handler(event, context):
    for watch in load_watches():
        if not claim_lock(watch["id"], ttl=120):
            continue          # 上一轮还没走完,别插队
        try:
            html = fetch(watch["url"], timeout=8)
        except Timeout:
            bump_backoff(watch["id"])
            continue
        h = sha(html)
        if h == watch["last_hash"]:
            continue
        emit_once(watch["id"], h, diff(watch["last_hash"], h))  # 幂等键 = watch_id + h
        save_hash(watch["id"], h)

看着也没多少行对吧。这个版本花了两个晚上、修了三次才稳下来。

中间还夹着我一次判断错误。我一开始把锁的 ttl 设成 30 秒,觉得够用了,结果有个抓取特别慢的站点直接把锁等过期了,又绕回重复通知。改到 120 秒之后,退避逻辑又跟锁的过期时间打架……这里我纠结了很久要不要干脆回头用那个付费服务。最后还是没回头,但那一晚我承认了一件事:我上面那个"二十分钟能搞定"的判断是错的,而且错得挺离谱。

真正让我想写这篇的,是后来在 DEV 上刷到的一篇文章。作者叫 Alexandra,标题大意是说 AI 精神病这现象,一部分得算到订阅疲劳头上。这个因果链我倒没那么买账。但文章里有一段我完全同意,而且比我上面这段经历说得清楚:AI 降的不是软件的价值,它降的是某一类软件的价值,就是那种"一层薄界面 + 一堆容易复制的功能"的东西。她拿 Netflix 和 Amazon 举例,说界面本身谁都能仿,值钱的是库里的片子和背后的仓库、物流、支付、客服。

她文章底下的评论里有一条,我看了半天,觉得是我这次踩坑的总结陈词。一个做自动化工具的开发者说,他那边客户付钱买的从来不是 AI 几分钟能写出来的"触发—动作"逻辑,买的是已经调通的认证握手、速率限制、还有五百多个 API 的各种边缘情况。

对,就是这个。

我付的月费,买的不是那二十行 if hash != last_hash。买的是有人替我把幂等、退避、锁、时区、对方站点的脾气都盘过了,而且我懒得看的时候他可以继续盘下去。

那"我自己能写"是不是一句伪命题?也不完全。

这里得自我推翻一下上面那段的语气——我有个自建的东西用了快三年,一行没改过。就是每天早上八点把某个 API 的返回值追一行进 Google Sheet。它一辈子不会变,因为那个 API 就是这么简单,坏了我十分钟能修。这种自建是对的。

区别在哪,我现在的判断是:看你这个功能一年会被现实碰几次。不是看它代码多少行,是看它依赖的外部世界有多少个会变动、会出错、会有人为它作妖的地方。一个只读固定接口的定时任务,外部变量是一;一个要抓网页、要发通知、要处理对方限流的东西,外部变量是一打——而每一个变量都在偷偷给你记维护账。

AI 让"做出第一版"这件事变得极其便宜,这个没得说。但它几乎没让"让第一版活过一年"变便宜,因为后者要花的不是打字的时间,是知道的功夫。这两件事以前被绑在一起,所以大家默认"能写第一版的人就能养它"。现在它们被拆开了,于是大量第一版被生产出来,然后烂在原地。

那个评论区还有一句话,是个软件工程师说的,大意是"代码从来不是重点,理解业务和用户才是"。我以前看到这种话会觉得有点装——我不写代码谁写。现在……怎么说呢,我现在觉得他说得对,但它不是我以前理解的那个意思。它的意思不是说代码不重要,是说代码是那个被生成得最快、最不值钱的部分,而剩下那些没被生成出来的部分,才是你真正花钱买、或者花时间养的东西。

我没有把那个付费工具接回来。三个自建里我留了两个,第三个在两个月前被我删掉了——它的维护成本已经超过它省下的钱。我承认了这件事,删的时候一点不伤感。

所以这事到现在也没有一个漂亮结论。我不能站在这里告诉你"自建永远是坑"或者"该自建就自建"。我能给的只有我这一年摸出来的一条不太性感的规矩:下次我想砍一笔订阅、决定自己写的时候,先问一句——这个东西依赖的外部世界,一年会变几次。答案超过三次的,老实交月费,别拿自己的周末去填。

阿舟
阿舟

写代码写到一半开始怀疑人生,靠 AI 工具续命,顺手把踩过的坑都记下来。

查看主页 →