浪白的博客の小站
首页项目归档照片墙音乐灵境说说杂谈友链关于

云端杂谈

“ 记录一些想法与碎片 ”

cover
✨ 温和
2026-08-28 21:45:00

写给刚开始写代码的自己

前几天有个朋友刚转行做开发,问我有什么建议。我零零散散说了些,事后想想可以整理得更清楚。 写下来,算是给当年的自己。 最开始学编程的时候,看教程什么都懂,自己写就卡住。当时以为是自己笨。 后来明白这是正常的。**看懂是被动接收,会写是主动构造,中间隔着一道墙。** 拆掉这道墙的唯一办法是自己动手写,而且写的时候不看教程。 现在我学新东西的流程是:看一遍文档,然后**关掉文档自己写一遍**。写不出来再回去看,但只看看不出来的那部分。 这个"自己写"的环节,比看十遍教程有用。 ## 二、错误信息要读到底 以前看到报错第一反应是复制去搜索。搜到答案贴上,问题解决了,但**不知道为什么**。 现在的习惯是先读完错误信息: - 出错的文件和行号 - 具体是什么错(类型不对?变量未定义?文件不存在?) - 调用栈里哪一层是我的代码 大概七成的错误读完信息就能想明白。剩下三成再搜,但搜的时候也能问出更准确的问题。 **"读错误信息"是个可以练的技能。** 而且越往后越重要,因为越复杂的系统,报错信息里包含的线索越多。 ## 三、先让它跑起来,再让它变好 我以前写代码喜欢一次写对。想清楚了每一个边界情况,然后一口气写完。 问题是:想得越多,开始得越晚。而且大部分"想清楚"的边界情况,实际上根本不会发生。 **现在的做法是先写一个"能跑就行"的版本。** 比如要写数据同步: ```python # 第一步:最朴素的方式,能跑通就行 for row in source: target.save(row) ``` 跑通了,确认基本逻辑对,再考虑: - 批量写入(性能) - 失败重试(健壮性) - 断点续传(大批量) - 并发(提速) **每一步都有可运行的版本**,而不是先在脑子里设计完整再动手。 这个方式还有个好处:**早发现"需求理解错了"**。如果花两周设计再开始写,发现理解错了就白费;如果先写个粗糙版本给人看,十分钟就知道方向对不对。 ## 四、代码是写给人看的 我用了很久才接受这件事。 刚工作时,我评判代码的标准是"能不能跑"。后来加上"跑得快不快"。再后来才是"别人能不能看懂"。 现在我优先级最高的反而是可读性。因为: - 代码被阅读的次数远多于被编写的次数 - 一个读不懂的优化,三个月后自己也不敢改 - 团队协作下,看不懂就等于不能用 具体怎么做: **名字要准。** `processData()` 不如 `calculateMonthlyRevenue()`。多打几个字,省下别人十分钟。 **函数要短。** 一个函数做一件事。如果一个函数需要"并且"来描述它做什么,那就该拆。 **注释写"为什么",不写"是什么"。** `// 循环用户列表` 没有意义;`// 这里要倒序,因为后面的逻辑依赖最新的在前` 有价值。 ## 五、学会说"我不知道" 这条我花了最久。 刚开始工作时,怕被人觉得水平不行,遇到不懂的会含糊过去,或者凭猜测给个答案。 结果是有一次猜错了,导致同事基于我的错误信息做了一天的无用功。那件事很尴尬。 后来我改了:**不知道就说不知道,然后说"我去查一下"或者"我不确定,可能是 X,我们验证一下"。** 出乎意料的是,没人因此觉得我水平差。反而因为我给的答案可信度高了,大家更愿意问我。 **"我不确定"比一个错误的肯定答案有价值得多。** ## 六、把时间花在基础上 我有一段热衷于学各种框架和工具。React、Vue、Django、Laravel、Spring 都摸过一遍。 但基础的东西——数据结构、算法、操作系统、网络、数据库原理——一直是短板。 结果是:遇到性能问题不知道怎么分析,遇到并发问题只能靠搜,遇到奇怪的 bug 看不懂底层原因。 真正的分水岭出现在我开始读《深入理解计算机系统》和看数据库的实现原理之后。**很多"玄学问题"变得可以推理了。** 框架会过时,基础不会。 ## 七、接受"写不完" 以前总觉得项目做完了就轻松了。实际上没有"做完"这回事。 需求会变、依赖会升级、服务器会换、流量会涨。维护是常态,不是例外。 接受这一点之后,心态好很多: - 不用追求"一次写完美",反正要改 - 不用做完所有功能才上线,可以慢慢迭代 - 不用因为"还有 TODO"焦虑,TODO 本来就不会清零 ## 八、记录比记忆可靠 我记性一般。以前靠记性,经常出现"这个坑我好像踩过但忘了怎么解决的"。 现在会写: - 踩过的坑和解决办法 - 一个决策的来龙去脉(为什么选 A 不选 B) - 排查问题的过程(哪怕最后没解决) - 看到的好文章和自己的理解 不用写得多正式,我自己用的就是简单的 Markdown 文件。 **写下来这个动作本身就有价值**——它强迫你把模糊的理解整理成清楚的表述。很多时候写的过程中就想明白了。 ## 九、关于"天赋" 这个可能是最想对当年的自己说的。 我曾经因为"别人好像很快就懂了,我要好久"而怀疑自己不适合做这行。 后来发现两点: **一、别人没那么快。** 你看到的是他学会之后的表现,没看到他卡住的时候。 **二、这东西确实有快慢,但差别没想象中大。** 而且**持续性比初始速度重要**。我见过很多上手快的人三五年后停在一个水平,也见过上手慢但一直在深挖的人后来居上。 如果一定要选一个特质,我选"能持续地投入时间",而不是"聪明"。 ## 十、最后一条 **做点能给别人用的东西。** 学的时候容易陷在"学"本身。但真正让我进步最快的,是做一个**真的有人在用**的东西。 因为有人用,所以: - 要考虑他没按你预期操作的情况 - 要处理你没想过的数据 - 要把错误信息写清楚(不然他看不懂) - 要真的把功能做完(不然他不用) 这些都逼着你把东西做扎实。玩具项目可以糊弄,有人用的项目不行。 ## 最后 这些话如果传回去给当年的自己,他大概会觉得"这些我都知道啊"。 知道和做到之间还是有距离的。而且有些事大概只能自己踩过才懂。 不过还是写下来了。万一有点用呢。
#随笔#成长#总结
cover
✨ 吐槽
2026-08-05 22:00:00

工具链的耐心是有限的

最近在优化项目的构建流程,顺手算了一下时间账,有点触动。 改造前的流程: | 步骤 | 耗时 | |---|---| | 装依赖(有缓存) | 20s | | 静态检查 | 35s | | 单元测试 | 50s | | 构建产物 | 40s | | 部署到测试环境 | 25s | | **合计** | **170s** | 接近三分钟。这还不算排队等 CI runner 的时间。 假设我一天提交 15 次,那是 42 分钟。加上偶尔失败重跑,一天一小时。 **一天一小时在等构建。** 而且这个等待是**打断式的**——你不能安心去做别的,因为几分钟后就要回来看结果。 ## 等待为什么比看起来更贵 如果一天有一小时纯粹的空闲,那还好。但实际不是: **一、上下文切换的成本。** 我在等构建的时候,会去开个浏览器、看个消息、读篇文章。等构建好了,回到代码上需要重新建立上下文。这个成本可能比构建本身还大。 **二、它改变了我提交的方式。** 因为构建慢,我会攒着改动一起提交。"等这几个功能都做完再跑一次 CI"。结果一次提交五百行,出问题的时候很难定位。 **三、它让人倾向于跳过检查。** 本地跑测试要 50 秒,急着验证一个想法的时候就会想"这次先跳过,CI 会跑"。然后 CI 也跳过了(或者没人看结果)。 ## 我做的优化 **一、并行化。** 静态检查和单元测试没有依赖关系,可以同时跑: ```yaml jobs: static: steps: [ ... ] test: steps: [ ... ] ``` 170s → 大概 110s(取最长的那条链路)。 **二、缓存依赖。** 这个是投入产出比最高的。依赖不变的时候直接命中缓存,20s → 3s。 缓存 key 要用 lock 文件的哈希: ```yaml key: deps-${{ hashFiles('composer.lock') }} ``` 不要用分支名做 key,那样每个分支第一次都要重装。 **三、测试分片。** 测试拆成 4 份并行跑: ```bash # 用 paratest 或者自己按测试类哈希分片 vendor/bin/paratest --processes=4 ``` 50s → 15s。 **四、只跑受影响的测试。** 这个需要工具支持(比如 `phpunit --filter` 配合 git diff 分析),准确率不完美,容易漏测。我用在本地开发的 watch 模式里,CI 上还是跑全量。 **五、部署改成增量。** 原来是每次全量打包上传。改成只传变化的文件,25s → 8s。 **优化后:** | 步骤 | 耗时 | |---|---| | 装依赖(缓存命中) | 3s | | 检查 + 测试(并行) | 20s | | 构建 | 40s | | 部署 | 8s | | **合计** | **71s** | 从 170s 到 71s。**一天省下大概 25 分钟。** ## 但真正的问题可能不是速度 优化完之后我在想:就算变成 30 秒,那一天还是 15 次提交,其中可能有一半是"改个错别字"或者"调一下日志"。 **频繁的小提交本身可能才是问题。** 不过这个不好一概而论。快速反馈循环对某些工作方式是有益的(比如边写边验证),只是不该每次都触发完整流程。 我现在的做法是分两层: - **快速检查**(保存时自动跑):语法、代码风格、单文件测试。5 秒内。 - **完整流程**(提交时跑):全量测试、构建、部署。接受它慢一点。 关键是**让开发者能自己选择**。不是所有改动都需要完整验证。 ## 一些不太明显的成本 **一、本地环境的启动时间。** 如果新同事 clone 下来要两小时才能跑起来,那这个项目在有意识地排斥贡献。我们目标是 10 分钟以内(下载依赖不算)。 现在用 `docker compose up` 一条命令起来,加上一份初始化脚本填充测试数据。 **二、报错信息的质量。** 静态检查报一个错,如果信息是 `Error in line 42`,我得自己去找问题。如果能说清楚"这个参数类型不匹配,期望 int 得到 string",我一眼就知道怎么改。 花时间定制检查工具的输出格式,收益比想象中大。 **三、日志的查找难度。** 线上出问题,从告警到定位到具体请求,如果需要二十分钟翻日志,那比代码 bug 本身更耗时。 我们后来统一了 trace id,一个请求跨几个服务都能串起来。这个改造花了两天,之后每次排查省的时间累积起来很可观。 ## 关于"等构建的时候干什么" 我之前的做法是切去干别的。后来发现效果不好,因为: - 构建只有两分钟,干不了需要深度思考的事 - 被打断之后回到原来的思路要时间 现在我的做法是: - **构建期间只看轻量的东西**:review 一下 diff、更新一下任务状态、回个消息 - **需要深度思考的事情,攒到构建跑完再做** 或者更好:**把两个不相关的任务交替做**。A 的构建在跑,我去写 B 的设计文档。 ## 一点反思 我以前觉得"工具链慢"是个客观限制,忍受就好。 现在觉得它是个**可以也应该被优化的东西**,因为: 1. 它每天都发生 2. 它的成本是隐性的(不会有人为"等构建"建个工单) 3. 它影响的是所有人的效率 我见过一些团队对"构建慢"的态度是"就那样吧,反正能跑"。但如果你算一下一年在这个上面花掉多少人天,可能会想改改。 ## 最后 优化完之后最大的感受不是"快了 2.4 倍",而是**我提交的时候不那么犹豫了**。 以前改个小东西会想"值不值得跑一次 CI",现在随手就跑了。这个心理变化带来的是更好的习惯——小步提交、频繁验证。 **工具的速度影响的不只是时间,还有工作方式和心态。**
#随笔#工具#效率
cover
✨ 平静
2026-07-15 23:10:00

"够用就好"这四个字,我花了很久才懂

刚工作那几年,我特别热衷于"把东西做好"。后来发现,很多时候我在做的是"把东西做复杂"。 早期做过一个配置读取的模块。需求很简单:从配置文件读几个值给业务用。 我当时的实现: - 支持 JSON、YAML、INI 三种格式(虽然只用 JSON) - 支持配置继承(base.yaml + env.yaml 合并) - 支持环境变量覆盖 - 支持热更新(监听文件变化) - 支持类型校验和默认值 - 加了一层缓存 - 写了 40 个单元测试 总共大概 1200 行。 **实际用了多少?** 只有 JSON 读取和默认值,大概 80 行的量。 剩下 1120 行是负债: - 每次有人要加个配置项,得先看文档搞清楚继承规则 - 热更新在生产环境出过一次 bug(文件被改到一半时读到了残缺内容) - 那 40 个测试里有 30 个测的是用不到的功能,跑一次要 8 秒 后来我把它删了,换成: ```php final class Config { private array $values; public function __construct(string $path) { $values = json_decode(file_get_contents($path), true, 512, JSON_THROW_ON_ERROR); if (!is_array($values)) { throw new \RuntimeException("配置文件格式错误: $path"); } $this->values = $values; } public function getInt(string $key, int $default = 0): int { return isset($this->values[$key]) ? (int) $this->values[$key] : $default; } public function getString(string $key, string $default = ''): string { return isset($this->values[$key]) ? (string) $this->values[$key] : $default; } } ``` 40 行。够用,而且**所有人都看得懂**。 ## 为什么当时会那样做 我复盘了一下,大概有几个原因: **一、把"以后可能需要"当成了"现在需要"。** 那时候看了一些讲"可扩展架构"的文章,觉得写得越灵活越专业。实际上,"灵活"是有成本的——每一层抽象都要有人理解它、维护它。 **二、想证明自己会写"高级"代码。** 这个不太好承认,但确实存在。写 1200 行带继承和热更新的配置模块,比写 40 行朴素代码更能显示"水平"。 现在的我知道,**能把复杂需求用简单方案解决,才是水平。** **三、没有成本意识。** 当时觉得多写代码不花什么。实际上每一行代码都在消耗未来的时间:读它的时间、改它的时间、调试它的时间。 ## 什么时候"过度"是值得的 也不是所有多余的东西都该删。有些"过度"是有道理的: **一、对外接口要留余地。** 如果是给别人用的库或者 API,多留一些扩展点是对的,因为你没法预知使用者的需求。 ```php // 对外 API:参数用对象,加字段不破坏兼容 public function send(Message $message): Result // 而不是 public function send(string $to, string $subject, string $body): bool ``` **二、核心数据结构要想清楚。** 数据库表结构、领域模型、数据格式,这些改起来成本极高。这类地方多想几天是值得的。 **三、真的有第二个使用方的时候。** 注意是"真的",不是"可能"。 ## 怎么判断是不是过度了 我现在的几个自问: **一、这个东西现在有几个人用?** 如果只有一个,抽象层可以省掉。 **二、如果需求变了,改起来要多久?** 如果十分钟能改,那就不用提前设计。 **三、新人理解这个要多久?** 如果一个简单功能需要解释半小时,说明抽象层太多了。 **四、删掉它会不会有人发现?** 如果不会,那它可能本来就不该存在。 **五、我在写这段代码的时候,是在解决问题还是在展示技巧?** 这个问题最扎人,但很有用。 ## 现在的一些具体做法 **一、先写最简单能跑的版本。** 不是先设计架构再实现,而是先让功能跑起来,然后看哪里真的需要抽象。 **二、同一段逻辑出现第三次才抽函数。** 出现两次就复制。因为两次的时候你还不确定它们会不会分化;第三次说明模式稳定了。 这条我以前不信,现在是认真的。复制两遍的成本,比抽错接口然后被迫兼容两种用法的成本低。 **三、不加"以后可能用到"的参数。** `function foo($a, $b, $options = [])` 里的 `$options` 如果现在没有使用场景,就删掉。需要的时候再加,加参数是向后兼容的。 **四、配置文件尽量扁平。** 嵌套结构看着整齐,但解析和校验的代码会复杂很多。而且改一个值要数清楚层级。 **五、定期删代码。** 我每季度会挑一个模块,看能不能删掉点东西。删代码比加代码爽。 ## 一件让我改变想法的事 有个模块我写得很"完善",各种设计模式都用上了。半年后要加个功能,我去改的时候,**花了一个下午才理解自己当时的设计意图**。 那个下午我在想:如果当时写的是 200 行直白的代码,现在可能二十分钟就改完了。 **代码的读者主要是未来的自己和同事。让他们少花时间理解,比让代码"优雅"重要。** ## 最后 "够用就好"不是"不追求质量"。它说的是:**把精力花在真正需要的地方。** 一个功能,如果数据结构选对了、边界情况处理了、错误有日志、有基本的测试,那就是高质量的。至于它是不是用了设计模式、是不是能支持某种未来的扩展,不重要。 我现在写代码会问自己一个问题:**如果明天我离职了,接手的人需要多久能改这个功能?** 这个问题的答案,比"代码是否优雅"更能说明质量。
#随笔#心态#工程
cover
✨ 感慨
2026-05-30 21:25:00

重读自己五年前写的代码

整理旧服务器的时候翻到一个五年前的项目,还在跑。打开代码看了一遍,感觉挺复杂。 **一、命名完全没有一致性。** 同一个东西,在不同文件里叫 `user_id`、`userId`、`uid`、`user`。变量名有 `$data`、`$data2`、`$tmp`、`$result`、`$res`。 有个函数叫 `handle()`,我盯着看了五分钟才明白它具体处理什么。 **二、函数长得离谱。** 最长的那个 380 行,做了:接收请求、校验参数、查数据库、算价格、写订单、发邮件、写日志。中间没有一个空行把它分段。 现在的我肯定会拆成七八个函数。但当时的想法大概是"逻辑是连续的,拆开了还要来回跳"。 **三、注释和代码不一致。** ```php // 检查用户是否已激活 if ($user['status'] == 2) { // ... } ``` 而 `status` 为 2 在这套系统里表示"已删除"。注释是半年前写的,后来逻辑改了但注释没改。 **四、大量的复制粘贴。** 有六处几乎一样的代码,只有表名和几个字段不同。当时肯定是"先复制一份改改,以后再重构"。然后就没有以后了。 **五、"临时"的痕迹。** - `test.php`:还在被一个定时任务调用 - `fix_20210315.php`:一次性修复脚本,但里面有个 cron 在跑 - `// TODO: 后面优化`:五年前的 TODO ## 再说让我意外的部分 **一、它真的还在正常工作。** 五年了,这个系统处理了大概几百万订单,没出过大事故。虽然代码丑,但**业务逻辑是对的**。 这让我重新想了一件事:**代码整洁度和软件正确性,相关性没有想象中那么强。** **二、有些地方比我现在的写法还谨慎。** 比如金额计算,那个项目里全部用整数分,还在关键地方加了断言: ```php if (!is_int($totalCents)) { throw new \RuntimeException('金额必须是整数分: ' . var_export($totalCents, true)); } ``` 现在的我反而经常直接用 float,因为"方便"。 **三、错误处理比我现在写的还全。** 每个数据库操作后面都有错误检查,每个外部调用都有超时和重试,每个 catch 里都有日志。虽然啰嗦,但**出问题的时候真的能查到**。 我现在写代码有时会觉得"这个不会出错",然后就不写了。 ## 一些理解 **关于"当时为什么那样写"** 现在我能回忆起一些具体的处境: - 那个 380 行的函数,是因为需求一天改三次,每次拆完函数就又要改结构,索性不拆了 - 那些复制粘贴,是因为当时有个上线时间点,重构要两小时,复制只要十分钟 - `fix_20210315.php` 里的 cron,是因为修完之后发现还有历史数据要处理,临时加了个 **大部分烂代码不是能力问题,是处境问题。** 这不是给烂代码找借口,而是说:如果现在的我处在那样的压力下,可能也会做同样的选择。 **关于"我现在是不是也在写烂代码"** 大概率是的。只是我现在看不出来——就像五年前的我也不觉得自己写得烂。 有些东西要隔几年回头看才看得出来。可能是命名,可能是抽象层级,可能是某个当时觉得聪明的设计。 **关于"该不该重写"** 我看了半天,最后**没重写**。理由: - 它在正常工作,重写的风险大于收益 - 业务本身可能一两年内会下线 - 重写之后,两年后的我大概也会觉得新代码烂 但我做了几件小事: 1. 给那个定时任务加了日志和告警(之前失败是静默的) 2. 把 `fix_20210315.php` 的逻辑合并到正常代码里,删掉了那个文件 3. 金额相关的代码加了几处断言和单元测试 4. 写了一份 README,说明各个"奇怪"的地方为什么是这样 四个小时的工作,风险很小,收益是后来接手的人(可能是未来的我)少踩几个坑。 ## 一个具体的安慰 五年前那个项目里,有一段我自己写的日期处理,用了 `strtotime` 加各种边界判断,二十多行。 现在我会用 `DateTimeImmutable` 三行写完。当时的我看现在的写法大概也觉得"这什么鬼,看不懂"。 **进步是看得见的,只是需要时间间隔才显现。** ## 最后 写代码这件事,很难在当下判断自己写得好不好。 能做的大概是: - 尽量留下"为什么"而不只是"是什么"(注释、commit message、决策记录) - 在压力过去之后,回来收拾一下 - 接受自己现在写的代码,两年后看起来也会很蠢 我现在的习惯是,每个功能做完之后过一周,回来读一遍自己的代码。隔一周还能发现问题,隔一年就是另一种体验了。
#随笔#成长#代码
cover
✨ 思考
2026-04-23 22:40:00

关于技术选型,我现在的判断标准

刚开始工作的时候,选技术主要看哪个"更先进"、社区更热、面试更常问。现在标准完全变了。 这条听起来很不技术,但影响最大。 我们去年要给一个服务选消息队列。候选是 Kafka 和 RabbitMQ。Kafka 吞吐高、生态好、业界主流,从技术指标上完胜。 但我们团队没人上过 Kafka 生产环境。Kafka 的运维复杂度在于分区再平衡、消费者组管理、磁盘规划、监控指标解读,这些文档里不会细讲,只有踩过才知道。 最后选了 RabbitMQ。吞吐对我们够用(峰值每秒几千条),而且有人用过。 后来有次消费端积压,从发现到定位到解决用了四十分钟。如果当时是 Kafka,我怀疑要搞一整天。 **技术选型里,"团队会不会用"这个权重,比大多数人给的高。** ## 二、看"出错的时候好不好排查" 我现在的评估清单里有一条:**这个技术出问题时,我能不能快速知道发生了什么。** 具体地问: - 有没有清晰的日志? - 有没有能看内部状态的手段(状态页、pprof、metrics)? - 错误信息是"something went wrong"还是能指出具体原因? - 社区里搜错误信息,能不能搜到有用的答案? Kubernetes 就是个典型例子。它的能力很强,但出问题时那个错误信息经常让人摸不着头脑。相比之下 systemd 的日志清楚得多。 这不是说 Kubernetes 不好,而是说**它的学习成本要算进选型成本里**。 ## 三、看"如果我要换掉它,成本多大" 没有技术能用一辈子。所以我会问:**它是不是把我和别的东西绑死了?** 好例子:用标准库的 `database/sql` 接口,换数据库驱动只改一行;用某个 ORM 的专有特性,换 ORM 就要重写查询层。 好例子:业务逻辑写成不依赖框架的普通类,换框架的时候能直接搬走。 我的做法是**在边界上做隔离**: ```php // 定义自己的接口 interface CacheStore { public function get(string $key): ?string; public function set(string $key, string $value, int $ttl): void; } // 具体实现包一层框架 final class RedisCacheStore implements CacheStore { /* ... */ } ``` 多写几十行,但换来的是"换掉 Redis 只改一个类"。 ## 四、看"这块业务以后会变得多快" 需求变化快的地方,选**改起来快**的技术。 我有个运营后台,一年改了三十多个需求,平均每个需求改动不超过一天。它用的是最"土"的 PHP + 模板,没有任何前端框架、没有前后端分离。 同期另一个项目用了前后端分离 + SPA,虽然技术上更"现代",但每次改个字段要动三个仓库、走两次发布。运营那边抱怨了很久。 **需求稳定的地方,选"跑得省心"的技术。** 反过来,需求天天变的地方,别追求架构优雅,追求改得快。 ## 五、警惕"以后可能会需要" 这是我最常犯的错误。 有个项目一开始就按"以后要支持百万用户"设计:分了服务、上了消息队列、做了读写分离、加了缓存层。结果两年过去,日活三千。 那些为了扩展性付出的复杂度,全部变成了维护负担。而且因为业务方向变了,有些服务压根没用上,删又不舍得删。 **现在的原则:为未来预留接口,但不为未来实现。** 比如:数据访问包一层接口(预留),但先不实现多数据源(不实现)。等真的需要了,替换实现就行。 ## 六、看"这个决定可逆吗" 可逆的决定可以快,不可逆的决定要慢。 **可逆的**:选哪个日志库、用哪个 HTTP 客户端、目录怎么分。这些改起来成本低,凭直觉选,不合适再换。 **不可逆的**:数据库选型、数据格式、对外 API 的形状、核心领域模型的划分。这些一旦定了,改起来动全身。 我在可逆的决定上花的时间变少了,在不可逆的决定上花的时间变多了。 ## 七、一个具体的反例 有次要给一个内部工具选前端方案。我花了三天比较 Vue 和 React 的生态、性能、学习曲线,最后选了 Vue。 结果那个工具总代码量不到 2000 行,只有三个页面,用哪个框架根本没区别。真正花时间的部分是我调 CSS 布局。 **决策成本应该和决策影响成正比。** 现在我遇到小决定,会给自己设个时间上限(比如 15 分钟),到点就选。 ## 现在的清单 写下来大概是这七条,按我实际使用的权重排: 1. 团队有没有人会(权重最高) 2. 出问题了好不好查 3. 换掉它的成本 4. 这块业务变化快不快 5. 是不是在为想象中的未来买单 6. 这个决定可逆吗 7. 技术指标(性能、生态、活跃度) 性能排在最后不是因为它不重要,而是因为**大部分项目根本到不了性能瓶颈**。真到了,再针对性优化。 ## 最后 有句话我挺认同:**架构决策的代价,往往在决定之后的两年里慢慢显现。** 技术选型也是。选的时候觉得无所谓的东西,两年后可能变成"当时怎么想的"。 所以我现在做稍微重要一点的选型时,会写一段决策记录:当时有哪些选项、为什么选这个、假设是什么、什么情况下应该重新评估。 那个"假设是什么"很重要——它让你以后能判断这个决定还成不成立。
#随笔#架构#思考