Glean 拾遗
← 所有期号
#017 最新 9/14–9/20 9 月 20 日发布

什么值得留下

本周十四篇文章,几乎都在回答同一个问题:什么经得起时间的消耗。Fowler 论证内部质量不是开销而是投资,技术债被拆成审美、可延期与毒性三类,'好代码容易删'则把代码行数重新定义为花掉的行数——变更的成本,才是软件的真实成本。往下是抽象与基础的拉锯:非平凡抽象终会泄漏,数据库这门老本行被当成加分项,微服务剥开后是 1971 年的模块。代码终究写给人看:注释的分类学、像对待人一样的评审、提问的技术与文档优先,构成一套协作的手艺。最后三篇回到人:二十年经验的边界、产品意识的养成、开源门槛的升降。

14 篇 4 章 约 3 小时
章节 01

变更是有价格的

3 / 14
martinfowler.com · 15 min
01

内部质量不是成本:高质量软件反而更便宜High Internal Quality Makes Software Cheaper, Not More Expensive

Martin Fowler 反驳了一个常见取舍:花时间打磨软件质量,还是尽快交付功能?他把软件质量拆成用户可感知的 external quality(界面、缺陷)和用户看不见的 internal quality(架构、命名、模块划分)。用户愿意为前者多付钱,却无法判断后者,于是内部质量常被当成可以砍掉的开销。Fowler 的论点是:internal quality 的作用是降低未来每次改动的成本,其成本实为负值。他用累积功能量对时间的伪曲线说明,低内部质量的项目初期推进快,随后 cruft 迅速堆积,改动越来越慢;他访谈的资深开发者表示,劣质代码数周内就会显著拖慢进度。文章也承认顶尖团队同样会产生 cruft,差别在于他们用自动化测试、频繁 refactor 和 continuous integration 把它压住。作者坦承软件产出无法测量,因此结论依赖经验判断而非数据。适合需要向管理层解释重构价值的工程师。

renegadeotter.com · 8 min
02

兰尼斯特有债必偿:先分清三种技术债再动手A Lannister Always Pays His Technical Debts

作者把技术债分成三类:审美债(只犯强迫症,不影响用户和交付速度,交给自动化代码分析就行)、可延期债(范围可控,需要在 sprint 里逐步销账)、毒性债(半成品变成 workaround 磁铁,新功能一层层叠在上面,越拖越大)。毒性债的两个典型来源是缺测试和缺文档:没有集成测试兜底,就不敢对代码动大锤;没有 README 和 runbook,每次复现 bug 都要重新逆向一遍系统,这是有实际美元成本的时间损耗。作者还主张 TODO 注释基本等于永不做,应该把它变成看板上的正式卡片,并给债务故事打标签,让团队能看清新功能与债务的比例。债务列永远不会空,但也不能失控增长。适合一线工程师和技术负责人。

programmingisterrible.com · 20 min
03

好代码容易删掉,而不是容易扩展Write code that is easy to delete, not easy to extend.

作者主张把代码行数视为“花掉的行数”而非“产出的行数”:每行代码都要持续支付维护成本,而为提高复用率构建的抽象会把调用方绑死在实现的显式与隐式行为上,让后续变更代价更高。因此目标应是可删除(disposable),而非可复用、可扩展。文中给出递进策略:能不写就不写;先复制粘贴几次再提取函数;把无状态、与应用无关的代码放进 util 并一工具一文件;接受 boilerplate 换来的灵活性;像 requests 包住 urllib3 那样把 policy 与 protocol 分层;允许业务逻辑先做成一坨泥巴;按“不与谁共享”而非功能拆分模块;用统一接口、HTTP 缓存与 CDN、feature flag 制造可替换点;错误处理放在端到端的最外层,如同 Erlang 监督树用 fail-fast 重启替代就地恢复。适合维护长期演进代码库的工程师与设计者。

章节 02

抽象之下的地基

4 / 14
www.joelonsoftware.com · 12 min
04

抽象必漏:TCP、SQL 与 C++ 字符串的同一课The Law of Leaky Abstractions

Joel Spolsky 从 TCP 讲起:TCP 承诺可靠、有序、不损坏的传输,底层却是会丢包、乱序、损坏的 IP,靠重传和重排把不可靠伪装成可靠——这就是抽象。文章的核心论断是:所有非平凡的抽象都会泄漏。二维数组按行还是按列遍历,缺页次数可能差出几个数量级;逻辑等价的 SQL 多加一句 a=c 可能快上千倍;C++ string 类再努力也写不出 "foo" + "bar",因为字面量永远是 char*;NFS 挂载的 home 目录在服务器宕机时会让 .forward 邮件直接丢失;ASP.NET 用一段 onclick JavaScript 假装超链接能提交表单,用户禁用 JS 就全线失效。结论对工程师有实际后果:抽象省下写代码的时间,不省学习的时间;工具层次越高,出问题时越需要往下钻。适合做系统、工具链和框架的一线工程师。

renegadeotter.com · 14 min
05

数据库技能不是加分项:从 2006 年 MySQL 分面搜索说起Your Database Skills Are Not 'Good to Have'

2006 年,作者在纽约杂志数字团队用 MySQL 4 加 Perl 给时装周做分面搜索:秀场图按「2006」「bag」「red」等标签分类,用户可下钻筛选,每个属性还要带精确计数。当时 Solr facets 尚不存在,Autonomy 的计数不对,Endeca 刚出隐身期,三个人的团队只能自己啃 SQL,靠 EXPLAIN、GROUP BY 和反复调 MySQL 服务器参数把延迟压下去。 二十年后作者看到的却是相反趋势:工程师给普通规模的问题上 DynamoDB 这类「行星级」数据库,却对自己正在用的关系库缺乏基本掌握。文中复述一次电商事故——商品列表页要 10 秒以上,且无流量时一样慢,同一页面同时踩了缺索引、ORM 循环逐条查询(单页 200–500 条 SQL)、SELECT 全部列三个坑。作者的主张是:现代 RDBMS 在被证明有罪之前都是清白的,举证责任几乎全在工程师身上;文末给出排障顺序(慢查询日志 → 高频查询 → EXPLAIN → 只取必要列 → 必要时写原生 SQL)与三类反模式。适合后端与数据工程师。

kevinmahoney.co.uk · 9 min
06

写软件的十一条原则:先数据,后代码My Principles for Building Software

作者列出自己构建软件时遵循的一组原则,主线是让系统更简单:让非法状态无法表示、保证数据一致性、先设计数据再写代码、测量之后再优化。文中用一个数据库例子说明不一致的代价——必须保持 x=y 的两个布尔变量一旦拆到不同库、无法原子更新,数据就多出两种状态,toggle 函数在这些状态下没有正确答案。作者认为一致性是当下最被低估的工程原则,多数问题本质上都是数据不符合预期;他同时主张代码一致性优先于局部「正确」,学习应聚焦 concepts(关系模型、代数数据类型、borrow checker、Curry-Howard 同构)而非 React、Kubernetes 的表层细节。适合做后端与数据系统设计、正在权衡微服务拆分和 schema 取舍的工程师。

blogs.newardassociates.com · 15 min
07

拆开微服务,里面是 Parnas 1971 年的模块You Want Modules, Not Microservices

Ted Neward 逐条拆解微服务的卖点:文中引用的六条好处,两条来自微服务文献,两条来自二十年前的 EJB 宣传,两条来自四十多年前的 Oracle Tuxedo 文档。剥掉包装后剩下的是 1971 年 Parnas 论文里的“模块”——独立构建、版本化、部署、可复用的代码单元;CLR 的 assembly、JVM 的 JAR、操作系统的动态库都是它,Unix 的 pipes-and-filters 早在 70 年代就在讲同一件事。作者认为企业真正买到的是组织清晰度:小团队自己掌握分析、测试、数据、部署等依赖,不再被 DBA、QA、基础设施团队阻塞,代价是团队必须具备全套技能并承担 on-call。技术代价则是把进程内模块调用换成跨网络调用,延迟增加五到七个数量级,并且撞上分布式计算的谬误——加节点只会让它更糟。结论:模块或独立进程加统一 API 约定即可,组织依赖要在组织层面解决。适合架构师与技术负责人。

章节 03

写给人看的代码

4 / 14
antirez.com · 31 min
08

antirez 拆解 Redis 源码:代码注释的九种类型antirez on code comments: a nine-part taxonomy from Redis

antirez 以 Redis unstable 分支(32e0d237)源码为例,把代码注释拆成九类:function、design、why、teacher、checklist、guide 六类有益,trivial、debt、backup 三类可疑。他反驳“代码够好就不需要注释”的常见观点,理由有两条:注释不是在复述代码做了什么,而是补上单读局部代码拿不到的信息(为什么这样做、为什么不选看起来更自然的写法);注释也是降低读者认知负荷的工具,scripting.c 里逐行标注 Lua 栈布局即是例证。文中逐类给出判定标准与取舍:design 注释让简化方案显得出于考量而非偷懒;teacher 注释扩大能读懂这段代码的人群;checklist 注释源于 4 bit type 这类无法中心化的设计;debt 注释(TODO/FIXME)应尽量挪到文件顶部或当场修掉;backup 注释在 Git 时代没有存在理由。适合长期维护大型系统代码库的工程师。

vadimkravcenko.com · 11 min
09

把知识写下来:一位 CTO 的文档优先实践Healthy Documentation: A CTO's Case for Docs-First Engineering

一位创业公司 CTO 复盘自己推行“文档优先”的整套做法:用一页纸代替半小时站会、把文档工时显式写进排期、给每个 feature 合并时附一段“为什么选 X 不选 Y”。他也承认过度文档会反噬(过时页面比没有更糟),因此只记录能活过一个季度的 80% 内容,并用一条轻量 CI 检查强制新埋点必须配 wiki 条目。文中给出 ADR、incident post-mortem、Diátaxis、docs gardener 等具体机制,适合正在搭建工程规范的中小团队负责人与一线工程师参考。

mtlynch.io · 23 min
10

代码评审不只是找 Bug:像对待人一样给反馈How to Do Code Reviews Like a Human (Part One)

Michael Lynch 指出,多数代码评审文章只关心「找 Bug」,几乎不谈如何把问题讲清楚,结果把评审变成对作者的人身评判。本文把评审同时当作技术过程与社交过程,给出一组可直接落地的做法:把空白格式、构建、测试、lint 等机械检查交给 CI 和 formatter;用 style guide 终结风格争论;收到 changelist 后立即开始评审,单轮返工上限一个工作日;单轮批注控制在 20-50 条以内,先给高层设计意见再抠细节;用可运行的代码示例代替口头说明;批注里避免出现 "you",改用 "we"、省略主语或改写为问句;把命令改成请求;把每条意见挂到具体原则上并附上风格指南或文档链接。适合正在设计团队评审流程、或想改善评审沟通方式的工程师。

jvns.ca · 13 min
11

提问的技术:先说清你已知什么,再问可回答的事实How to ask good questions about software

Julia Evans 认为提问是一项可以练出来的工程技能,并把方法拆成可操作的几步:先陈述自己对主题的现有理解,再问『这样对吗』;把『SQL join 怎么工作』这类宽泛问题改写成答案明确的事实问题(例如 join N 与 M 两张表的时间复杂度是 O(NM) 还是 O(NlogN)+O(MlogM),MySQL 是否先排序 join 列)。她给出的实例包括:在 rkt-dev mailing list 上先写清 rkt 与 Docker 在磁盘上存容器镜像的差异,再问为什么这样设计;入职数据团队时给 Hadoop、Scalding、Hive、Impala、HDFS 等术语建了一份词典。文中还讨论了如何选人提问(对方答一题 5 分钟、能省自己 2 小时才划算;不必事事找最资深的人)以及提问者与回答者共同承担的责任。作者明确表示问『笨问题』无妨,也批评 ESR《提问的智慧》把全部成本压给提问者。适合正在 ramp-up 的一线工程师与需要跨团队取信息的人。

章节 04

判断力与门槛

3 / 14
www.simplethread.com · 14 min
12

20 年工程生涯的 20 条经验,作者先标注了适用边界20 Things I've Learned in my 20 Years as a Software Engineer

Simple Thread 联合创始人回顾 20 年工程生涯,先交代自己的经验边界:前半段在小公司和创业团队,之后做咨询接触大企业,再把公司从 2 人带到 25 人,因此这些判断都带有『人少、要交付、还要维护老系统』的前提。文中 20 条多半反直觉:最难的是做对的东西而非写好代码;最好的代码是不用写的代码;系统终将变烂,该追求可持续改进而非完美;10x 程序员是神话,真正该做的是别让 0.1x 程序员进团队;数据比代码库活得更久;面试几乎无法预测一个人是否是好的队友;技术选型要看存活多年的『鲨鱼』而非新潮工具。适合希望校准自己工程判断的一线开发者,也可当团队讨论素材。

blog.pragmaticengineer.com · 12 min
13

产品意识型工程师的 9 个特质与养成方法The Product-Minded Software Engineer: 9 Traits and How to Grow Them

作者把「产品意识」拆成可观察的行为:这类工程师不满足于拿到 spec 就开写,而是先追问 why,主动挑战需求,并同时评估产品与工程两侧的取舍。文中给出 9 个特质,包括主动接触业务与用户数据、与非工程角色建立关系、用「最小可爱产品」标准处理 edge case、上线后持续追用户行为指标直到得出结论。作者认为产品直觉来自反复的项目循环——提问、给方案、快速验证、复盘落差、再提问——循环越多,建议越准,最终成为 PM 在立项前就会来问的人。面向做用户侧功能、与 PM 协作的工程师,末尾附了 6 条可执行的练法。

stitcher.io · 5 min
14

Laravel 关掉部分仓库的 issue,改要 PR:门槛该升还是降No More Issues: Laravel Asks for PRs Instead

Laravel 在 Socialite、Scout 等包仓库关闭了 issue 创建入口,改为引导贡献者直接提 pull request,主框架仓库暂未受影响。作者以开源维护者身份逐条列出顾虑:强制提 PR 确实能省下维护者的 triage 时间,但在 AI 辅助下提 PR 的成本已接近提 issue,而 LLM 生成的补丁需要更多审查精力;同一个 bug 引发的重复 PR 还会让 GitHub Actions 反复触发,这一点 issue 不存在。更关键的是,要求 PR 是把贡献门槛抬高而非降低,会用不起 AI 工具、或还没积累足够经验的开发者排除在外——而作者自己当年正是从给 Laravel 提 issue 起步的。全文没有硬结论,只把 trade-off 摆出来,适合关注开源维护、issue/PR 流程与 AI 编码影响的一线工程师阅读。