
放假第一天闹钟没响我七点二十照样睁眼了。躺床上刷了十分钟手机脑子里翻来覆去的不是假期去哪玩而是上周那个拖了半个月没动的小脚本——它到底该按文件名排序还是按拍摄时间排序。九点钟我端着杯茶坐到电脑前新建了一个目录随手敲了个名字holiday_fun。没需求文档没排期没评审也没有人凌晨两点在群里问这个什么时候能上。写崩了就删掉重来写爽了就多跑几遍。这大概就是程序员假期里最朴素的乐子把写代码这件事从交付还原回玩。这篇东西不聊任何高大上的东西就是把我这几年假期里折腾过的几类乐子摊开来讲讲给生活写小脚本、把落灰的旧电脑救活、在终端里写点没用的游戏、教家里人写第一行代码、以及如何跟非技术朋友解释自己到底在干嘛。每一类我都会写清楚为什么这么干、中间踩了什么坑、以及哪些地方是看着简单其实有讲究的。不管你是刚入行一两年想找点练手方向还是干了七八年已经有点职业倦怠、只想找回点新鲜感应该都能从里面挑到一两个能立刻上手的玩法。1. 假期第一天我为什么还是坐到了电脑前1.1 没有需求文档的编程快乐程度完全不一样平时写代码你面对的是别人定义好的问题接口长什么样、字段叫什么、异常怎么抛、性能要求多少毫秒。你像一个拿着图纸施工的人砖怎么砌由不得你。假期自己折腾最大的区别是问题的边界由你定。照片重复了怎么办你可以选择花两周做一个带界面的管理工具也可以选择花二十分钟写个二十行的脚本把重复的先扔进一个待删目录里。没人会因为你做得不够完整给你打回。这种自由带来的直接后果是你会开始关心一些平时根本没空想的问题。比如我那次就想重复照片到底怎么定义文件内容完全一样算重复那同一个场景连拍五张、构图差一点点算不算尺寸缩小过的算不算这些问题在真实项目里通常会被产品经理一句就按 MD5 去重给糊过去但你自己玩的时候会真的去查感知哈希、去算汉明距离、去调阈值。这些没人要求你搞清楚的细节恰恰是技术进步最快的地方。我个人的经验是假期折腾的项目最好满足三个条件一是自己真的会用哪怕只用一次二是技术栈里有一块是你不会的三是能在两三天内跑出第一版。第一条保证你有动力第二条保证你有收获第三条保证你不会半途而废——假期项目最大的死因从来不是太难而是假期结束了还没看到东西跑起来。1.2 我给假期折腾定的三条规矩踩过几年坑之后我给自己定了几条规矩写出来给同样容易上头的人参考。第一条不做需要长期维护的东西。任何需要我每天盯着、每周更新的项目一律不碰。假期项目是玩具玩具坏了可以扔。一旦它变成你不动它就会坏的东西那就不是乐子了。第二条不引入任何需要注册、需要联网授权、需要填手机号的依赖。这条规规矩矩来自一次教训我曾经花了一整个下午给一个小工具接某个在线服务结果卡在验证环节最后什么也没干成假期第一天的心情直接毁了。现在我优先选纯本地、纯离线、纯文件的东西。第三条给自己定一个收工信号。我的收工信号很土当脚本能在我不干预的情况下连续跑通三次我就停手去干别的。这个规则听起来很傻但极其有效——它挡住了我无数次顺手再加个功能的冲动。提示假期折腾最容易滑向加班的形态是顺手加个功能。加之前先问自己这个功能我这辈子会用第二次吗如果答案是大概不会那就别加。2. 第一个乐子给生活写脚本解决那些忍了很多年的小痛点2.1 从照片重复这件破事说起我家里的照片目录大概有十几年的历史经历过至少四次迁移从早期的功能机、到卡片机、到手机、到后来各种备份。迁移过程中产生的重复文件多到什么程度呢我随手统计了一下接近三万个文件里有大概四千多个是完全重复的还有两千多个是同一张图的不同尺寸版本。每次想整理打开目录看一眼就关掉了因为手工处理根本不可能。这个痛点我忍了大概五年。今年假期我决定一次解决而且给自己限定只用一个下午写不超过一百五十行。这个限制很关键因为如果我允许自己做完整的照片管理方案那这个项目必然烂尾。我的方案分三层从粗到细层级判断依据处理方式误判风险第一层文件大小 MD5 完全一致直接归入待删目录几乎为零第二层图片感知哈希pHash汉明距离 ≤ 8输出成对清单人工过一遍中等连拍会误判第三层按 EXIF 拍摄时间归档到年月目录移动而非复制低无 EXIF 时回退到文件修改时间第一层用hashlib.md5五分钟能写完跑一遍三万个文件大概十几秒。这里有个小坑别对每个文件都算全量哈希。先用文件大小分桶只有大小相同的文件才需要算哈希三万个文件里大小完全相同的组合其实不多这一步能把哈希计算量砍掉百分之九十以上。2.2 一个不到百行的去重脚本和它的三个参数坑第二层是感知哈希我用的是imagehash库配合 Pillow。核心逻辑很简单把图片缩到很小的尺寸、转灰度、做离散余弦变换、取低频部分、和均值比较得到 01 串然后比较两个 01 串的汉明距离。代码骨架大概长这样from PIL import Image import imagehash from pathlib import Path import itertools def phash_of(p): try: with Image.open(p) as im: im im.convert(RGB) return imagehash.phash(im, hash_size16) except Exception: return None # 先按文件大小分桶只在桶内比较避免 O(n^2) 全量比较 buckets {} for p in Path(photos).rglob(*): if p.suffix.lower() in {.jpg, .jpeg, .png, .heic}: s p.stat().st_size buckets.setdefault(s // 4096, []).append(p) # 4KB 粒度分桶 pairs [] for _, files in buckets.items(): hashes [(f, phash_of(f)) for f in files] hashes [(f, h) for f, h in hashes if h is not None] for (f1, h1), (f2, h2) in itertools.combinations(hashes, 2): if h1 - h2 8: # 汉明距离阈值 pairs.append((f1, f2, h1 - h2))这里面有三个参数是真正需要动手调的光看文档调不出来。第一个是hash_size。默认值是 8也就是 64 位指纹我用了 16256 位。位数越大越敏感越能区分细节但也越容易把同一张图经过轻微裁剪判成两张。8 位太粗我实测下来会把完全不同的风景照判成相似16 位对我这种连拍很多的相册比较合适。第二个是汉明距离阈值。这个值在 5 到 10 之间浮动取决于你的照片构成。我一开始设成 12结果连拍的一组照片全被判成重复差点删掉我认为最满意的那张。后来改成 8误判明显减少但代价是有一些真的重复没被找出来。这类任务永远是精度和召回做取舍你只能接受漏掉一些而不是追求全部找出来。第三个是分桶粒度。我按 4KB 分桶也就是大小在同一个 4KB 区间内的文件才互相比较。粒度太细比如按字节精确值等于没分桶粒度太粗比如按 100KB会把大量无关文件塞进同一个桶比较次数暴涨。4KB 这个值是我试了三次之后定下来的在我的样本上把比较次数从几千万降到了几十万。第三层归档相对无聊但有个细节值得说EXIF 里的拍摄时间字段格式不统一有2024:07:15 10:23:11这种带冒号的也有标准 ISO 格式还有大量手机导出的图片干脆没有 EXIF。我的做法是按优先级取EXIF 原始时间 → EXIF 数字化时间 → 文件修改时间取到哪个用哪个并且在目录里留一个_noexif子目录专门放猜不准的避免污染主目录结构。2.3 顺带把账单整理也自动化了照片之后我又顺手做了个更小的东西账单整理。起因是我发现自己每个月都在手动把几个支付渠道导出的 CSV 拼到一起然后用表格软件做透视一年下来干了十二次每次半小时。六小时的生命就这么没了。这个脚本更简单纯标准库csv加datetime就够了。真正麻烦的不是代码是各渠道导出格式的差异有的用正负号表示收支有的用单独一列收/支有的日期是2024/07/15有的是2024-07-15 10:23:11有的金额带货币符号和千分位逗号。我的处理方式是把每个渠道写成一个小的解析函数统一输出成(日期, 金额, 分类, 备注)四元组然后统一写到一个总表里。这里分享一个我觉得挺值的小技巧给每个渠道的解析函数写一个三行的自检。就是拿最近一个月的真实文件跑一遍把总金额和渠道页面上的月度汇总对一下对不上就报错。这比写单元测试省事得多而且能立刻发现这个月格式又变了这种问题——渠道导出格式一年改两三次是常态纯靠肉眼检查你迟早会漏。注意处理账单数据时我强烈建议把脚本和原始数据放在本地目录不要顺手传到什么在线服务上去。这类数据一旦外流麻烦比省下的时间大得多。3. 第二个乐子把落灰的旧笔记本复活成家里的数据管家3.1 为什么我不用现成的成品方案家里有一台大概七八年前的旧笔记本配置在今天看来很低双核处理器、4G 内存、一块 500G 的机械硬盘开机风扇响得像要起飞。它躺在柜子里两年多扔掉可惜卖也不值钱。今年假期我把它翻出来决定做一件纯粹为了好玩的事把它变成家里局域网里的数据管家负责照片归档、文件共享和定时备份。很多人第一反应是为什么不直接买个成品设备确实更省事。但我坚持自己折腾理由有三个。第一成品方案出问题时你只能等更新而自己搭的东西出了问题你可以直接看日志、改配置、换组件。第二成品方案通常把功能做得很全但每个功能都只做到够用而我要的只有三件事做减法反而更清爽。第三也是最实在的一条这台旧机器的性能跑三个轻量服务绰绰有余闲着也是闲着。选型上我用了轻量 Linux 发行版桌面环境全部不装只留命令行。这一步省下的资源非常可观——同样一台机器装完整桌面跑起来内存就吃掉一半纯命令行启动后内存占用不到 300M剩下的全留给服务。这里的原则是这台机器的定位是家里的基础设施不是电脑你不会坐在它前面办公所以不需要任何图形界面。3.2 系统选型与最小可用服务的搭建装系统这一步没什么好说的但有几个地方容易翻车。第一是网卡驱动老机器上偶尔会遇到装完系统不认无线网卡的情况我的做法是直接插网线反正它以后就固定放在路由器旁边无线根本用不上还省得配。第二是启动方式老机器可能是传统引导也可能是新型引导装之前先进固件设置里看一眼能省掉大量装完起不来的排查时间。三个服务我分别说一下。文件共享用的是常见的局域网共享方案配置文件里其实只需要几行[share] path /srv/share browseable yes read only no valid users homeuser create mask 0664 directory mask 0775这里我踩过一个坑一开始没设create mask结果从另一台机器拷进去的文件权限变成了 0600家里其他人访问不了我找了半小时才发现是权限问题而不是网络问题。共享目录的权限问题百分之八十出在掩码没设对。照片归档就是复用上面那个脚本只不过改成了定时任务。定时任务的表达式我用的是每天凌晨三点0 3 * * * /usr/bin/python3 /srv/scripts/archive.py /var/log/archive.log 21写这个的时候一定要注意把标准错误也重定向到日志里否则脚本出错你完全看不到只会觉得怎么照片没归档。我第一次就漏了21白白多花了一个晚上排查。定时备份我用了最土的办法用rsync把关键目录同步到一块外接硬盘上同样挂在定时任务里。土办法的好处是可靠、可读、随时能手工跑一遍验证。3.3 自启动与稳定性一条配置背后的思考服务写完接下来是让它开机自启、挂了自动重启。我用的是系统自带的服务管理方式配置文件大概是这样[Unit] Descriptionphoto archive service Afternetwork.target [Service] Typesimple Userhomeuser ExecStart/usr/bin/python3 /srv/scripts/watcher.py Restarton-failure RestartSec10 [Install] WantedBymulti-user.target看起来简单但Restarton-failure和RestartSec10这两个参数背后是有讲究的。为什么不设成总是重启因为如果你的脚本本身有逻辑错误一启动就崩总是重启会让它疯狂重启把日志刷爆机器负载飙升你还得花时间去找为什么风扇突然狂转。设成仅失败时重启配合 10 秒间隔既能让偶发的网络抖动、文件锁冲突自动恢复又不会在真出问题时把机器拖垮。另外Afternetwork.target这一行也不是随便写的。早期我没加结果开机后脚本比网络就绪还早一上来就去挂载网络位置失败后直接退出。加了这一行之后问题解决。顺序依赖这件事在你自己家里的小机器上一样重要。3.4 硬盘容量与备份策略的算术题最后一个值得算清楚的账是容量。这台机器原装是 500G 机械盘我算了一下十几年的照片大约 300G加上一些文档和少量视频勉强够用但没有任何冗余空间。一旦这块盘坏了这些照片就全没了。所以我又加了一块同容量的硬盘做成镜像模式两块盘存一模一样的数据。代价是可用容量还是 500G但任何一块盘坏掉数据都还在。这笔账我算得很清楚一块旧硬盘的成本远低于数据丢失的代价。这里有几个数字值得记住。假设你有 3 万张照片、平均每张 4M那就是 120G如果再加上视频一个家庭十年的素材很容易超过 500G。所以做这类规划时不要按现在有多少来算要按三年后有多少来算。我当初就是按现状配的容量结果一年半就紧张了。另外镜像阵列不是备份它只防硬件故障不防误删和损坏。所以我还留了一份外接硬盘的冷备份每个月手动接上去同步一次。冷备份的好处是物理隔离代价是需要人记得去做——这也是我把它写进日历提醒的原因。4. 第三个乐子在终端里写小游戏纯粹图一乐4.1 为什么选终端而不是图形界面写小游戏这件事很多人第一反应是搞个窗口、画点图形。我试过最后放弃了原因很现实图形界面会把你的大部分时间花在和渲染框架、资源加载、窗口事件打交道上真正写游戏逻辑的时间不到三成。而假期项目最怕的就是时间分配失衡。终端里写游戏好处是极其轻量没有窗口、没有贴图、没有音频你只要管每一帧打印什么和按键怎么响应。整个游戏的状态可以用几个变量表示屏幕就是一格一格的字符。这种限制反而让人专注——你会发现仅仅是蛇怎么移动吃到食物怎么变长这些逻辑就足够玩一下午。我用的是 Python 自带的curses库不需要装任何东西。唯一的坑是这个库在某些系统上默认不可用需要额外装一个补充包装完记得改一下导入名。这个问题我第一次遇到时完全没头绪报错信息只说模块不存在查了半天才发现是平台差异。4.2 贪吃蛇的帧循环与按键处理的难点贪吃蛇的核心是一个循环读取按键 → 更新蛇的位置 → 判断碰撞 → 重绘屏幕 → 等一下。听起来简单但有两个地方是真正的难点。第一是按键响应不能阻塞。如果你用标准的等键盘输入的函数游戏会卡在那里直到你按键蛇就动不了了。正确做法是把输入设成非阻塞模式每一帧去看一眼有没有新按键有就处理没有就按原方向继续走import curses, time def main(scr): scr.nodelay(True) # 非阻塞输入 scr.keypad(True) direction (0, 1) tick 1 / 12 # 每秒 12 帧 while True: ch scr.getch() if ch in (curses.KEY_UP, ord(w)): direction (-1, 0) elif ch in (curses.KEY_DOWN, ord(s)): direction (1, 0) elif ch in (curses.KEY_LEFT, ord(a)): direction (0, -1) elif ch in (curses.KEY_RIGHT, ord(d)): direction (0, 1) elif ch ord(q): break # ……更新位置、判断碰撞、重绘…… time.sleep(tick) curses.wrapper(main)第二是帧率到底设多少。这个数字直接决定游戏手感。我一开始设成每秒 30 帧结果蛇快得像在飞根本来不及反应改成每秒 5 帧又慢得让人想睡觉。最后定在每秒 12 帧配合吃到食物后速度略微提升的规则手感刚好。这里有个反直觉的点帧率不只是画面流畅度它同时是游戏难度的一部分。在一个没有动画的纯字符界面里帧率就等价于蛇的移动速度。还有一个特别容易忽略的细节方向和反向的判定。如果你正在向右走此时按下左键如果你直接赋值蛇会立刻掉头撞上自己。所以要么忽略反向输入要么把输入放进队列、在每一帧只消费一个。我一开始就是直接赋值结果自己撞自己还以为碰撞检测写错了排查了半小时。4.3 代码高尔夫一种病但很快乐终端小游戏之外我假期还特别爱干一件事把一段代码压到尽可能短。这个玩法在圈子里有专门的名字本质是用极少的字符表达同样逻辑。比如求一个列表所有元素的乘积正常写法是循环压到极致是一行from functools import reduce prod lambda xs: reduce(lambda a, b: a * b, xs, 1)这种玩法对工作没有任何帮助我必须诚实地说。但它有一种特别纯粹的乐趣它逼你去想这段逻辑的最小表达是什么。你会开始研究三元表达式、列表推导、map和filter的组合、用位运算代替乘除、用字符串切片代替分支。这些技巧在日常代码里多半是不该用的——可读性优先这点不能动摇——但作为思维体操它确实能让你对语言的理解深一层。我给自己定的规矩是玩代码高尔夫的东西一律不进正式项目。原因很简单压缩过的代码只有写的人看得懂三个月后连你自己都看不懂。它属于假期的乐子不属于交付物。5. 第四个乐子教家里人写第一行代码5.1 别从语法开始教这个假期我做了一件挺有成就感的事教家里一个刚上初中的孩子写了人生第一段能跑起来的代码。我原本准备了一堆变量、类型、循环的讲解结果发现完全跑偏了——他坐下三分钟就开始问然后呢它能干嘛这个反馈很真实。对完全没接触过编程的人来说这东西能干什么永远排在它是什么前面。所以我立刻改了策略直接上画图。画图的好处是即时反馈你写一行屏幕上立刻多出一条线因果关系是看得见摸得着的。import turtle t turtle.Turtle() t.speed(0) for i in range(36): t.forward(120) t.right(170) turtle.done()这段代码的效果是一朵有点好看的花。他很惊讶就这几行然后我们开始改参数170改成150会怎样改成172呢36改成72呢每一个数字的改动都会带来肉眼可见的变化这个过程本身就是在教他参数这个概念比讲定义有效一百倍。他后来自己试出了好几个我没见过的图案那个兴奋劲儿比我写出一段优雅的代码还高兴。5.2 一次失败的变量讲解和后来的补救当然也有讲砸的时候。我想给他解释变量用的比喻是一个贴了标签的盒子你可以往里放东西。讲完之后他点头我以为懂了随手出了道题交换两个盒子的东西。他写出来是这样a 1 b 2 a b b a然后运行两个都变成了 2。他一脸茫然我也一时不知道怎么解释——因为在我的脑子里先存一份是常识但对他来说是全新的。后来我换了方式我拿了两个真实的杯子一个装水一个装果汁让他想办法交换。他本能地先去找了第三个空杯子。我说对代码里也要有第三个空杯子那叫临时变量。他立刻就明白了还自己吐槽哦所以刚才那个果汁被水盖住了。这件事给我的启发挺深很多我们觉得这不是理所当然的吗的东西对新手来说是完全空白的。而我们平时讲技术最容易犯的错就是跳过这些理所当然。所以我后来跟他讲任何概念都尽量配一个能动手碰的东西。顺便说一句如果你也想带新手我的建议是别急着让他理解为什么。先让他改参数、看变化、产生咦这个有意思的感觉再回头讲原理。顺序反了大概率在三分钟内失去兴趣。6. 第五个乐子给非技术朋友讲清楚我到底在干什么6.1 类比不是简化是重构假期聚会最常被问到的问题是你们程序员到底干嘛的。这个问题看起来简单实际上极难回答。我见过太多同行一开口就是我们做的是分布式系统的可观测性建设对面立刻礼貌微笑然后转移话题。我后来总结出一条解释技术不是把术语换成大白话而是把你脑子里的结构重新组织成对方脑子里已有的结构。这两件事完全不同。前者是把缓存改成临时存东西的地方对方还是不知道你为什么需要它后者是你家厨房烧水要三分钟所以你在客厅放了个保温壶想喝随时倒——这就是缓存对方立刻就懂了你为什么需要它。这个区别很关键。类比的价值不在于简单而在于对方已经有的经验。所以给不同的人讲同一个概念类比应该是不一样的。给经常做饭的人讲缓存用保温壶给常开车的人讲缓存可能就得用后备箱。6.2 三个我常用的类比和它们的失效边界我常用的类比大概有三个每个我都清楚它在哪里会不成立。第一个变量 贴标签的盒子。这个类比几乎人人都能懂但它在讲引用的时候会失效。因为现实里的盒子装的是东西本身而代码里的盒子经常装的是东西放在哪的纸条。一旦讲引用传递这个类比就会把人带沟里。所以我用完之后一定会补一句注意盒子里有时候装的不是东西而是一张写着地址的纸条。第二个函数 豆浆机。放豆子和水进去出来豆浆。这个类比好在能解释参数和返回值。它的失效点在于副作用——真实的豆浆机不会顺手把你厨房的灯关掉但函数可以顺手改一个全局变量。我用这个类比的时候会明确说好的函数就像好豆浆机除了出豆浆什么都不干坏的函数会顺手把你家灯关了。第三个数据库索引 书的目录。这个类比很经典也基本准确。它的失效点在于书的目录不会因为你在书上写字而变慢但数据库的索引会——每次写入都要维护索引。所以我会补一句这是一种用写的时候慢一点换查的时候快很多的交易。我特别想强调的一点是别怕承认类比的边界。很多人为了让解释显得干净会把类比推到它不该到的地方结果对方后来发现不对劲反而觉得你在糊弄。主动说这个比喻到这儿就不准了反而会让人更信任你讲的东西。心得讲技术的时候判断对方有没有听懂不要问明白了吗要问你觉得这个东西在什么情况下会不好用。能答上来说明真懂了答不上来说明你的类比还没到位。7. 乐子的边界别让假期折腾变成另一种加班7.1 时间盒与完成度只要六成原则我必须把这部分写出来因为我在这上面栽过不止一次。有一年假期我本来只想写个小工具结果越做越大加了配置、加了日志、加了图形界面最后写了两千多行假期结束的时候它还是个半成品而且因为需求一直在变代码已经乱到我不想再碰。那次之后我意识到假期项目的最大敌人不是技术难度是我自己失控的完美主义。现在我给自己上时间盒每天最多两小时到点必须停。这个限制看起来很反效率但它实际上提升了总产出因为两小时的专注度远高于断断续续六个小时。而且每天停下来之后第二天重新开始时会带着昨天那个问题怎么解的思考回来效率反而更高。配套的是完成度只要六成原则。一个脚本能跑输出有点丑参数写死在代码里这就可以收工了。别急着把参数抽成配置、把输出格式化、把异常处理补全。这些东西在你的正式项目里当然要做但在假期玩具上它们只会让项目活得更久、更烦人。我现在的判断标准是这个项目如果明天我完全不碰它会不会有损失不会那就可以停了。7.2 那些我踩过的、把乐子玩崩的坑最后列几个具体的坑都是我自己实打实踩过的写出来省得别人重走一遍。第一个坑把玩具项目接进了生产流程。我曾经写了个自动整理下载目录的小脚本觉得挺好用就让它常驻后台跑。三个月后它因为一个没处理的异常挂掉了而我已经完全忘了它的存在直到某天下载目录里堆了六百多个文件才想起来。教训是临时脚本一旦变成常驻服务你就欠了一笔维护债。要么认真维护要么干脆别让它常驻。第二个坑在假期后半段开始学全新的大技术。我试过用一个假期入门一个完全陌生的技术栈结果是前几天全花在环境配置上中间两天在各种报错里挣扎最后一天急着出成果写了一堆自己都不满意的代码。现在我的原则是假期只做一块新东西的项目其余部分都用熟的工具。用熟的部分保证你能跑出东西新的那一块保证你有收获。第三个坑把给自己玩的东西拿去给别人用。一旦有第二个人用你的工具性质就变了他开始提需求、报 bug、问怎么装。你从玩的人变成了维护的人。我现在非常谨慎除非我已经确定要把这个东西当正经项目做否则不会主动给别人用。分享代码和分享工具是两件完全不同的事。第四个坑追求优雅到写不出来。有段时间我陷入一个怪圈脑子里一直在想这段代码最优雅的写法是什么结果一行都没写。后来我强迫自己先写最丑能跑的版本跑通了再重构。能跑的丑代码永远好过脑子里的优雅方案。这个道理在正式工作里成立在假期玩具上更是如此因为你没有 deadline 逼你从想切换到写。第五个坑不写一句注释。你可能会想玩具项目写什么注释。但我吃过亏隔了两周回来想改个参数看着自己写的变量名tmp2发呆完全不记得当初在干嘛。现在我给玩具项目的唯一强制要求是开头写五行说明这段脚本干什么、怎么跑、参数在哪改。五分钟成本能省掉未来半小时的困惑。说到底程序员的假期乐子本质上是一种把技能从谋生工具还原成玩具的过程。工作里的代码要对齐需求、要过评审、要考虑稳定性和可维护性假期里的代码能跑就行跑不通删了重来也行。这种自由在职业生涯里是稀缺品别把它也搞成 KPI。我这几年最开心的一段代码是一个只有四十多行、按我自己的奇怪规则给照片排顺序的小脚本它没有任何价值但我每次跑它都觉得挺有意思。如果你这个假期也想折腾点什么我的建议是从你忍了很久的那件小事开始那个每次都手动做的操作、那个每次都让你烦的文件目录、那个你一直想搞清楚但没时间的工具。别做大别做全两天能跑出第一版就够了。跑通了你会找回一点刚学编程时的那种感觉。