代码之外

成为一名优秀的软件工程师不仅仅是编写能够运行的代码。而是编写其他人(包括未来的你)可以理解、维护和构建的代码。而是清晰沟通、深思熟虑地贡献,并成为你参与的生态系统中的好公民——无论是开源还是专有。

单向沟通

软件工程的很大一部分涉及为缺乏你当前背景的人写作:后来加入的队友、继承你代码的维护者,或者是六个月后的你自己,那时你已经忘记了为什么做出某个特定的选择。对于所有这类写作,一个关键建议是你的目标是捕获和传达_为什么_,而不仅仅是_什么_。什么往往是显而易见的,而_为什么_是来之不易的知识,很容易随时间流逝。

工程师之间最常见的沟通形式(除了代码本身)可能是代码注释。我个人发现很多代码注释是无用的。但它们不必如此!好的注释解释代码本身无法解释的事情:_为什么_以特定方式做某事,而不是_如何_工作(这正是代码展示的)。它们可以节省数小时的困惑,而糟糕的注释会增加噪音,或者更糟,误导。

几乎总是值得的注释类型:

README(你有一个,对吧?)也是与其他开发者的常见首次接触点。一个好的 README 立即回答四个问题:这做什么?我为什么要关心?我如何使用它?我如何安装它?按这个顺序。像漏斗一样构建它:顶部是一个单行描述和可能的视觉演示,这样某人可以在几秒钟内决定这是否解决了他们的问题,然后逐渐增加深度。在安装之前展示用法——人们希望在承诺设置步骤之前看到他们将得到什么。

提交消息是另一种经常被忽视的”为他人写作”。它们通常写成 “fixed blah” 或 “added foo”,虽然在某些情况下这足够了,但很容易忘记它们形成了代码库_为什么_这样演变的历名记录。当某人(包括你!)运行 git blame 试图理解一个令人困惑的更改时,好的提交消息应该给他们答案。

通常,正文应该回答:

显然,你应该根据复杂性调整细节。一行错别字修复只需要一个主题。一个花了数小时调试的微妙竞争条件修复值得用几段来解释问题和解决方案。

对于复杂的更改,遵循问题 → 解决方案 → 影响的结构可能很有用:从迫使因素或限制开始,然后解释更改了什么和关键设计决策,然后列出值得注意的后果(正面和负面)。最后一部分特别重要;真正的工程涉及平衡关注点,记录权衡是有意的可以防止未来的开发者认为你错过了问题。

LLMs 可以_在编写提交消息方面有帮助。然而,如果你只是简单地指向你的更改并要求它为更改编写提交消息,LLM 只能访问_什么,而不是_为什么_。因此生成的提交消息将主要是描述性的(与我们想要的相反!)。如果你首先使用 LLM 帮助你进行更改,在同一会话中要求 LLM 编写提交消息可能是一个更好的选择,因为你与 LLM 的对话本质上是一个关于更改的丰富上下文来源!否则,或除此之外,一个有用的技巧是特别告诉 LLM 你想要一个专注于”为什么”(以及笔记中的其他细微差别)的提交消息,然后_告诉它向你查询缺失的上下文_。本质上,你充当 MCP”工具”,编码代理可以使用它来”读取”上下文。

随着你的更改变得更复杂,确保也逻辑地拆分提交(git add -p 是你的朋友)。每个提交应该代表一个可以独立理解和审查的连贯更改。不要将重构与新功能混合或将无关的错误修复组合在一起,因为这混淆了哪些更改修复了什么问题的故事,几乎肯定会减慢最终对你更改的审查。它还通过 git bisect 给你超能力,但那是另一个时候的故事了。

当你开始更勤勉地进行技术写作,并更广泛地使用它时,一个注意事项:确保你尊重读者。一旦开始,很容易最终过度解释,但你必须抵制这种冲动,以免读者_不读_你写的内容。解释”为什么”并相信他们为他们的具体情况弄清楚”如何”。

协作

作为工程师,我们可能在工作中的很大一部分时间在自己的键盘上编码,但我们的大部分时间也用于与他人沟通。这段时间通常分为协作和教育,投资于在两者上做得更好的回报是显著的。

贡献

无论你是提交错误报告、贡献简单的错误修复,还是实现巨大的功能,都值得记住通常用户比贡献者多几个数量级,贡献者比维护者多一个数量级。因此,维护者的时间非常紧缺。如果你想增加你的贡献产生富有成效结果的可能性,你必须确保你的贡献具有高信噪比并值得维护者的时间。

例如,一个好的错误报告尊重维护者的时间,提供理解和重现问题所需的一切:

如果你发现安全漏洞,不要公开发布。首先私下联系维护者,并给他们合理的时间在披露前修复。许多项目有 SECURITY.md 文件或类似文件用于此目的。

确保你搜索现有问题。 你的错误或功能请求可能已经被报告,向现有讨论添加信息比创建重复更好。更不用说,它减少了维护者的噪音。

最小可重现示例是金子,如果你能想出一个的话。它们为维护者节省了大量时间和精力,可靠地重现错误通常是修复它最难的部分。更不用说,你为隔离问题所做的努力通常也有助于你更好地理解它,有时会引导你自己找到修复。

如果你没有立即收到回复,请记住维护者通常是时间有限的志愿者。如果你在等待他们的回复,几周后礼貌的跟进是可以的;每天提醒是不行的。同样,”我也是”评论,或只是终端输出复制的错误报告在为你的问题获得关注方面往往是净负面的。

如果你想进行代码贡献,你还想熟悉贡献指南。许多项目有 CONTRIBUTING.md——遵循它。你通常还想从小处开始;错别字修复或文档改进是一个很好的首次贡献,因为它帮助你学习项目的流程,而不必在内容上进行大量的来回讨论。

检查项目使用的许可证,因为你贡献的任何代码都将受到相同许可证的约束。特别是,注意 copyleft 许可证(如 GPL),它要求衍生作品也开源,如果你接触它可能对你的雇主有影响!choosealicense.com 有更多有用的信息。

当你决定打开拉取请求(”PR”)时,首先确保你隔离了你实际想要被接受的更改。如果你的 PR 同时更改了许多其他不相关的东西,审查者很可能会把它退回给你,要求你清理它。这类似于你应该将 git 提交分解为语义相关的块。

在某些情况下,如果你有许多看似分散的更改,但它们都是启用一个功能所需的,打开一个捕获所有更改的较大 PR 可能是可以的。但是,在这种情况下,提交卫生特别重要,以便维护者可以选择”逐个提交”审查更改。

接下来,确保你很好地解释了更改背后的”为什么”。不要只是描述_什么_更改了——解释_为什么_需要更改以及_为什么_这是解决问题的好方法。你还应该主动指出更改中需要特别注意的部分(如果有)。根据 CONTRIBUTING.md 和更改的性质,审查者可能还期望看到额外的信息,如你做出的权衡或如何测试更改。

我们建议向上游项目贡献,而不是”分叉”项目,至少作为第一种方法。分叉(如果许可证允许)应该保留给你想做的贡献超出了原始项目范围的情况。如果你确实分叉,确保你承认原始项目!

AI 使得快速生成看起来合理的代码和 PR 变得极其容易,但这并不能免除你理解你正在贡献的内容。提交你无法解释的 AI 生成代码会给维护者带来审查和可能维护甚至连其作者都不理解的代码的负担。使用 AI 帮助你识别问题和产生修复/功能是可以的,只要你仍然进行尽职调查将其打磨成有价值的贡献,而不是将这项工作传递给(已经超负荷的)维护者。

请记住,对于维护者来说,接受 PR 意味着接受长期责任。他们将在贡献者继续前进后长期维护此代码,因此可能会拒绝那些意图良好但不符合项目方向、增加了他们不想维护的复杂性或需求没有充分记录的更改。作为贡献者,_你_有责任证明为什么接受贡献值得维护负担。

当收到 PR 反馈时,请记住你的代码不是你!审查者试图让代码更好,而不是个人批评你。如果你不同意,提出澄清问题——你可能会学到东西,或者也许他们会。

审查

你可能认为代码审查是高级开发者做的事情,但你可能会比你预期的更早被要求审查代码,你的观点很有价值。新鲜的眼睛会发现经验丰富的开发者忽略的事情,来自不太熟悉代码的人的问题通常会揭示应该记录或简化的假设。

审查也是学习的最快方式之一。你会看到其他人如何解决问题,学习模式和习语,并发展对什么使代码可读的直觉。除了个人成长,审查在错误到达生产之前捕获它们,在团队中传播知识,并通过协作提高代码质量。它们不仅仅是官僚开销。

好的代码审查是一项你需要随时间磨练的技能,但有一些技巧可以让它们快得多地变得更好:

AI 工具可以捕获某些问题,但它们不是人工审查的替代品。它们错过上下文,不理解产品需求,并且可以自信地建议错误的事情。它们值得用作第一遍,但不是深思熟虑的人工审查的替代品。

教育

作为工程师,我们很多非编码时间要么用于提问,要么用于回答问题,可能是两者的混合;在协作期间,与同行对话,或在学习时。提出好问题是一项让你能够从任何人那里学习的技能,而不仅仅是完美的解释者。Julia Evans 有一些优秀的博客文章关于”如何提出好问题“和”如何获得问题的有用答案“,值得一读。

一些特别有价值的建议是:

请记住:精心设计的问题使整个社区受益。它们揭示了其他人也需要理解的隐藏假设。

注意,这些建议在与 LLM 交流时同样适用!

AI 礼仪

随着 LLM 和 AI 在软件工程中的使用增长,围绕它们的社会和专业规范仍在变化。我们已经在代理编码讲座中涵盖了许多战术考虑,但它们使用中也有一些”更软”的部分值得讨论。

首先是当 AI 对你的工作有重大贡献时,披露它。这不是关于羞耻——这是关于诚实、设定适当的期望,并确保结果工作得到适当级别的审查。披露你使用 AI 的_哪些_部分也是值得的——”这整个东西是 vibecoded”和”我写了这个备份工具并使用 LLM 设计了 Web 前端”之间存在有意义的区别。例如,我们使用 LLM 帮助编写了一些这些讲座笔记,包括校对、头脑风暴和生成代码片段和练习的初稿。

你还想遵循你贡献的团队和项目的规范。一些团队对 AI 使用的政策比其他团队更严格(例如,出于合规或数据驻留原因),你不想意外违反它。对你的使用保持开放有助于防止潜在的错误。

如果你的目标是在你正在做的工作中学习,请记住,如果你让 AI 为你完成大部分工作,可能会适得其反;你可能会学到更多关于提示(也许还有审查 AI 输出)而不是任务本身。特别是当你学习时,重点可能是旅程,而不是目的地,所以使用 AI”快速获得解决方案”是一个反目标。

一个相关的担忧出现在面试和其他评估情况中。这些通常旨在专门评估_你的_技能和能力,而不是 LLM 的。现在更多的公司允许你在面试中使用 LLM 和其他 AI 辅助工具,只要你让他们观察这些交互作为面试的一部分(即,他们也在评估你使用这些工具的技能!),但这些仍然是少数。如果你不确定 AI 辅助是否在特定任务的范围内,请问!

这应该是不言而喻的,如果评估情况明确要求没有外部工具、没有 LLM 等,你不应该使用它们。试图在不被发现的情况下这样做会反过来咬你。

练习

  1. 浏览一个知名项目的源代码(例如,Rediscurl)。找到讲座中提到的一些注释类型的例子:一个有用的 TODO、一个外部文档的引用、一个解释避免方法的”为什么不”注释,或一个来之不易的经验教训。如果那个注释不存在会丢失什么?

  2. 选择一个你感兴趣的开源项目并查看其最近的提交历史(git log)。找到一个带有解释_为什么_进行更改的好消息的提交,和一个只描述_什么_更改的弱消息。对于弱的一个,查看差异(git show <hash>)并尝试按照问题 → 解决方案 → 影响的结构编写更好的提交消息。注意事后重新组装必要的上下文需要多少工作!

  3. 比较三个有 1000+ stars 的 GitHub 项目的 README。它们都同样有用吗?寻找对你来说主要是噪音的东西,作为你未来编写的 README 的教训。

  4. 在你使用的项目上找到一个未解决的问题(如果有的话,检查”good first issue”或”help wanted”标签)。根据讲座中的标准评估问题:它是否似乎重视维护者的时间并包含调试所需的所有信息,还是你期望维护者可能需要与提交者进行多轮问题才能找到根本问题?

  5. 想一个你在使用的软件中遇到的错误(或在问题跟踪器中找到一个)。练习创建一个最小可重现示例:剥离所有与错误无关的东西,直到你有仍然演示问题的最小案例。写下你删除了什么以及为什么。

  6. 在你熟悉的项目上找到一个有实质性审查评论(不仅仅是”LGTM”)的合并拉取请求。阅读审查。所有评论都同样有成效吗?如果你是 PR 作者,收到所有这些评论的体验会如何?

  7. 去 Stack Overflow 并在你知道的技术中找到一个有高票答案的问题。然后找到一个被关闭或大量反对的问题。根据讲座的建议比较它们;是否可以预测哪个问题会得到更好的答案?


Edit this page.

Licensed under CC BY-NC-SA.