人工智能让我质疑我们是否需要理解自己的代码
摘要
本文质疑在AI编程代理时代代码理解的必要性,强调可观测性和按需理解,而非传统的审查实践。
我一直在大量使用编程代理,包括在构建一个生产级DSP时,最终让我质疑的不再是AI是否编写了“好代码”这样一个更基本的问题。为什么我们首先假定需要有人理解所有代码?在软件历史的大部分时间里,代码的生产和修改成本高昂。我们的许多工程文化在那种约束下是合理的:DRY原则、可复用的抽象、小类、清晰的架构、避免重复等。代理改变了经济学。它们可以编写和机械性地修改远比我能阅读的更多的代码。通常的反应似乎是:这造成了理解/审查的瓶颈。我们需要更好的审查、更多的文档、持久的代理上下文、更多的验证等。我越来越不确信理解一定是需要偿还的债务。代码是文本。它可以放在那里不被阅读。如果某个生成的子系统在其实际运行的环境中完成了它应有的功能,并且我可以观察其行为,为什么我仅仅因为“拥有”它就要阅读它的两万行代码?如果它开始表现异常,我可以那时再检查相关源代码。理解可以按需重建。代理恰好也让这变得极其便宜。而且我并不是说“信任测试”。测试是在人工条件下对系统的另一种观察。它们可能错误、不完整或仅仅测试了错误的东西。最接近真相的是在为其构建的实际环境中运行的实际系统。这使得可观测性比代码审查或理解更让我感兴趣。不是可观测性作为流程中的最后一个绿框:lint → 测试 → 审查 → 部署 → 观察 ✓ 更像是一种认知立场:我实际上并不知道我的系统在做什么,所以我持续观察。源代码、测试、文档和解释代码的LLM都是证据。它们都不是运行中的系统。这也让我重新思考抽象。我总是对像Telegram的Android客户端这样的代码库有好感:巨大的类、令人惊讶的少量抽象、许多裸露的机制。人们常常觉得这很可怕。我常常觉得更容易阅读,因为当我需要知道发生了什么时,我可以从某处开始向前阅读。当抽象允许我真正忘记它隐藏的内容时,它是有用的。但许多应用抽象并不移除信息。它们只是转移了信息。与其阅读一百行无聊的代码,我得到了:UseCase → Repository → RepositoryImpl → DataSource → Mapper → Adapter 当某个东西出错时,我最终还是得阅读具体的实现,只不过现在我还需要重构五层被发明出来隐藏它的东西。这里还有一个看起来更离经叛道的观点:重复可以是解耦。假设二十个组件共享一个抽象。我们传统上说这很好:修复一次,所有二十个消费者都得到修复。换种方式说完全相同的事情:改变一件事,二十个消费者的行为了发生变化。当人类维护时,二十个无聊的副本成本很高。但代理特别擅长“改变这二十个无聊的副本,运行它们的测试,并告诉我发生了什么”。所以也许有时重复能为你购买更小的故障域。共享购买的是行为耦合。这并不意味着抽象不好或测试不重要。write()是一个美妙的抽象,因为我确实可以忘记内核做了什么。共享的可变意大利面仍然是地狱。我怀疑在AI时代,代码的有用目标是否更像是:显式控制流、局部状态、窄行为边界、积极的可观测性、廉价的重复、少量的抽象,以及源代码被视为可按需读取的因果存储,而不是某人必须永久携带的知识。这也让我对研究发现AI生成的代码包含更多“代码异味”持怀疑态度。它有味道。那又怎样?机器人没有鼻子,而且我的鼻子也塞住了。我越来越关心的问题不是生成的代码是否类似于为人类维护优化的代码。而是:它有效吗?我能观察到它是否持续有效吗?当它不工作时,我能找到相关的机制,修改它,并快速观察干预是否达到了预期效果吗?也许我们称之为“好代码”的一些东西之所以好,只是因为人类是维护机制。而机器维护的代码可能应该看起来完全不同。
相似文章
AI代理是否让构建软件比理解软件更容易?
一位开发者反思了AI编码代理如何能够快速构建和修改软件,但开发者往往失去对代码库架构和决策的理解,从而带来新的工程挑战。
AI编码工具已强大到能真正交付产品,但对学习而言却成问题
一位开发者反思AI编码工具如何提升生产力,但可能阻碍深度学习——用户交付了不完全理解的代码,引发关于技能发展的疑问。
@geoffreylitt: 热论:我认为理解我们的代理编写的代码仍然很重要!在这个超长帖子中(基于我的 A…
Geoffrey Litt 认为,开发者理解 AI 代理编写的代码仍然很重要,并分享了高效理解这类代码的想法。
理解成为新的瓶颈
Geoffrey Litt认为,随着AI代理生成更多代码,理解这些代码成为了新的瓶颈,他提出了代码解释文档、测验和微世界等技术,帮助人类在创意过程中保持参与,而不仅仅是验证正确性。
我是在学编程,还是只学会了向AI提问?
一位开发者反思了使用AI快速编写代码与深入理解代码之间的权衡,并质疑如何在AI辅助与真正学习之间取得平衡。