手动输入代码

Lobsters Hottest 新闻

摘要

本文认为,通过记忆手动输入代码可以提高编程理解和技能,引用了生成效应,并强调在AI编码智能体时代,这种做法更为重要。

<p><a href="https://lobste.rs/s/zotppg/type_out_code">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/19 14:42

# 亲手敲出代码 来源:https://haskellforall.com/2026/05/type-out-the-code 多年前我读过 Zed Shaw 的“XX 硬啃”系列课程(我猜是他那门 C++ 课程 https://learncodethehardway.com/courses/learn-cpp-the-hard-way),他在课程开头做了一个声明,大意是“不要复制粘贴示例;要亲手敲出来”,因为他坚信这样做能加深你对材料的理解。 如今他推荐的做法更进了一步: > 最好的做法是:完成一个练习后,删掉你写的东西,然后重做一遍,但尽量依靠记忆。如果卡住了,就看看练习里的提示,但尽量靠回忆去重现你刚做过的事。一开始你会频繁地参考练习和代码,但最终你会达到可以独立完成的程度。这也会提高你记忆更多代码的能力、总结事物工作原理的能力,并且比只做一遍就继续前进更能快速提升技能。 这在认知心理学中被称为**生成效应**(https://en.wikipedia.org/wiki/Generation_effect):与被动消费相同材料相比,主动生成新内容能提升理解力。或者像理查德·费曼说的:*“我不能创造的东西,我就不能理解。”* 我不是研究者,但我仍想以一个普通程序员的视角补充我的看法,说明为什么我认为这一现象对提升编程能力至关重要——即便是在智能编码代理盛行的时代也是如此。事实上,我认为现在比以往任何时候都更需要人们重新认识这种“古老的智慧”,否则我们将培养出一整代程序员,他们无法想象没有编码代理的帮助该如何编程。 在这篇文章中,我将阐述为什么你偶尔应该亲手敲出代码——逐字符、尽可能凭记忆地敲出来。这并不意味着你永远不应该使用任何开发工具来简化编码过程,但你也应该偶尔在没有这些便利条件的情况下锻炼自己的编码能力。 ### Freecoding(凭记忆自由编码) 我相信时不时亲手写出细节的重要性。我指的不只是性能特征或 UI 设计决策之类的东西,而是基本的东西: - **语法与结构**……即对关键字、标点符号和语言构造的熟练程度 - **类型与模式**……包括对类型系统和数据模型的熟悉与舒适程度 - **名称**……比如能准确回忆起函数、方法、类、导入、文件等 这些东西需要扎根在你的大脑里,这样你才能“freecoding”(凭记忆自由编码)。据我经验,能做到这一点的程序员,即使在那些看起来与 freecoding 熟练度无关的任务上,也显著优于其他程序员。而且我不认为这仅仅是相关关系,而是因果关系:**freecoding 培养了更广泛的编程卓越性**。 为什么?因为,如果你不能以一定程度的精确性敲出你脑海中的东西¹ (https://haskellforall.com/2026/05/type-out-the-code#user-content-fn-1),那就意味着你**实际上并没有真的把它想清楚**。你只是在幻觉自己理解了,就像大语言模型一样。 “但是 Gabby,”你可能会反驳,“我很确定我能够高水平地理解和操作代码,而不必纠结于语法或名称等细节。” 不,我不认为这像某些人想的那么有效。我来解释为什么。 ### 语法与结构 先从语法开始说起,这大概是我最鼓励死磕的方面,也是最容易引起争议的。毕竟已经是 2026 年了,我们的 IDE 或编码代理能帮我们搞定语法,为什么我们还要费心去想语法呢? 首先,如果你在语法上挣扎,我敢打赌你在许多其他事情(甚至非编程的事情)上也会挣扎。比如,如果你连括号都难以配对,那我会怀疑你能否流畅地将别人的逻辑前提与结论连接起来。 我再进一步说:对细节的忽视,与那些“功能性表述不清”的人群的泛滥有关——他们无法逻辑地跟随或串联句子。我指的是那种凭感觉沟通、而不是清醒地根据词语含义进行推理的人。 举个例子来说明我的意思。最近我审查了一个大语言模型的提示词,偶然看到这样一段话: > Never suggest external tools or alternatives that aren't part of the skills listed above. If a task requires capabilities beyond the available skills, say so. 如果不去仔细读,这个提示词听起来合理;但仔细读就会发现,它实际上给了模型两条截然相反的指令。 我发现,那些功能性表述不清的人,几乎无一例外,自己也很难独立拼写或写出语法正确的句子,因为**你不能把小事和大事分开**:如果你在细节上敷衍,这种习惯会渗透到你做的所有事情中,你也会开始把大局搞错。 类似地,在语法上挣扎的人往往也在抽象思维上挣扎。他们可能认为语法是阻碍大局思考的细节,但事实完全相反!语法是一种心理工具,它压缩并支持更高层次的思考,为我们提供一种精确、清晰、紧凑的内部语言来进行推理。 换句话说,语法就是下面这两者的区别: > `x` 是一个对象数组,每个对象有一个必需的 `domain` 属性(存储字符串)和一个可选的 `port` 属性(存储数字) ……以及: ```typescript x: { domain: string, port?: number }[] ``` ……这引出了我要强调的下一点: ## 类型与模式 你怎么强调自己对类型系统和数据模型的熟悉程度都不为过,就像熟悉自己的手背一样² (https://haskellforall.com/2026/05/type-out-the-code#user-content-fn-2)。正如 Fred Brooks 在《人月神话》(https://en.wikipedia.org/wiki/The_Mythical_Man-Month)中所说: > 给我看你的流程图,却隐藏你的表格,我会继续困惑。给我看你的表格,我通常就不需要你的流程图了;一切都会变得清晰。 举个例子,如果你使用数据库,你需要对自己的项目中的表、列名以及它们的关系了如指掌,甚至倒背如流。不要偷懒地“按需”查询这些信息;你应该主动收集这些信息,并时刻放在心上,才能进行有效的系统级设计。那些不愿意下功夫的人创建出的数据库模式就像他们混乱的思维一样:非规范化、充满重复/相似的数据(没有单一事实来源)。 这同样适用于类型和类型系统!任何使用过强类型语言(例如 Rust 或 Haskell)的人都可以证明维护一个扎实的类型心理模型的好处。此外,培养准确的“心理类型检查器”或“心理借用检查器”即使在弱类型语言(https://haskellforall.com/2026/05/type-out-the-code#user-content-fn-3)中也会派上用场。当你锻炼这样的心理肌肉时,你会提高抽象推理项目的能力;如果不这样做,你就会在数据建模的基本问题上栽跟头,比如**使无效状态不可表示**(https://www.awwsmm.com/blog/make-invalid-states-unrepresentable)。 猜猜看,如果你**不**理解所有类型是如何组合的,会发生什么。你(或你的编码代理)将会在你的 TypeScript 代码里到处洒下 `as any` 类型断言,因为你们不知道如何修复遇到的错误。毕竟,如果你连在正常路径(编写代码)中都无法忍受思考类型的痛苦,那你在非正常路径(调试类型错误)时肯定也扛不住。 ### 名称 我还认为,你应该能轻松回忆出自己项目或依赖中常用函数/方法/类/导入/包/文件的名称。当项目演进时,你也应该保持这种知识的更新。 这是一条更通用规则的特例:你应该熟悉**已有成果**(无论是开源世界还是组织内部已经构建好的东西)。很多人依赖编码代理来做工作的一个重要原因是,他们不知道有可复用的已有成果,于是只好让代理去重新发明轮子(代理也乐此不疲)。 例如,如果你不知道存在**SaaS 套件项目**(https://github.com/topics/saas-boilerplate),你会以为需要让编码代理为你搭建所有脚手架。克隆一个经过实战检验的开源项目(这正是它存在的目的)比让代理做同样的事情更快、更便宜、更可靠⁴ (https://haskellforall.com/2026/05/type-out-the-code#user-content-fn-4)。 在同一个项目或公司内部复用代码也是如此。如果你记不住名称,你也很难复用同事的代码。如果你连要找什么都不知道,你就无法在别人的工作基础上构建。 代理**可以**帮忙,但你现在有了一个新问题:你能有意义地审查代理的输出吗?例如,如果你连其他功能存在与否都不知道,你怎么能判断编码代理是否在重复实现功能?如果你对代码的理解比代理还差,你怎么能审查代理输出的质量⁵ (https://haskellforall.com/2026/05/type-out-the-code#user-content-fn-5)?你可以让代理生成测试,但根据我的经验,很多依赖代理的编码者甚至懒得仔细审查那些测试——这就是你会看到下面这种测试代码的原因: ```javascript // This is a real example I've seen in the wild describe("abort detection logic", () => { it("detects aborted stopReason in messages", () => { const messages = [ { role: "assistant", stopReason: "aborted", content: [] }, ]; const isAborted = messages.some((m: any) => m.stopReason === "aborted"); expect(isAborted).toBe(true); }); it("detects abort in error string", () => { const error = "The operation was aborted"; const isAborted = error.includes("abort"); expect(isAborted).toBe(true); }); it("does not false-positive on normal errors", () => { const error = "Network timeout"; const isAborted = error.includes("abort"); expect(isAborted).toBe(false); }); it("does not false-positive on normal stop reasons", () => { const messages = [ { role: "assistant", stopReason: "stop", content: [] }, ]; const isAborted = messages.some((m: any) => m.stopReason === "aborted"); expect(isAborted).toBe(false); }); }); ``` 软件开发充满了日常摩擦,而我们应对小摩擦的方式会影响我们应对更大摩擦的方式。当我们决定小摩擦(比如记住一个名称)不值得克服时,我们最终也会决定更大的摩擦(比如仔细审查测试)不值得克服。 ## 结论 你可能已经注意到这些例子之间有一些共通之处: - **你不能分割主观能动性:** **良性压力**(https://en.wikipedia.org/wiki/Eustress)是好的:当你挑战自己去做和学习新事物时,你会培养对较小不适的容忍度,并为克服更大不适铺平道路。如果你总是逃避不适,你就会陷入一个不断增加的无力感和沮丧的恶性循环。 - **你不能分割熟练度:** 当你在某件事情上变得非常擅长时,这种能力会渗透到其他事情上,因为一切都是相连的。就像大语言模型从训练数据中泛化一样,人类也是如此!在一个领域培养精确性、回忆能力和结构化思维,也会转化为其他领域的提升。 如果你喜欢这篇文章,你可能也会对我另一篇文章感兴趣:《软件工程师不是(也不应该是)技术员》(https://haskellforall.com/2024/07/software-engineers-are-not-and-should)。在那篇文章里我解释了软件开发本质上意味着要时常走出舒适区。 1. 重要的是,英语并不是一种精确的语言,除非你把它扭曲成和代码一样详细的东西(https://haskellforall.com/2026/03/a-sufficiently-detailed-spec-is-code)。↩ (https://haskellforall.com/2026/05/type-out-the-code#user-content-fnref-1) 2. 反驳观点:其实我并不怎么熟悉自己的手背。↩ (https://haskellforall.com/2026/05/type-out-the-code#user-content-fnref-2) 3. 这并不一定意味着我认为 Haskellers 或 Rustaceans 比其他人写出更好的代码(不撒谎,我见过他们写出一些很烂的代码),但在其他条件相同的情况下,熟悉类型确实有帮助。↩ (https://haskellforall.com/2026/05/type-out-the-code#user-content-fnref-3) 4. 我曾经看到有人预测几年后大语言模型就能构建出一个完整的浏览器。我当时想的是:“我今天就可以通过 fork Chromium 做到这一点”,而且修改和维护起来会容易得多。↩ (https://haskellforall.com/2026/05/type-out-the-code#user-content-fnref-4) 5. 此外,编码代理会捕捉到驱动它们的人的思维习惯。你给代理的指令构成了代理上下文的主要部分,如果你一直懒得思考,你就会把你的代理带入同样懒得思考的盆地。或者用一个非代理的类比:懒得思考的管理者会培养出懒得思考的下属。↩ (https://haskellforall.com/2026/05/type-out-the-code#user-content-fnref-5)

相似文章

我们应当比模型更累

Hacker News Top

作者反思了使用AI代码生成时的认知脱节,指出它绕过了手动编写代码所带来的深度学习和记忆过程。她分享了在AI辅助开发中重新引入摩擦和刻意练习的实用策略。

用AI写更好的代码,但更慢

Lobsters Hottest

Nolan Lawson认为,AI编程助手可以通过使用多个模型进行彻底的代码审查和漏洞检测,从而更慢地编写高质量代码,提升代码库的健康状况,而不是最大化输出速度。

与代理协作编程

Reddit r/singularity

与代理协作编程探讨了AI代理如何帮助开发者编写代码、自动化任务以及提高生产力。

AI生成代码的质量

Reddit r/AI_Agents

这篇文章讨论了一个担忧:随着AI工具生成越来越多的代码,未来基于这些合成代码训练的模型可能会质量下降、原创性降低,并询问像OpenAI、Anthropic和GitHub这样的主要AI实验室计划如何应对这个问题。