跳到正文
Hacker News· roblaszczak·· 5 天前AI 评分22

AI 写代码时代,DDD 为何更重要

When AI writes your code, DDD is more relevant

AI 导读

在 AI 智能体承担越来越多编码工作的背景下,Domain-Driven Design 的核心思想反而更有价值:理解并建模业务领域仍是软件工程最难的部分,AI 生成的领域模型文档无法替代团队共同理解问题。实现细节因 AI 工具而变得次要,但领域复杂性依旧需要团队协作攻克。

正文 · AI 翻译

无论你怎么看待 AI 编程,我们构建软件的方式正在改变。 每个人都想知道:“什么会保持相关性?” 我们有的只是观点,所以这是我的: 领域驱动设计背后的大多数理念如今比以往任何时候都更具相关性,因为 DDD 从来就不严格地关乎代码。 随着我们用智能体做更多的编码,我们仍然需要理解领域、对其进行良好的建模,并作为一个团队协作。 让我们看看 DDD 还能教给我们什么,以及 AI 无法取代什么。

照例免责声明:我写的是那些维护数月乃至数年的复杂项目,团队往往在这些项目中苦苦挣扎,而 DDD 正是在这些地方大放异彩。 在业余项目或 CRUD 中,没有理由使用高级模式。

自 2003 年以来未曾改变的东西

Eric Evans 在二十多年前出版了《领域驱动设计》,但它至今仍出奇地新鲜。 这本书涵盖了许多主题,但核心信息是:软件工程中最困难的部分是理解问题领域,并在代码中对其进行良好的建模。 它呼吁工程师不要迷失在技术细节中,而要专注于他们正在解决的问题, 因为把这一部分做对更为困难。

相反,技术人才却去致力于精心构建的框架,试图用技术解决领域问题。 学习和建模领域的工作则留给了其他人。 软件核心的复杂性必须正面应对。不这样做就有被淘汰的风险。

我们现在看到,得益于 AI 工具,实现细节变得不那么重要了。 在一定程度上,你无需深入了解一门编程语言或框架就能高效工作,而且迁移到另一个技术栈也更容易了。 如果你试过顶级的编程智能体,你很可能见过它们快速生成像样的代码。

但了解高层次的软件概念对于引导智能体并判断输出是否合理仍然至关重要。 最重要的是,领域复杂性(你正在解决的业务问题)仍然是困难的部分。 好消息是,编码比以往任何时候都更容易,因此我们可以专注于这些复杂问题,而不是迷失在实现细节中。

只不过,我们不再痴迷于构建框架(或添加更多微服务,或任何其他时髦趋势), 而是现在追随模型基准,并优化我们的智能体配置,以更少的努力生成更好的代码。 正如 Evans 在 2003 年所指出的,我们仍然试图用技术解决领域问题。 有趣的是,这一次 CEO 们也相信这一点,并推动我们朝这个方向前进。

只剩下一个小问题需要弄清楚:这东西应该如何运作。 如果有人告诉我们,我们就可以把 token 砸向实现,然后收工。 如果没人知道,那么,也许智能体可以替我们写计划?

领域模型不是一件制品

DDD 的基础是模型驱动设计:将领域如何运作提炼为一个用代码表达的模型。 领域模型是一个抽象概念,没有唯一的方式来表现它。 你可以使用文档、图表和代码,但它们都是对领域如何运作的简化。 一个准确的模型来自在设计与实现之间来回切换,并反复应用你所学到的东西。

DDD 称之为知识消化。 你需要领域专家(了解业务如何运作的人)以及擅长构建软件的工程师。 他们共同协作,理解软件应该做什么以及如何实现。 (例如,在 Event Storming 会议中,开发人员和利益相关者用便利贴梳理业务运作方式的研讨会。)

弄清楚领域是团队协作的成果,也是艰苦的工作,因此很容易想找人代劳。 例如,AI 智能体在研究和分析方面表现出色,并且可以访问你的文档、聊天记录和代码。 看起来它们可以为你创建领域模型,然后还能实现它。

但这种天真的做法没有抓住重点。 在模型上投入工作的价值在于你(和你的团队)理解了领域是如何运作的。 AI 生成的一大堆文字并不能帮助你弄清楚业务问题。 智能体只会产出一份令人印象深刻却无人阅读的产物。

Three people sharing one thought bubble that holds a flowchart, a spreadsheet, code, sticky notes, and documents.

领域模型不是一份产物。

挑战从来都不是写一份关于某件事可能如何运作的长文档。 更常见的情况是,软件项目因其他问题而失败:

  1. 工程师在做错误的事情。
  2. 一开始就没人知道需要做什么。
  3. 团队试图一次性构建所有东西(范围蔓延)。

这是我多次掉入的一个陷阱。 在工程团队中,很容易觉得自己已经全搞明白了: 我们是编程专家,熟悉各种模式,遵循最佳实践。 我们只需要有人(业务利益相关者或产品经理)告诉我们该做什么。

但通常,领域专家也没有清晰的计划。 他们知道自己面临的问题,但还不知道解决方案是什么。 没有一份包含所有需求的完整文档。 开发者的第一反应可能是想:“等你想出点什么再告诉我,我随时都在。” 换句话说,我们只等着做编码中有趣的部分。 (注意,这正是 AI 智能体所自动化的部分。)

另一个极端是,非技术管理者试图想出完整的解决方案,然后交给团队去实现。 或者,最近的一种趋势是,有人用 AI 生成完整的功能或产品规格,甚至都不读一遍,就直接传下去。 所以计划算是存在了,但离可投入生产还差得远。 (如果有人声称不是这样,看看他们交付的东西到底有多复杂。)

在所有这些场景中,没有人清楚自己在做什么。 无论谁来写代码、写得多快,此时都价值不大。

相反,当你们一起在模型上工作时,你的团队理解了问题领域,并就解决方案达成一致。 开发者去实现它,而一旦出现意外情况或开始新一轮迭代,领域专家可以随时加入。

即使你用 AI 完成大部分编码,你也需要深入了解领域来引导智能体,就像引导一位队友一样。 你如何配置智能体、使用哪个 LLM,都不如对解决方案有清晰的心智模型重要。 如果非要选择,我宁愿自己一行代码都不写,也不愿跳过对需要做什么的思考。

先设计,再写生成代码

写代码一直比读代码容易。 现在智能体可以一次性为你生成一个大功能,这一点就更加极端了。 即使它能运行,你也完全不知道底层发生了什么。

我们现在更频繁地卡在代码审查上,但这也不是一个新问题。 它甚至让人感到熟悉,就像和一个独狼式开发者合作,他准备好完整的功能,然后丢给你一个巨大的 PR。 你对设计一无所知,而且常常存在没人考虑过的缺口。 审查这个 PR 很痛苦。

有一种方法可以避免这种情况,而且我在许多项目中都见过它奏效。 在坐下来写代码之前,先作为团队设计和讨论解决方案。 首先是领域部分:你在解决什么,以及它应该如何工作(参见上面的知识消化)。 然后是技术方面。 在你启动实现之后,就不太可能出现任何意外了。

你不需要在每一个微小细节上达成一致,也不需要一份完整的书面规格说明。 勾勒出一个所有人都知道的高层计划就足够了。 一些便利贴和一块白板就足够了。

A digital whiteboard from a design session, filled with sticky notes, event flows, architecture diagrams, and UI screenshots.

改变设计的最佳时机是在任何人写代码之前。

在这样的会议之后,审查 PR 就很顺畅。 如果你不在 PR 评论中讨论整个设计,就很容易把工作拆分成小块。 而且当功能上线到生产环境时,缺口也更少,因为你们已经就细节达成了一致,每个人都理解自己在构建什么。

跳过设计看起来像是节省时间。 没人喜欢更多的会议。 但如果你的团队第一次了解这个功能是从 PR 中,那么审查、讨论和修复所有问题会花费长得多的时间。 由于审查通常是异步的,你最终会经历很多轮评论。 代码审查应该是双重检查实现是否正确,而不是开始讨论这个方法到底是否合理。

Two timelines compared. Coding first means long stretches of code and review, while planning first ends sooner.

从一份简短的计划开始,可以为你以后节省审查时间。

在代码之前进行设计工作也会帮助你更好地与 AI 智能体协作。 如果你给智能体更多高质量的上下文,它的输出会更准确,编码部分也会更短。 而且你的团队对解决方案了解得越多,审查就越容易,即使所有代码都是你生成的。

通用语言:与你的智能体说同一种语言

最近,我一直在尝试削减我们的云成本。 这是 AI 的完美任务,因为有一个明显的指标可以衡量。 我告诉智能体找出我们的 Go 服务在哪里使用内存最多,并削减它。

经过几次运行,我节省了许多兆字节的 RAM。 但后来我意识到内存相当便宜,而这些更新每月只降低了 1 美元的成本。 不奇怪,对吧?我没有向智能体解释我是在试图削减成本。 我只是把内存使用量作为目标给了它。

这听起来应该很熟悉,因为这正是团队中人与人之间会发生的事情。 帮助你在团队中沟通的良好实践,在你与 AI 协作时也同样适用。 (谁会想到软技能可能帮助你与计算机打交道呢?)

DDD 通过一些关于你所使用语言的想法来解决这个问题。

第一个是通用语言:在团队之间和代码中说同一种语言,这样每个人都知道你在说什么。

在许多团队中,混乱的命名是默认状态。 在代码中,工程师使用的技术术语逐渐偏离了公司其他部分使用的名称。 即使你向 AI 提供文档和决策记录,智能体也需要理解你在那里使用的任何语言。

在知识梳理过程中,考虑你在领域中对这些概念的称呼。 最初,每个概念通常会有多个名称。 选择一个你已经在使用的名称,或者想出一个更准确的名称。 然后,在文档、口头交流和代码中使用它。

Sticky notes with competing names for one concept: User, Account, Profile, Customer, and a note saying TODO: Agree on names! An arrow points to the agreed result: Customer, Onboard Customer, Customer onboarded.

事件风暴(Event Storming)会议是选择名称的好场合。

你不能只选择一次名称,因为语言会随着项目不断演变。 这是你在设计会议和讨论中需要关注的事情。

另一个与通用语言(Ubiquitous Language)直接相关的观点是,在更大的系统中,你选择的名称不会在所有领域中都通用。 DDD 将每个这样的领域称为限界上下文(Bounded Context)。 你可以把客户在支持上下文中视为一个档案(profile),在电子商务上下文中视为一个用户(user)。 你应该在每个上下文内使用一致的名称,而不是在整个项目中强制统一。

这在更大的产品中尤为重要,当你需要在团队之间拆分软件时。 如果你过于专注于为所有类型的任务创建一个模型,就很难划定边界。 考虑到限界上下文,更容易得到一个解耦的系统。

智能体可以访问你的整个代码库,它们会寻找你给它们的名称。 它们可能会天真地试图统一相似的实体,所以要明确它们分开是有原因的。

A map split into four contexts: CRM has a Customer, Accounting has an Account, e-commerce has a User, and Support has a Profile.

同一个领域概念可以根据上下文有不同的名称。

如果你使用众所周知、精确的概念,就更容易表达你的意思。 这对你的队友和你的提示词都有效。

一个模糊的提示词会让智能体在错误的解决方案上花费大量精力:

在用户创建后,将其添加到 CRM 和支持系统

通过坚持使用精确的名称,你会让目标更加明确:

一旦用户在网站上注册,异步创建:1)CRM 中的客户条目,2)支持系统中的档案

你希望智能体的上下文中包含关于你领域语言的细节。 这是保持文档更新的又一个理由。 靠近代码的 Markdown 文件是一个很好的起点,因为你不需要自定义集成来获取文档。

开发者成为领域专家

模型和智能体不断变得更好,但你不能完全信任它们。 它们仍然会犯错,最终你需要有人来负责。 你怎么知道结果是否正确?

在领域中工作是你建立信任、证明你知道自己在做什么的方式。 设计系统并理解其工作原理的人可以告诉你某些事情是否可能出错。

开发者成为他们正在研究的主题的专家。 他们学到写作代码之外的技能,对于一个好奇的工程师来说,即使是一个复杂的业务领域,随着时间的推移也会变得直观。 如果你在那个领域需要帮助,你知道该问谁。 通过与一个你可以信任能完成任务的人合作,你省去了巨大的麻烦, 而不需要极其详细的指令。

最近有两种关于 AI 的叙事。 一种是现在任何人都可以生成一个知名应用的克隆。 这起初令人震惊,但从长远来看并不那么有趣。 即使没有 AI,我们已经有了太多半成品应用。

更有趣的故事是经验丰富的工程师现在如何构建更复杂的应用。 例如,一个团队可以用内部解决方案替换昂贵的第三方软件。 但他们怎么能信任自己的版本运行良好呢?

因为他们已经在特定领域工作,所以已经掌握了相关知识。 他们将自己所知与 AI 结合,快速构建出自己的版本,成本比支付许可费更低。 除此之外,他们还能添加任何所需的自定义功能。

对雇主而言,拥有熟悉领域、具备产品思维的工程师意义重大。 他们能弄清那些晦涩的细节,并引导讨论。 把开发者当作只会按指令写代码的人一直都很天真,如今更是如此。

对工程师来说,学会在陌生领域中工作,比只专注于技术技能回报更高。 如果你擅长解决业务问题,在利润中心工作会更容易,而不是成为某个框架的专家。

不要外包思考

DDD 表明,软件远不止写代码。 如果你处理过复杂系统,我相信你已多次见证这一点。

这个话题勾起了我对第一份软件工作的回忆。 我曾处理一个棘手问题,花了好几个小时用纸笔涂画。 最终我弄明白了,尽管几乎没碰键盘。 我当时刚入职不久,所以无言以对:“哇,今天我拿工资来思考。”

这在当时感觉很疯狂,如今感觉更甚。 那时,如果我说我花了一天思考一个问题,我的经理眼睛都不会眨一下。 如今,我们更关注产出。为什么不让 AI 来代替思考呢?

A prompt typed into a coding agent: i don't have the time to read it, explain again in simple terms

思考,被外包了。

其一,因为我能判断智能体的输出是否合理的唯一原因,是我过去曾花大量时间思考过这类问题。

有一种观点认为,你不再需要这种经验,因为 AI 反正会为你处理一切。 依赖 AI 最终可能成为新常态,但有一个问题我无法忽视。 你需要锻炼大脑来处理复杂问题,而不思考就无法建立新的心智模型。

我能想到的最佳类比是读书与读摘要。 读书迫使你花数小时思考主题。 你在脑海中慢慢构建一个复杂的模型,并将其与你所知的其他事物联系起来。 你理解了主题,而不是读了几句话,十秒后关闭浏览器标签就忘了。

还没人知道最佳实践,所以现在是实验并看看什么有效的好时机。 目前,我会将老派的问题解决方法与尝试新的代码工作方式结合起来。


在这篇文章中,我更侧重于 DDD 背后的哲学,而非个别模式。 我计划写更多内容,包括更贴近代码的战术模式。 即使我不再手写它们,我仍然觉得它们有用。 请在下方订阅,以免错过后续文章。

来源:Hacker News · threedots.tech