第34天平台期:用自动化脚本和数据闭环撑过长期项目

📅 发布时间:2026/10/1 3:30:55
第34天平台期:用自动化脚本和数据闭环撑过长期项目
1. 第34天不是里程碑而是最容易放弃的高危平台期今天这篇没有项目代号标题就一个词day 34。熟悉我博客节奏的朋友应该猜到了这是我自己定下的一个100天长期计划——每天固定时间写作、写代码、做复盘并把整个过程用一套自动化脚本记录下来。第34天听起来既不是开头也不是结尾但它恰恰是我认为整个周期里最危险的一个节点。很多人喜欢引用21天养成习惯的说法但以我自己跑了三年个人项目的经验来看21天只能解决开始的问题真正决定项目能不能做完的是第五周前后这个平台期。到第34天你已经没了第一周的兴奋感也不会像第二周那样靠立flag的羞耻心硬撑同时你还没看到足以证明一切的成果。卡在这个位置的人通常会用一个特别体面的理由退场最近太忙了先停几天后面再补。可这一停基本就停到项目结束了。我之所以专门为day 34写这么一篇是因为这周我确实有好几天早上打开电脑时脑子里闪过了要不今天就算了的念头。我把前33天的节奏盘了一遍把项目的自动化记录脚本又加固了一轮最后没断更还顺手修掉了一个隐藏了很久的时区问题。这篇就聊聊这个节点上我具体做了什么以及为什么我说第34天才是项目真正的分水岭。1.1 前33天走过的三条曲线如果你也在跑类似的长期计划我想先给你一个参照系任何持续性的个人项目前33天都会同时走过三条方向完全不同的曲线。第一条是兴奋曲线。第1到第10天你每天都在接触新东西哪怕只写十行代码都觉得我今天进步了。这种情绪是天然的燃料但也是不可持续的燃料。到第三周同样的任务变得无聊你开始意识到大部分工作都是重复劳动。第二条是能力曲线。前30天确实是能力爬升最快的阶段尤其是如果项目涉及你不熟悉的工具链你会发现自己每天都能学会几个命令、踩掉几个坑。但能力爬升会带来一个副作用你能做的事变多了你给自己定的每日标准也跟着水涨船高。第三条是期待曲线。第一周期待很朴素——我只要坚持就行。到了第34天期待变成了我要看到成绩、看到成品、看到别人认可。这三条曲线一交叉就形成了所谓的高危平台期能力在涨、兴致在降、眼光在变高。不把这个状态点破你很容易把项目本身有问题错判成我不适合做这个。我自己的解决方式很土在第34天做一次项目体检把项目从热情驱动切换成系统驱动。说白了一句话——坚持不靠意志力靠的是你给自己设计的规则。1.2 我在第34天做的项目体检清单这次体检我列了四个问题每一个都值得单独写进当天的记录里如果这个项目今天就停在这里之前33天做的哪些事情是白费的有没有一项每天都花时间、但从来没被认真审视过的重复动作如果让现在的我回到第1天我会改变哪条初始设定有哪些环节仍然高度依赖手工操作一旦状态不好就会断档这四个问题的价值在于它们会把我今天不想做这种情绪翻译成可排查的系统问题。比如我当时的答案是前33天里最浪费时间的动作不是写代码而是每晚打开好几个工具去汇总当天的完成情况。状态好时这只要十分钟状态不好时我会坐在那里刷半天手机。这件事很自然地指向了我这周要做的一个技术改动——给日报自动化脚本补一套状态流转逻辑也正是下一篇要展开的部分。2. 这周的正事给自动化日报脚本补齐状态流转逻辑项目进行到这个阶段我手头主要在做一个小工具名字很直白叫dayslog。它做的事情用一句话概括每天定时从几个数据源拉取我的活动数据——代码提交次数、本地文件变更、日历上的事项完成情况然后汇总成一条结构化记录写入本地数据库。每周日再自动生成一份周报告诉我这周的有效投入时间、产出节奏和中断次数。前33天里这个脚本的运行逻辑一直很简单每晚10点cron触发一次Python脚本把当天数据insert进SQLite。听起来够用了但它有两个很致命的问题第一如果当天cron没跑那一天的数据就永远丢了第二如果我在第二天早上想补充修改前一天的数据没有人能看出这条记录到底是当天正常完成还是事后补的。你可能会说这有什么区别区别大了。数据一旦进入复盘系统就必须保留它的可信度。我第34天的工作就是给每条记录加一个完整的状态机让没跑和没做完都成为一等公民而不是静默消失的异常。2.1 为什么需要状态字段而不是一条INSERT到底在设计这个状态机之前我花了半小时认真想一个问题每天的记录到底有几种可能的存在状态实际跑下来无非是这几种计划里安排了但还没开始进行到一半被中断正常完成cron漏跑导致缺失以及事后手动补齐。如果你只用一张表存完成内容这些状态天然就在信息损失。比如你看到周一有一条记录内容是空的你没法判断是周一没工作还是脚本坏了没采集到。这对周报的统计影响很大——我后来看数据时发现有三天的空窗其实都是cron环境问题而不是我真的没干活。所以我给表结构加了一个status字段取值如下状态值含义进入条件planned今日计划已写入当天早上脚本初始化running正在进行中连续工作时间超过15分钟done正常完成当日收尾主动标记missed缺失cron漏跑或手动标记失败patched事后补齐次日修改并写入上一日记录这套设计真正带来的收益不在状态本身而在它逼着我去思考每一条记录是谁、在什么条件下、以什么形式写入的。一旦把写权限和状态分离后面做统计、出周报、画趋势图才有底气。数据源可以粗糙但数据的血缘关系必须清楚这是我在监控系统里学到的经验。2.2 状态流转的触发点和幂等处理实现状态流转时最容易被忽略的是同一份数据被重复触发的问题。cron每晚10点触发一次但如果我当天熬夜到11点还没收尾而脚本11点又被我手动跑了一次就有两条running记录。我一开始的做法是直接再insert一条结果数据库里出现了同一天的多条记录周报的统计全乱了。修复方法是给(date, source)加唯一约束同时用一条UPSERT语句把状态更新和写入合并起来INSERT INTO daily_log(date, source, status, payload, updated_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(date, source) DO UPDATE SET status excluded.status, payload excluded.payload, updated_at CURRENT_TIMESTAMP;Python侧只保留业务层判断当脚本启动时会先查询status如果是done或patched就不再写入而是把本次运行记录进一个单独的执行日志。这套逻辑跑了一周我再也没出现过同一天重复记账的脏数据。这里有个小教训顺便说一句写自动化工具别一上来就追求智能先把幂等性做对。幂等的意思是同一个操作不管执行一次还是两次最终数据都一致。你只要保证任何一步重跑都不会把数据搞乱后面的维护成本会直线下降。3. 实测踩坑cron 与 Python 对今天的理解各说各话我是在第32天发现这个问题的——周报里出现了周四和周五两天的记录时间都比实际晚8小时周五的报告里还出现了2025-06-06 23:00这种明显属于周六凌晨的时间点。第一反应当然是脚本有bug但手动跑了一遍脚本写入的时间戳又是对的。这种手动正常、定时任务出错的现象几乎可以断定是环境差异而不是代码逻辑问题。3.1 现象描述手跑正常cron跑出来差8小时先说环境我的脚本跑在一台廉价的海外VPS上系统默认时区是UTC日常使用全靠应用层手动指定Asia/Shanghai。手跑那次Shell里已经通过环境变量把时区设置好了所以datetime.now()返回的是东八区时间。但cron启动的进程是全新的环境不会继承我手动export的变量。于是Python拿到的是UTC时间而我在周报SQL里又只用了strftime(%Y-%m-%d)截取日期这就导致所有记录的实际日期都被往前挪了一天。还有一个更隐蔽的坑我用了数据库的CURRENT_TIMESTAMP作为写入时间SQLite这个函数返回UTC时间和Python侧生成的本地时间戳混在一起日期的错乱程度会加倍。我一开始以为是cron漏跑后来一查数据库发现记录都在只是日期错位了。这种错位比丢失更坑因为它会悄悄污染你的周报统计不仔细看根本发现不了。3.2 完整排查链路从crontab到进程环境变量这个问题的排查过程比较典型我完整记录下来方便你以后遇到类似问题直接按这个顺序走。第一步先确认cron到底有没有执行。我看了一眼cron的执行日志确认任务确实触发了脚本也没有报错说明问题出在执行了但结果不正确这一类。第二步在cron任务里加一行输出环境变量的命令把进程的TZ和日期打出来* 22 * * * /usr/bin/env | grep -E TZ|LANG /tmp/cron_env.log date /tmp/cron_env.log跑了一天后看日志date输出的是UTC时间而我自己在Shell里执行date得到的是东八区时间。到这里基本锁定了cron进程没有继承用户Shell里的环境变量。第三步验证Python获取时间的方式。在脚本里临时加了一行print(datetime.now().astimezone().tzinfo)跑下来输出的是UTC进一步确认了根因。修复方案不复杂我做了两处调整第一在crontab文件顶部显式声明时区CRON_TZAsia/Shanghai这样整个cron进程都会按东八区跑第二把脚本里所有拿到时间戳的地方改成统一走一个helper函数内部用zoneinfo.ZoneInfo(Asia/Shanghai)明确指定时区彻底不依赖系统默认值。from datetime import datetime from zoneinfo import ZoneInfo TZ ZoneInfo(Asia/Shanghai) def now_local() - datetime: return datetime.now(TZ)改完之后我把数据库中已错位的记录重新校正了一次然后连续观察了一周周报里的日期全部恢复正常。这个问题技术上不复杂但如果不是手动对比cron和交互Shell的差异我可能还会在数据缺失这个错误方向上查很久。3.3 修完之后的稳定性收益这个bug的修复表面看只是时区问题但它带来的连锁收益比我想象中大。首先之前为了防止漏跑我在cron里设了三个不同的触发时间晚上10点、11点、凌晨1点。最初的想法是多跑几次更保险但因为没有幂等逻辑实际上制造了大量重复数据。修复时区问题后我把三个任务合并成了一个每晚11点触发一次由脚本自身处理重复执行cron层面不需要冗余补偿。其次这次排查逼着我把环境差异纳入了脚本设计。现在脚本启动时会先做一次自检把当前进程的时区、Python版本、数据库文件路径都记录到一个meta表里。谁在哪次运行、用了什么环境全部可追溯。以后再出类似问题我不需要靠猜直接查meta表就能定位。4. 跑完34天我用数据抓出了三个低效习惯技术问题解决之后我趁着第34天做了一次相对完整的复盘。因为从第1天开始每天的记录都进了SQLite所以我手头有34天的结构化数据。这些数据单个看没什么但按周聚合之后暴露出了三个我平时完全没察觉的低效习惯。先说统计方式。我写了几条简单的SQL按星期几统计有效工作时间按小时段统计启动次数把每天的running状态时长汇总。没有用任何复杂的分析模型就是最普通的GROUP BY和平均值。数据量只有34条但足够说明问题了。SELECT strftime(%H, started_at) AS hour, COUNT(*) AS cnt FROM activity_sessions WHERE status IN (done, patched) GROUP BY hour ORDER BY cnt DESC;4.1 一组让我意外的统计结果第一组数据我的产出时间高度集中在凌晨时段晚上11点到1点之间的有效工作时间占全天的百分之四十。这看起来像我是一个夜猫子但实际不是——我每一晚都计划10点收工只是收工前顺手再做一件事的惯性总把我拖到凌晨。也就是说我自己认为的夜间高效其实是白天拖延后的时间挤压。第二组数据从周四到周六连续中断的记录明显增多中断时长短则20分钟长则两小时。一开始我以为是工作太忙但对照日历后发现这三天都有同一个共性——下午安排了需要大量沟通的会议。会议本身没问题问题是会议之后我没有任何缓冲时间导致晚上状态切换不过来于是靠熬夜补偿。第三组数据每天上午9点到11点的记录里有60%的状态是planned而不是running。意思是我每天早上花两小时制定计划但真正动手的时间很少。这个发现挺扎心的因为我一直认为自己是个计划型选手数据却在说你的计划是拖延的变体。4.2 三个低效习惯与对应调整发现问题的下一步是调整而不是自责。我针对这三个习惯分别做了非常具体的改动。第一个习惯白天拖延、晚上熬夜补偿。对应调整我把每日的核心产出任务设置成上午必须启动。所谓启动不需要完成只需要做15分钟把状态从planned改成running。这个设置利用的是一旦开始就不想停下来的心理惯性副作用很小但确实有效。第二个习惯会议后无缓冲。对应调整下午的沟通类日程结束后强制插入30分钟的低强度处理时间专门处理简单事务不做深度工作。这30分钟不写入产出统计它的唯一目的就是让我大脑换挡。第三个习惯计划时间过长。对应调整把每日计划的时间盒子从原来的一小时压缩到20分钟而且只写三件事今天必须完成的一件事今天最多做三件事的清单以及一件如果精力允许再去做的加分项。其余的想法全部丢进收集箱不进入今日计划。这些调整都是基于数据的不是拍脑袋。我现在回看第34天最大的收获反而不是那天修好了什么bug而是我意识到长期项目的日常记录本质上是给自己装一个仪表盘。你不需要每天盯着仪表盘感动自己你只需要定期看一次然后冷不丁发现哦原来我是这么用完时间的。5. 后半程计划放弃完美日报转守最小闭环第34天过完项目还有66天我心里很清楚单靠打卡是不可能走到第100天的。打卡式坚持有一个隐藏成本你要么最终变成敷衍要么因为某天没打卡而全盘否定自己。这两个结果我都不想要所以在第34天我主动把项目的执行标准降了下来——不是降低目标而是把目标从每天完美记录改成每天完成一个最小闭环。5.1 为什么我把每日复盘从长文改成三行前20天我每天的日报写得很用力恨不得把看过的每篇文章、每段代码、每个想法都记进去。结果到了第25天左右写日报变成了一种负担每晚一想到要写两小时复盘就开始逃避。这其实是记录工具最典型的失败记录行为的成本超过了记录本身的收益。现在我把每日复盘改成三行固定句式今天真正做完的一件事是什么今天最大的浪费发生在哪个时间段明天的最小闭环是什么这三行字哪怕状态再差5分钟内也写完了。它牺牲了记录的丰富性但换来了可持续性。长期记录项目的本质不是每天都有高光而是让你在数据层面保持一条连续的线断掉一天整条线的分析价值就没了。5.2 新的每日闭环与允许失败的容错设计执行层面我现在每天只守一条铁律晚上11点前无论进度如何把当天的记录状态标记掉。完成了就标done没完成就标missed绝不允许大脑再花一小时去想我明天要怎么补。这个不补卡的规则是我从跑步训练里学来的——那些今天缺了跑量就第二天加倍补的人最后几乎都受伤了。个人项目也一样你今天没写完第二天硬着头皮补两天的量通常第三天你会直接放弃。合理的容错线应该是一条长一点的曲线每10天允许出现2次missed只要平均值还在可控范围就不启动惩罚机制。很多人把容错设计当成给自己偷懒留后门但我的实际体会是没有容错系统的计划本质上是在押注自己每天都拥有满格意志力而现实显然不是这样。5.3 怎么判断自己是真累了还是在逃避说到放弃这也算长期项目里最常被问到的问题。第34天之后我大概率还会遇到想放弃的日子所以我提前给自己写了一条判断标准。不是靠感觉而是靠几个可验证的信号睡眠平均值连续三天低于6小时且白天明显注意力涣散这是真累了该做的是休息不是硬撑。每天启动时间越来越晚但启动后的专注时长其实没变这是逃避问题多半出在任务本身。连续三天在同一个步骤反复卡住且没有尝试新解法这是方法失效需要降级难度或者请教别人。一想到项目就觉得烦但在做别的琐事时反而有精力这是典型的逃避信号需要重新找回项目的初始动机。这套判断标准并不完美但它最大的价值是把要不要放弃这个模糊的情绪问题变成了四个可以回答的事实问题。我在之前的项目里欠过很多次结局几乎每一次都是栽在这个地方把疲惫当成了厌倦又把厌倦当成了我不行。现在项目走到第34天我反而比第10天更笃定。第10天靠的是冲动第34天靠的是一套已经跑通的数据闭环——每天有记录、每周有统计、遇到问题能追踪到环境差异。如果你也卡在自己的某个第34天我建议你今晚上不要急着加班赶进度先花半小时回答一下我前面那张体检清单里的四个问题。项目的问题基本都能在产品、工具和习惯这三个层次里找到根源而停留在情绪层面的自责是这个阶段最浪费时间的动作。