软件工程的核心在于管理复杂性

Hacker News Top 新闻

摘要

本文认为,软件工程主要通过架构决策和权衡来管理复杂性,人工智能在代码生成方面很有效,但不擅长处理这些更高层次的方面。它强调了构建软件涉及关于约束、成本和演化的关键选择,而不仅仅是语法。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/08/27 15:23

# 软件工程在于管理复杂度 – H[ack]-∞S 来源:https://hack8s.com/422/once-again-software-engineering-is-about-managing-complexity 关于软件工程存在一个误解,AI正使其日益明显:我们常常将编写代码与构建软件混为一谈。 这两者在某些方面有重叠,但并非同一回事。 编写代码意味着将想法转化为计算机可执行的指令。而构建软件意味着决定哪些指令应当存在、它们应如何交互、哪些约束至关重要、决策的代价是什么、哪些权衡可被接受,以及生成的系统如何在自身约束和限制下持续演进而不崩溃。 我们先从一个基本前提开始:AI是一项关键工具,因为它极其擅长解决第一个问题。 而第二个问题才是软件工程真正的起点。 ## 从未困难的是敲代码 考虑一个相对普通的工程需求。 > 我们需要处理传入的事件并更新某些数据。 以下是技术讨论初期会浮现的一些问题: - 应当同步处理还是异步处理? - 是否应将它们放入队列? - 需要恰好一次处理,还是至少一次即可? - 系统能否容忍最终一致性? - 如果处理中途失败会怎样? - 是否需要重试?重试多少次? - 如果消费者三小时内不可用怎么办? - 事件是否需要保持顺序? - 今天的预期流量是多少?两年后呢? - 如果一个事件被处理两次会有什么后果? ……等等。 这些问题与语法几乎无关。 编程语言的选择很重要,因为它影响团队熟练度、团队效率、系统性能、安全性、可维护性、工具链和运维特性,但它并不能回答那些根本问题。 真正的难点在于选择一个代表正确权衡组合的架构。 而这很少有普适的正确答案。 ## 一个问题可以有N个完全不同的正确解 当软件存在于业务环境中时,这一点尤为明显。 想象两家公司让它们的工程团队构建听起来完全相同的功能。 表面上看,它们的需求可能完全一致。 但实际情况是: - A公司有500名用户,而B公司有2000万用户。 - A公司可能只有三名工程师维护系统,而B公司可能有200名。 - 一家可能需要强一致性,因为错误会导致严重的财务损失;另一家可能欣然接受最终一致性,以换取可用性和吞吐量。 - 一家可能需要在三周内上线。 - 另一家可能希望系统能持续运行十五年。 - 一家可能已有Kafka、Kubernetes、PostgreSQL、可观测性基础设施以及熟悉分布式系统的工程师;另一家可能只有一个应用服务器和由四名开发者维护的PostgreSQL数据库。 对一家公司而言技术上出色的方案,对另一家公司可能就是不负责任的方案。 这就是为什么架构不能简化为询问: > 实现X的最佳方式是什么? 正确的问题通常更接近于: > 鉴于这些约束条件、这个团队、这个业务、这个基础设施、这个预算、这些风险以及产品的预期演进,在今天实现X的最合适方式是什么? 这是一个截然不同的问题。 ## AI生成解决方案,工程师主导权衡 这一区别正变得越来越重要,因为软件组织正在采用AI的方式。 AI对软件开发极其有用。我们将其用作加速器:生成样板代码、探索API、提出实现方案、查找潜在bug、解释陌生代码、生成测试、比较不同方案,或者简单地减少将想法转化为可运行代码所需的机械性工作量。 但存在一种危险的趋势,即将这种能力扩展到更广泛的领域: > 委托工程判断本身 你可以: - 给AI模型一个需求,要求它“零失误”地设计系统——它会设计出一个方案。 - 让它选择数据库——它会选出一个。 - 询问是否应引入队列、微服务、缓存、CQRS、事件溯源、Kubernetes、Redis或另一种抽象——它会给你一个答案。 然而,答案的存在并不意味着底层的工程问题已得到解决。 真正的问题在于,正确的决策取决于上下文——通常是大量的上下文,需要数小时、数天甚至数周来分析、理解和评估,往往涉及同一组织内的多个部门,并且常常留下难以形式化的灰色地带,未来需要变更时可能成为挑战。 这就是现实世界的运作方式:部分上下文存在于文档中,但很多并不在其中。 它存在于与客户的对话中、产品的历史中、工程团队的技能中、三年前的运维事件中、预算约束中、截止日期中、合同义务中、无人愿意触碰的遗留系统的怪异行为中,以及对“客户可能在六个月后请求特定功能”的预判中。 有时,它仅仅存在于经验中:认识到理论上优雅的架构将成为期望维护它的团队的运维噩梦。 你不能将所有这些视为一个细微之处,以为只需在AI提示词中增加一段话就能捕捉到。 ## 无权衡,不架构 工程在很大程度上是一门决定“你愿意接受哪些问题”的学科。即使你不在FAANG规模的企业工作,你的项目仍可能需要处理大量数据,因此做出正确的工程决策对于维持足够性能至关重要,而无需用昂贵的硬件来弥补系统性的低效。 即使是“简单”的选择也需要考虑多种因素: - 激进地规范化数据库,可能获得数据完整性,但会让某些查询变得更复杂。 - 反规范化,可能提高读取性能,但会增加同步复杂性。 - 添加缓存,能降低延迟,但会引入失效问题。 - 引入异步处理,可以解耦组件,但会创造新的故障模式。 - 将单体应用拆分为微服务,获得了独立部署边界,但引入了分布式系统的复杂性。 - 激进地优化,可能提升性能,但会牺牲可读性。 - 激进地抽象,可能减少重复,但会使系统更难理解。 - 避免抽象,最终又会出现相反的问题。 这些因素都无法独立评估。每一次优化都在某处消耗复杂度。因此,工程师的工作不仅仅是让系统运行起来。 而是要决定复杂度应归属于何处。 ## 算法思维更重要而非更不重要 这就是为什么我认为在AI时代,算法思维变得更加重要,而非不再重要。 我所说的算法思维,并非仅仅指了解教科书算法或能够凭记忆实现红黑树。 我所说的算法思维,是指定义一套需要遵循的基本规则并每日应用。我总结了十点原则: 1. 分解问题。 2. 识别不变量。 3. 理解数据流。 4. 选择合适的数据结构。 5. 推理时间和空间复杂度。 6. 识别竞争条件。 7. 理解故障模式。 8. 辨别哪些操作开销大,哪些真正重要。 9. 区分本质复杂度与偶然复杂度。 10. 理解当假设不再成立时应发生什么。 我知道,在生产力提升的时代,这听起来可能有点奇怪。AI可以在过去工程师编写一个实现的时间内生成十个实现。挑战在于将AI作为工具,通过有针对性的、可控的迭代来应用这些原则,同时保持人类在决策循环中。 这使得选择正确的实现方案变得十倍重要。这才是生产力提升的真正含义。 ## 你的编程语言是工具,而非身份认同 同样适用于编程语言。 选择正确的工具来做正确的事很有价值。语言有不同的优势、生态系统、性能特性、类型系统、并发模型、部署模型和安全保证。 这些差异很重要,但工程讨论有时对编程语言选择“过度估值”。在许多业务系统中,选择两种合理语言中的哪一种,其重要性远低于选择错误的架构、错误的数据模型或错误的运维策略。 选择适合场景的语言。如果有多种语言适合,选择你的团队深入理解的那种具有重要价值。不要基于“大语言模型对某种编程语言表现更好”的假设来做决策。永远将团队放在首位。 五个重要的标准是: - 熟练度很重要。 - 生态系统成熟度很重要。 - 可维护性很重要。 - 调试很重要。 - 部署很重要。 这就是为什么,今天比以往任何时候都更甚,在拥有大语言模型的便利下,我鼓励工程师们不要狂热地忠诚于单一编程语言,而应尽可能成为多语言使用者,学会将AI助手作为学习工具,而不仅仅是代码生成器,并始终仔细核对官方文档。 ## 真正的优化目标是系统的生命周期 另一个错误是仅针对当前需求优化软件。 现代软件不能是静态的,它必须能够应对以下变化: - 用户 - 客户 - 流量 - 商业模式 - 法规 - 团队 - 基础设施 每个方面的权重都可能随时间变化。因此,今天看似微不足道的功能,两年后可能成为核心工作流。 因此,良好的工程需要具备一定的预见变化的能力,同时要避免陷入相反的陷阱:仅为可能永远不发生的假设未来设计精巧的抽象,只因为团队接受了某种代码产出,其篇幅过长而无法完全审查。这种平衡很难把握,因为: - 这个需求变化的可能性有多大? - 我们应该在哪些地方特意引入灵活性? - 哪些地方应该保持简单? - 哪些技术债务是可接受的? - 哪些技术债务可能变得在结构上代价高昂? - 我们现在应该优化什么? - 我们应该特意留待测量证明其重要后再优化什么? 这就是工程判断。 没有AI能正确回答这些以及许多其他问题,你也不能简单地将AI代理投入到这些问题上并期待可靠的结果。没有任何提示能让这些问题消失。 ## AI生成的代码带来新的所有权问题 还有一个问题我认为软件组织正在低估:代码库的所有权。 AI使得生成代码变得极其廉价。这听起来完全是好事,直到你想起生成代码和维护代码是两种不同的经济活动。现在,工程师可以极其快速地生成数百或数千行看似合理的代码。 团队可以合并它们,测试通过,功能生效,生产力似乎提高了,但一些关键的东西可能已经丢失了。如果你想了解丢失了什么,请问自己: 1. 团队是否真正理解整体架构? 2. 他们是否理解数据结构? 3. 他们是否理解为什么存在特定的抽象? 4. 他们是否理解故障行为? 5. 他们是否知道生成的实现依赖于哪些假设? 6. 我们能否假设,六个月后他们能自信地修改它,而无需再让另一个AI来解释之前由AI生成的代码? 如果答案是否定的,那么团队并未消除复杂度。它很可能引入了其他复杂度,而这些复杂度会随着时间的推移变得越来越重要。 ## 代码行廉价,理解昂贵 这可能是AI给软件经济学带来的最大变化。 历史上,生成代码相对昂贵。现在它正变得极其廉价。而理解一个系统,正变得越来越昂贵。 这个区别很重要:一个包含3000行AI生成代码的拉取请求,仍然是向你的系统认知面积增加了3000行代码。而且,一套通过的测试套件并非答案,因为它会让你产生一种虚假的信心,认为一切尽在掌控,因为系统“显然”按预期工作。 ## 如果你发布了,你就得负责 因此,我认为强有力的技术管理应坚持一个非常简单的原则: > 绝不发布你不拥有的代码。 我所说的“拥有”是什么意思?即完全理解代码库,并验证其中的每一个部分。 AI生成的代码不应因其是通过某种先进/昂贵/前沿/(随便什么)大语言模型快速生成的,而受到更低的审查标准。 现在的挑战在于确定是否(以及何时)投入时间审查此类大型拉取请求是合理的,还是最好通过更小的迭代缓慢推进,其中少量的应用逻辑通过受控的人机交互来开发。 ## 软件工程可能正变得更难 最后一点,我想聚焦于一个似乎近期出现的有趣悖论: > AI让编程更容易,却可能让软件工程更难。 我们能生成更多代码,速度前所未有。我们可以几乎瞬间创建原型。我们可以在不花几天时间阅读文档的情况下集成陌生的库。我们可以以惊人的速度生成测试、API、数据库层、基础设施配置、前端组件、迁移和整个服务。 但我们理解系统的能力并未以同样的速度增长。这就产生了一个新的瓶颈,不再是: > 我们能多快写出这个? 而是: > 这个组织实际能理解和控制多少复杂度? 我们迄今使用的生产力指标已经失效:一个产出五倍代码的团队未必有五倍的生产力。我们使用AI系统进行软件工程的时间还不够长,尚无可靠数据,但越来越清楚的是,我们在大语言模型采用初期看到的生产力提升,如今在维护和演进中等复杂度系统方面正付出代价。 因此,从现在起,生产力不应再通过生成多少软件来衡量。 而应通过我们能多有效地解决问题,同时将由此产生的复杂度控制在何种程度来衡量。 ## AI应增强工程杠杆,而非取代工程判断 所有这些并非反对AI辅助开发。恰恰相反。AI可能是软件工程师几十年来获得的最强大的生产力工具。 但我们应该利用这种杠杆,将更多时间投入到工程中,而非更少。我们可以让AI编写重复的适配器、生成测试套件的第一版、解释一个陌生的库。让它帮助调查错误、提出3种实现方案……

相似文章

软件工程基础更为重要

Hacker News Top

本文强调,在人工智能和智能工具时代,软件工程基础仍然至关重要,突出了大型语言模型的推理局限性,以及对可维护、良好设计的软件的需求。

工程纪律

Reddit r/AI_Agents

一位开发者讨论了在使用AI编码代理时,代码生成速度超过理解速度,如何保持工程纪律和架构完整性的挑战,并寻求避免创建低质量软件的最佳实践。