务实的开源贡献者:一份实用指南
The Pragmatic Open Source Contributor
我有时觉得,领薪水的程序员不太愿意修复或扩展开源代码,原因可能是以下几种情况的组合:
- 他们认为这不是他们的工作。
- 他们的 雇主 认为这不是他们的工作,并且/或者没有必要的法律基础设施和意愿。
- 他们过去提交的补丁请求被拒绝或忽略,感到沮丧。
- 他们担心整个过程的时间投入。
这些都是可以理解的障碍,我希望能够打破它们。更广泛地说,我编写本指南有两个目标。 首先,我希望鼓励人们共享开源软件所代表的知识。 特别是如果你在大型环境工作,或者你是新兴技术的早期采用者,你就有很好的机会发现可以使每个人受益的问题和改进领域。 其次,我旨在提供一个关于如何成功完成此类工作的行动手册, 并设定对你可以期待什么以及期望你做什么的预期。多年来,我为许多项目做出了贡献,通常遵循一种模式,即识别出可以解决我 95% 问题的代码,然后将剩余的 5% delta 贡献回去。我也在一个开源社区 OpenStack 中进行了更广泛的工作,在那里,公共贡献和分布式协作是常态。
The Pragmatic Contributor
开源社区指南 简洁地总结了回馈开源项目的好处。但是,我发现这些指南将该过程定义为由 个人激励 所驱动的,例如,个人发展或成就感。对于务实的贡献者来说,有一个动机胜过一切:改进企业所依赖的软件。你希望修复一个阻碍你做到最好的工具。
这种心态会影响贡献过程的各个方面。如果你主要对学习新东西或建立联系感兴趣,你可以选择要贡献的项目,以及要从错误跟踪器中挑选哪些问题。务实的贡献者从不试图寻找问题;问题会找到他们。 也许这是一个较新的项目,尚未充实所有小细节,例如,对多种身份验证方法的支持。或者,你可能只是发现行为中存在看似 bug 的问题,或者在高吞吐量下使用时效率低下。
务实的贡献者还会 对解决方案进行压力测试。你很可能遇到了其他人没有见过的问题。你 真的 需要在开源层中实现修复程序吗?或者说你的应用程序的行为只是很奇怪?将此功能贡献回社区的更广泛的好处是什么?随着时间的推移,软件自然会想要扩展其表面积和复杂性。有些维护者以铁腕手段来控制代码的范围,保持其低且稳定,另一些维护者则更愿意相信扩展范围会使事情变得更好。随着时间的推移,我逐渐体会到第一种方法的智慧,尽管这给你作为局外人带来了挑战。无论哪种情况,我发现尊重维护者的观点(毕竟,这是你一直在愉快使用的代码)以及找到最优雅的解决方案,都会有很大的帮助。
The Pragmatic Patch Playbook
一旦你确定了潜在的贡献,以下是我喜欢遵循的一些通用步骤。
Step 0: Talk to legal
在你做任何其他事情之前,请获得法律部门的批准。 即使你正在为没有贡献者许可协议 (CLA) 的项目做出贡献,你也有责任确保不会给你的公司或自己增加风险。理想情况下,这次对话的结果是批准签署任何经过审查的 CLA;如果项目支持公司 CLA (CCLA),则 CCLA 可以让你团队的新成员自行获得许可,并且比与一名员工绑定的个人 CLA 更具未来性。
这可能有点困难,但在我的经验中,这主要是时间问题。你可能需要一些耐心,让所有利益相关者了解回馈开源的业务案例以及任何可能的风险。我通常倾向于以下论点:
- 我们目前使用开源系统 X,它通过功能和成本效益提供业务价值,即它通常是免费的 ——“as in beer.”
- 然而,如果没有修改,它无法处理一些新的业务用例。
- 修改工作量相对于绕过约束条件而言很小。
- 我们不需要也不会暴露专有代码。
- 私下修改代码(fork)会带来长期的维护负担并增加风险。将来 X 很可能会以某种方式更改,从而需要大量返工我们的修改,从而阻止我们执行安全升级。
- 公开修改代码可以降低这种风险,并且(尤其是在 X 在业内广为人知的情况下)甚至可以通过提高公司的知名度来吸引新人才。
- 因此,为 X 做出贡献符合企业的最佳利益。
我从来没有见过这个论点在有足够的动机的情况下失败,但我相信会有例外。需要记住的一件事是,你不是在争论 实际 执行工作或估算时间投入或投资回报率;你是在为 选择权 辩护。但是,你应该准备好给出你可能做出的贡献类型的具体示例。
Step 1: Get the lay of the land
我看到开发人员经常跳过这一步,直接提交补丁。这可能会让你和维护者都感到沮丧。快速检查任何已定义的贡献过程,并将自己置于维护者的位置通常可以避免这些问题。
贡献过程是什么? 你需要先签署 CLA 吗?是否欢迎在 repo 上提交 pull request?你能找到来自外部的贡献示例吗?维护者如何回应贡献请求?你能从中了解到什么?
维护者有多活跃? 他们是每天都在审查补丁,还是看起来更零星?你能注意到他们响应查询的方式和时间的任何模式吗?是共享的维护者模式还是单个所有者?如果是共享的,谁最近似乎最活跃? 这实际上需要多长时间? 从打开 pull request 到合并的时间间隔是多少?其中有多少是等待补丁作者,有多少是维护者的反馈?你认为你需要做多少个补丁,它们需要串行完成吗?由此,你通常可以得到一个粗略的估计,但我也有一个启发式方法:预计一个错误修复需要两周到一个月才能完成,而大型功能的工作需要三个月到一年的时间。其中很大程度上取决于你对该过程的关注程度。
Step 2: Get maintainer buy-in
对于小的更改,你通常可以跳过此步骤,但如果你正在考虑对代码库进行任何重大更改,那么在此处投入时间将使整个过程更加顺利。你的目标是找到至少一位维护者来帮助支持你的更改。
在维护者所在的地方与他们会面。 他们是否有一个用于协调更改和共享信息的 Slack 频道?一个双周特别兴趣小组 (SIG) 会议?一个邮件列表?找出他们首选的沟通方式,然后自我介绍。简要介绍你是谁,以及为什么有兴趣为该项目做出贡献,以及你试图解决的问题。
遵循正式的提案流程。 如果项目使用提案系统(例如,Kubernetes Enhancement Proposal [KEP]),请了解如何提交提案。你可以在提出提案之前与维护者交谈,也可以在提交提案后通知他们。我仍然认为在这里进行“热切换”很重要,实际上亲自与维护者联系,让他们知道你愿意讨论,并且认真地开始做出重大贡献的过程。
就范围达成一致并保留纸质记录。 如果你在与维护者的对话中就你的贡献范围达成一致,并且重要的是,可以考虑哪些超出范围,请确保你将其公开记录在某处。这有助于为可能在稍后审查工作的其他维护者提供背景信息。通常,如果存在该流程,则此信息也应包含在提案文档中,但拥有其他记录仍然有用。
因此,我也喜欢在例如公共 Slack 频道而不是私人消息中进行有关提案的对话。你可以在私下讨论细节,然后在 Slack 中发布对话摘要以保留记录。
Step 3: Do the work
你可能已经注意到我们还没有编写任何代码。猜猜怎么着?这是我们唯一要讨论代码的步骤!开源工作的大部分是沟通。也就是说,根据我的经验,有一些通用规则可以改善补丁请求的结果。
不要害怕 fork。 有些人对“fork”这个词感到反感。就我个人而言,当我进行开源贡献时,我总是 fork 该项目,在那里进行补丁,并在内部使用 fork 一段时间。你需要在交付速度(立即利用你的补丁)和维护负担(在升级过程中携带你的 fork)之间进行权衡;在我的工作场所,前者通常胜过后者,这取决于你期望拉入上游更改的频率。在内部进行 Fork 还可以让你在提交上游补丁之前,在实际环境中对更改进行实战测试。 我经常通过这种方式发现错误,或者可以纠正关于某些东西在实践中如何工作的错误假设。
从你想要的特定结果反向推导到有助于实现该结果(以及其他结果)的通用机制。 例如,在这个旧的 webpack 补丁 中,我想要的是一种将 Git commit SHA 放入 webpack 构建的文件名中的方法。我没有明确地对此案例进行编码,而是提出了一种使插件能够为新的文件名模式占位符提供支持的方法。这使我能够在单独的插件中处理我的需求,并且 多年来似乎对其他人很有用。
添加测试! 如果你在某些代码中发现了一个错误,这可能意味着没有针对该行为的足够好的测试。添加一个在没有你的补丁的情况下失败,并在有你的补丁的情况下成功的测试。如果你正在添加新功能,请确保你具有良好的覆盖率。维护者最终将负责 你的代码 中的错误,而你的工作是尽可能减少这种负担。有时会发生没有适当的测试基础设施来表达你需要的测试。在这种情况下,请与维护者联系,询问他们对如何进行的看法;通常,他们会接受较少的测试覆盖率,因为他们正在研究解决代码这一方面的通用解决方案。
使每个补丁都成为一个原子更改。 此处“更改”的定义是可以解释的。如果你在一个开源项目中广泛工作,并且正在实现一个涉及代码多个区域的大型功能,你可能应该将实现的每个部分分解为一个针对代码库子集的单个补丁。如果你的工作的更广泛的背景是已知的并且被正式跟踪,那么这是最好的;并非所有项目都具有这种基础设施。我喜欢将我的补丁范围保持在最小,以减少审阅者的上下文开销。例如,在处理一个较大的功能时,我首先确定了一个(相当大的)重构,我可以这样做,这会使实现该功能更容易。我为此更改提交了一个补丁,然后为最小的功能实现提交了一个补丁。
如果你的原子更改仍然很大,请将其分解为迭代提交。 在后一个示例补丁中,我将其分解为多个提交,以便更容易审查和了解思维过程。我可以将这些提交分解为单独的 pull request,但在我看来,当所有内容都在一个可以参考和迭代的 pull request 中时,它可以减少认知开销(对于审阅者而言)。如果你的提交以这种方式构建,你总是可以在以后将其分解为单独的请求。
尽量减少重构。 你可能会想“清理”其他与你的代码没有明确关联的代码区域。这可能很困难,但你应该尽量不要屈服于这些冲动。任何不必要的重构,特别是当它涉及代码的可读性或样式时,都会增加审阅者必须做的工作,并扰乱你实际试图通过补丁实现的消息。将重构减少到仅对使你的更改成为可能所必需的程度。稍后,你可以返回并进行重构(如果你愿意)。补丁请求也是一个很好的机会来询问维护者他们将来对这种重构的看法,从而使你能够获得一些早期的支持。
保持向后兼容性。 开源项目被广泛使用,你无法了解今天使用它的所有方式。你可能在某个时候被某个库改变默认行为或其 API 表面而没有进行重大版本升级而搞砸。主要版本是开源代码库的重大步骤,如果你将更改与破坏性更改版本相关联,这将大大增加你的更改可用的延迟。因此,请努力确保你所做的任何事情都不会破坏现有行为。
Step 4: Do the other work
这可能是开发人员最不喜欢的过程部分,但你应该计划在此处花费一些时间来获得高质量的贡献。
编写良好的文档。 你应该为你添加到项目中的任何新功能编写文档。有时你需要添加一个全新的文档部分!我认为我不需要争论文档的好处,以及当文档严重缺乏时它是多么令人沮丧。如果这是一项对你来说特别困难的任务,我猜 LLM 现在可能非常擅长编写文档,并且可能是以更易于理解的格式总结功能如何工作的好工具。我还没有亲自尝试过,仍然手工编写所有文档。
- 另请参见来自 WriteTheDocs 的这些有用的资源。
示例也是文档。 通常,开源代码的技术文档是“技术参考”类型。但是,这只是文档的一种类型。示例添加了更多的“操作方法”风格,有时展示确实胜于讲述。如果你正在向代码添加另一个功能,你应该提供一些如何使用它的示例。
Step 5: Finish line
一旦你将所有代码和非代码部分组装完毕,并且检查了过程中所有其他必需的框,你就可以提交你的补丁了!这可能会感觉有点像“赶紧等等”,特别是如果你一直在处理一个补丁来交付你的工作的一部分。根据我的经验,耐心在这里很重要。仅仅因为现在是你迭代补丁的好时机(因为你已经构建了所有上下文)并不意味着对于维护者来说也是如此。以下是如何浏览此时间的一些提示。
主动联系维护者。 如果这是一个较大的更改,请使用你在步骤 2 中建立的渠道和连接。维护者可能希望将你的更改作为其自身流程的一部分进行正式讨论,并且让他们知道你的补丁已准备好进行审查可以帮助他们确定如何优先处理它。对于较小的更改,我通常不这样做,因为我认为仅仅通过补丁请求本身已经通知了维护者某些事情,而 ping 维护者有点烦人。
定期礼貌地检查以提高可见性。 如果我难以让任何人关注补丁请求,我通常会等待一两周,然后友好地 ping 一些维护者,并要求他们查看补丁,或者我应该做其他任何事情。如果第一次没有奏效,我会在大约一周后再次执行此操作。如果一个月左右过去了,我将开始变得更具创造力,看看我是否可以在另一个合适的公共社交渠道上与他们联系。始终要有礼貌,并且绝对 不要 咄咄逼人;即使你认为你在帮助项目,你也在占用维护者的时间和精力。
一旦你收到维护者对你的补丁的关注,立即采取行动。 尤其是对于较大的请求,如果你获得代码的首次审查,请尝试在一两天内响应反馈。这使补丁对话对每个人都非常新鲜。维护者刚刚通过审查你的代码花费时间构建了你的工作的上下文,并且根据我的经验,他们感谢对评论的快速跟进。根据你自己的工作承诺,这可能难以平衡。我尝试设定预期,即当补丁处于最后阶段时,我应该留出一些工作时间作为缓冲区。
Step 6: Tie it off
如果你做到了这一点,你的补丁被上游接受了!当你看到合并成功完成时,总会有一种很棒的感觉(解脱?)。这并不总是发生 —— 我有一些 pull request 已经 闲置多年,收集了一些悲伤的“+1”评论。作为务实的贡献者,这并不是什么大问题,因为它通常表明代码的演变率较低,因此维护你自己的 fork 不需要太多的工作。
无论如何,一旦越过终点线,我通常会做一些最后的事情。
感谢审查你的代码的维护者。 成为维护者通常是一项吃力不讨好的任务,因此我发现真诚的感谢会有很大帮助。我经常从与开源维护者的代码审查中学到很多东西,并将此视为礼物。你只需在补丁对话中留下感谢,或直接联系,以你过去与他们的沟通方式为准,以确定哪种方式更合适。
减少你的公交车系数。 你是否开始研究一组更长的相关功能?如果是这样,请确保你清楚地记录在某处工作中下一步是什么。可能会发生很多事情。你可能会参与其他占用你所有注意力的工作承诺,你可能会被解雇,你的公司可能会完全停止使用你修补的软件。尽管如此,可能有人想稍后完成你开始的工作。应该可以在没有你的参与的情况下完成该操作。
将你的补丁带回内部。 如果你一直在处理代码的私有 fork,请努力将你的更改带回你的 fork 中。也许你甚至可以停止完全使用你的 fork!
Conclusion
让我们回顾一下我建议的阻止人们为开源做贡献的一些原因:
他们认为这不是他们的工作。 希望我已经为 为什么 这很重要做了一个简短但像样的案例,无论是对于更广泛的社区,还是对于你自己的成长。熟悉和自信于此过程使你能够突破技术障碍,因为你可能不再因某些底层第三方代码不支持 XYZ 而“阻止”实现你的目标。
他们的 雇主 认为这是他们的工作,并且/或者没有必要的法律基础设施和意愿。 步骤 0 描述了你可以尝试为贡献辩护的方法。最终,这在实践中可能仍然是一个障碍,但我认为值得在这里探讨一下假设。有时你可能会对你的雇主对这类工作的开明程度感到惊讶。
他们过去提交的补丁请求被拒绝或忽略,感到沮丧。 步骤 1 和 2 应该让你能够更好地设定自己的期望,并通过与你所依赖的人建立更好的关系来改善时间表。
他们担心整个过程的时间投入。 在我看来,这是对整个工作最明智的反对意见。希望很明显,编写代码只是整个过程的一小部分。尽管如此,我确实发现你在这方面拥有的经验越多,它就越容易,因为你更了解如何保持球的滚动,并且如果你在同一个生态系统中工作,随着时间的推移,你应该从维护者那里获得更多的信任,这对于未来的贡献非常有帮助。
写下你希望在世界中看到的改变!
最初发布于 2025-03-02。 我想相信