Consort:分支数据库上的测试驱动开发
DataHot 速览
本文作者回顾25年软件开发实践,探讨如何用 Lakebase Postgres 的分支数据库实现测试驱动开发。通过写时复制分支,开发者可在内循环中用真实数据库运行测试,免去 mock 和共享环境的负担。schema 随版本化迁移与代码一起作为一个单元演进,破坏性测试也能安全进行。文章介绍了从传统数据库工作流向数据库分支开发模式转变的方法。
为什么值得关注:数据库分支能让开发测试基于真实数据而非 mock,对数据平台工程和研发流程改进有直接借鉴价值。
译文
AI 逐段翻译25年来,我一直在用伴随我成长的实践来构建软件:Kent Beck的TDD、Martin Fowler的重构、Uncle Bob的整洁代码、Jez Humble和Dave Farley的持续交付,以及Pramod Sadalage和Scott Ambler的进化式数据库设计。
借助现代软件开发实践,代码可以分支,环境得以容器化,基础设施也变成了代码。但技术栈中有一部分从未配合:数据库。它像一块僵硬的巨石一样坐在那里,我们不得不接受许多变通做法,例如用模拟对象代替真实对象、共享staging环境、架构变更需小心谨慎地执行。
我一直在与软件开发团队讨论Lakebase Postgres,以及它如何改变我们过去的做法。你可能听我说过:像分支代码一样分支数据库。但这在实践中意味着什么,我又该如何应用呢?
数据库分支带来了什么变化
集成测试回归内部循环
过去,针对真实数据库的测试被视为外部循环的问题。搭建数据库、填充schema和数据、对schema进行版本控制,并且为每个单元测试都这样做,成本过高,所以我不这么做。没人这么做。我们转而针对模拟对象编写单元测试,不是因为模拟对象好,而是因为在内部循环中使用真实数据库遥不可及。我们知道模拟对象会随时间漂移;它们与数据库实际行为脱节,你维护得越多,它们验证的真实行为就越少。
写时复制分支消除了模拟对象存在的理由。你可以创建真实数据库的隔离分支,时间大致恒定,与数据大小无关。我写的第一个测试,采用TDD风格,在真实数据的实时分支上运行,而不是使用伪造品。我可以运行破坏性测试,把所有东西都炸掉,搞乱schema,却不会影响团队其他人正在使用的分支。这是我自己分支。完成后,我就把它扔掉。
代码与schema同步交付
由于schema现在以版本化迁移(如Alembic、Flyway或Knex,按技术栈选择)的形式流动,schema变更及其所依赖的代码,作为单一单元一并移动。你合并schema(而非数据),代码与之对齐。我向两个不同团队的两名工程师展示了这一点,他们不约而同地用同一个词来形容:数据持续交付(Data CD)。
凌晨两点的生产事故在发生前就被捕获
当生产环境出问题时,通常在凌晨两点,你在反向工程什么变了。分支改变了时机。在每次拉取请求时,以及在合并时再次创建,你从目标环境创建新的数据库分支,运行迁移和完整测试套件,在部署前发现回归或冲突。schema变更作为迁移包含在拉取请求中,因此你的DBA可以作为代码所有者在那里审查,而不是向下游队列提交工单。
过去,提升schema变更曾是一场高风险仪式。现在你分支生产环境,应用变更,隔离测试,然后提升。在普通的云Postgres设置上做同样的事情,需要一堆脆弱的DevOps脚本,浪费宝贵的人力时间。
这些都不是你可以直接开启的数据库功能。这是一种转变,关于何时进行艰难的测试,即尽可能提前,在你真正编写代码的循环中。
具备数据库分支的智能体开发框架
既然分支的基础设施已经具备了,讨论中缺少的就是让它真正在开发循环中发挥作用的东西。这就是我构建Consort的原因。
Consort是一个开源的智能体开发框架,利用Lakebase分支作为其测试驱动构建的基础。如果你熟悉Scrum,这个想法从名字就能看出来。团队中每个角色(产品负责人、规格编写者、架构评审者、DBA、测试策略师,以及导航者/驾驶员配对)都成为一个智能体。他们在指挥者的领导下协同工作,最终像一个整体乐团的合奏。
工作分两条轨道进行:一条是先规格后设计,在此确定并冻结意图;另一条是构建轨道,在真实数据库的实时分支上运行完整的红/绿/重构循环。当编写代码的智能体时,一些组件使其能够保持稳定。
- 智能体得到真正的边界去击破。给智能体具体的东西,它就能解决问题,因为当代码无法工作时它能察觉。给它封闭隔离的模拟测试,它会高兴地交给你看似通过、但一旦在真实数据库上运行就会崩溃的东西。真实数据的实时分支就是具体边界,智能体无法假设绕过实际存在的数据库。
- 驾驶员不能移动球门柱。负责编写代码的角色不能为了让测试通过而修改测试。唯一例外是真正的取代,即后来的故事合法地废弃旧测试。绿色意味着测试在真实数据库分支上运行并通过,而不是因为智能体声称它通过了。
- 指挥者是代码而非智能体。团队中称为scrum master的角色变成了确定性状态机。它路由工作,持有需人工批准的门控,当发生冲突时重新路由到负责任的角色,从而确保推动循环的过程不会被打乱或迷失方向。
- 它不会失去使命。关于其他框架,我最常听到的抱怨是几天后智能体丢失使命,你会感到沮丧,然后重新开始。Consort将每个特性的工作产物保存在专用目录结构中,智能体将其视为角色间契约来读取。这正是真实团队进行的交接:每位专家审查工作,添加自己的关注点,然后传给下一位。
作为一种优化策略(这样你的智能体就不会在每轮开始时重新扫描所有内容),Consort 为每个智能体提供一份限定范围的上下文包,包含需要通过的精确测试、设计需求以及这些测试所在的位置,而不是让它在整个代码库中自由行动。无范围访问会导致智能体偏离方向,并花费大量时间在代码中挣扎,试图弄清楚该做什么、代码应该放在哪里,直到它们忘记 DRY 原则是什么。
架构师在任何代码编写之前审查规格说明,并融入你所关心的设计,包括跨功能需求、分层、DRY、SRP 和 SOLID 等模式。这种前置设计就是为什么你不会最终把所有内容都放在一个文件里。当你想进行探索时,Consort 会为一个故事并行运行实验,每个实验都在自己的数据库分支和工作树上完全隔离,这样你可以尝试多种方法并保留更好的实验。当我向其他工程师展示这一点时,他们往往会立刻意识到这正是他们一直试图自己构建的东西。
你可以观看所有过程,并引导方向。一个 VS Code 插件显示跨层的配对的 Git 和 Lakebase 分支,代码和架构变更在单一差异视图中显示。一个可观察性仪表盘实时显示每一步:每个角色、其提示以及为下一个角色产生的工件。在每个门禁点,你本人检查运行在自己的分支上的工作软件,然后批准它继续前进。
Consort 是什么(以及不是什么)
Consort 是开源的,由社区支持。它是一个你可以采用、运行和推荐的项目,具有自己的生命周期。它不是捆绑的平台功能,也没有 SLA。
你不必重新连接你的技术栈来使用它。Lakebase 就是 Postgres,所以你的最终应用可以直接运行在它上面,无需分支流程;配对的 Git 和 Lakebase 分支是你在开发过程中做的事情。
25 年来,数据库一直是我们技术栈中无法分支的部分。现在它可以了。这就是改变,也正是我构建 Consort 的原因。
亲自尝试
你只需要 Lakebase。在免费版中获取。
技术栈的其余部分是开放的——Java、Python 或 Node——它运行在 Claude 上,并配有 VS Code 插件伴侣。
当你准备好时,只需三个命令:
/consort:start 引导你进入项目。从 StockFlow 示例开始。它从三个文件播种,你可以看着它从那里成长为一个完整的代码库。
软件构建的(r)演进
如果这项工作激励了你,你想让它变得更好,我正在寻找更多的贡献者和代码所有者。如果你想加入,或者只是看看内部工作原理,访问仓库: https://github.com/databricks-solutions/consort
这篇内容对你有用吗?
反馈只用于改善内容筛选,不等同于收藏