想法与洞见

探索 AI 优先开发、编码护栏和可处置架构。

你的 Gherkin 规格说明正在对你撒谎

一旦你指望人工手动维护,Gherkin 规格说明就会立刻与现实脱节。本文介绍如何通过自动化检查,让 feature 文件始终如实反映系统行为。

你的 Gherkin 规格说明正在对你撒谎。 并非故意。它们起初是忠实的。但六个冲刺之后,有人重构了结账流程,却忘了更新 这一步。 文件仍然通过,因为 step definition 依然存在。只是它调用的代码早已不再与场景实际描述的行为相符。你拿到了一片绿的测试,以及虚假的安心。这就是 BDD…

编译器检查语法,测试应该检查架构。

大多数团队把架构规则写在维基里。这里介绍如何把它们写成可执行的测试,当你的依赖图发生漂移时让 CI 挂掉。

你的测试套件验证了 在输入正确时返回 42。但它没有验证 是否被允许导入 。编译器对两者都满意。你的单元测试对两者都满意。但其中之一是架构违规,六个月后会让你花上一周时间来重构。 这就是盲区。我们为逻辑写测试,却假设结构会自行管好。并不会。…

你的领域层引用了 Postgres。你的 CI 却视而不见。

整洁架构的图在白板上看着很美。本文介绍如何在构建流水线中强制执行依赖方向,让领域代码永远无法触及基础设施。

团队里有人刚在 里引入了 。PR 编译通过。测试全绿。代码审查长达三百行,却没人发现。 三个月后,你想把领域逻辑抽成一个共享包。做不到。它依赖了 Postgres 的类型、连接池逻辑,以及一个只在单体应用里存在的自定义驱动封装。白板上那套同心圆架构图,此刻成了笑话。 这就是没有自动化 enforcement…

你的架构图早就是谎言了

架构文档在你保存的那一刻就开始腐烂。以下是如何利用代码生成图表、ADR 和自动化架构测试来保持文档的诚实。

我在 wiki 里见过的每一张架构图都是错的。不是那种惊天动地的错,而是悄无声息、日积月累的错。标着 "Auth" 的服务六个月前就被拆成了三个微服务。标着 "sync call" 的箭头现在已通过队列变成了异步调用。标着 "PostgreSQL" 的数据库在一次紧急故障处理中被迁移到了别的系统,却没人更新那个方框。…

你的重试循环假设第一次请求失败了。大概率并没有。

超时或崩溃并不意味着你的 API 请求已经丢失。本文介绍幂等键(idempotency key)如何让重试变得安全,以及真正防止重复请求的数据库存储模式。

你的服务在处理 请求时中途崩溃。客户端看到超时,于是重试。结果出现了两笔扣款。客户很生气。数据库是一致的。但业务逻辑不是。 这不是边缘情况。这是分布式系统的默认行为。网络丢包。容器在请求中途被 OOM 杀掉。负载均衡器对已经到达后端的请求返回 502。如果你的 API…

比进程更长寿的锁:分布式租约的真实工作原理

内存中的互斥锁在服务器重启后随即消失。本文介绍带有 fencing token 和 TTL 的分布式租约如何防止崩溃后的重复执行,以及它们在哪里仍然会失效。

你的 无法挺过 。它也无法挺过 OOM、部署滚动更新或节点重启。进程退出的瞬间,锁就消失了。如果这个锁正在保护一个定时任务、数据迁移或主节点选举,那么你现在会有两个进程都坚信自己是唯一在运行的那个。 这不是你的互斥锁有 bug。这是一个范畴错误。进程本地的锁无法保护集群范围的资源。…

无 goroutine、无定时器、无后台开销的熔断器

大多数熔断器库都会启动后台线程来探测恢复。你根本不需要它们。本文介绍一种请求驱动的设计,在消除所有后台开销的同时,不牺牲正确性。

我审查过的每一个生产环境熔断器,最终都会拉起一条后台线程。它可能是 Go 的 goroutine、Java 的 ,或是 Rust 的 tokio task。干的事情永远一样:每隔几秒唤醒一次,检查下游服务是否已经恢复,然后把状态从 OPEN 切回 CLOSED。…

你的 Web 服务有一条优雅关闭路径。这就是 Bug。

Crash-only 软件将每一次失败视为崩溃,将每一次启动视为恢复。对于 Web 服务来说,这意味着删除你的关闭逻辑,并设计出能在 kill -9 下存活的状态。

你的 Web 服务有一个关闭处理器。它会刷写缓冲区、关闭连接、写入检查点。也许你曾经测试过一次。在生产环境中,它可能只在每年一次的有计划部署时才会运行。其余时间,你的服务死于 OOM kill、节点驱逐、断电,或是超时后被 SIGKILL 的部署。 Crash-only…

看不懂突变改了什么,怎么写测试杀死它?

mutation testing 发现了一个 survivor,但你根本不知道这个 mutation 做了什么。这里有一个分步方法,让你不用先理解 mutant 也能写出正确的测试。

你的 mutation testing 报告里全是 survivor,其中至少有一个你完全看不懂。 工具说它把第 47 行的 换成了 ,或者把整个条件块替换成了 ,又或者变异了一个你根本不知道正在被测试的字符串字面量。你 diff 看了三遍。你还是不明白这个 mutant…

认证代码需要 90% 的变异覆盖率。你的字符串工具函数不需要。

为什么在整个代码库强制执行单一变异分数是错误的,以及如何根据实际风险设置按模块划分的阈值。

在整个代码库强制执行单一变异分数,是让团队厌恶测试的绝佳方式。 用 PIT 或 Stryker 跑一个典型仓库,你会看到同样的模式:认证模块得 40%,字符串工具类冲到 95%,ORM 层则在 60 多分徘徊。本能反应是设一个全局门槛,比如 70%,堵住所有低于它的 PR。两个冲刺之后,就会有人在 CI…