打卡不等于达标

2026-08-20思考
我给自己写了一套 21 天习惯追踪。真正花时间的不是代码,是决定"什么才算数"。

给自己写一个习惯打卡系统,代码是最简单的部分。难的是定义口径—— 而口径决定了这套系统会不会对你说谎。

以"每天静坐 60 分钟,连续 21 天"为例,下面每一条都是我实际纠结过、并且改变了实现的决定。

一、打卡 ≠ 达标

如果今天只坐了 30 分钟,我照样记录,但这一天不计入连续

很多习惯 App 在这里偷懒:点一下按钮就算完成,数量是装饰。 结果是连续天数越来越长,而实际执行强度越来越低。你在给自己发奖章,不是在测量自己。

记录和达标必须是两件事:记录是为了留下真相,达标是为了兑现承诺。 把两者合并,系统就开始替你美化现实。

二、"今天还没打卡"不等于"断了"

如果我在下午 3 点打开页面,今天还没坐,连续天数应该显示什么?

显示 0 是错的——今天还没过完,我晚上还能补。 直接算达标更错——那是凭空送我一天。

正确的答案是第三种:从昨天往回数,并且明确标出"今天不做就归零"。 状态不是二元的,是三态:今天没记录 / 记录了但没达标 / 今天已达标。 少了任何一态,页面就会在某种情况下说谎。

三、写失败的时候,绝对不能猜

打卡走的是消息记录。如果落盘时抛异常,系统该回复什么?

最自然的写法是"记录失败,请重发"。这句话很可能是假的: 写入抛错的时候,字节可能已经部分甚至全部落盘了。用户照做重发,就变成重复计数。

所以正确的做法是回读确认:出错后重新打开文件,按完整的校验规则查这条记录到底在不在, 返回三种结果之一——已记录 / 确未记录 / 结果不确定

第三种最重要。"我不知道"是一个合法的答案,而且比猜一个好听的答案诚实得多。 对应的提示也不该是"请重发",而是"先查状态,少了再补"。

四、关键词识别宁可漏判,不可误判

打卡靠发消息触发。"静坐60" 应该被记录,但 "帮我读一下这份材料" 绝不能被误认成阅读打卡。

我的解法是不对称的:专有词(静坐、站桩)带数字就触发; 而"阅读""读"这种日常高频词,必须带上明确的打卡标记才触发。 另外,识别完所有片段后,剩下的文字必须全是标点和空白—— 只要还剩有实际含义的词,整条就不算打卡。

宁可漏判(我重发一次即可),不可误判(一条正经请求被吞掉,而且我不知道)。 两种错误的代价完全不对称,设计就该向代价小的那边倾斜。

五、目标本身可能不现实,但这不是系统该替你决定的

我设的目标是连续 21 天,而我此前的历史最好成绩是 6 天。 这是一个 3.5 倍的跳跃,失败概率不低。

系统不该替我把目标改软,但必须把基线摆在旁边:页面上同时显示"当前连续"和"历史最佳"。 不劝我,也不哄我,只是让我在看到 D3 的时候,同时看到 6 这个数字。

收束

一套自我度量系统的价值,完全建立在"它说的是真的"之上。 一旦它为了让你好受而模糊了一次口径,你对它全部历史数据的信任都会崩塌—— 而那些数据本来是你最该相信的东西。

宁可让它显得苛刻,也不能让它显得体贴。