【译】写给自己的软件工程笔记

这是 Keras 的开发者 François Chollet 整理的笔记,里面有很多思考值得学习。
原文链接

在开发过程中

  1. 代码不仅仅意味着要被执行的。代码也是团队之间的沟通方式,是向他人描述问题解决方案的一种方式。(写出)具有较高可读性的代码不是一件值得骄傲的事情,它是编写代码最基础的部分了。这其中包括清楚地分解代码,选择不言自明的变量名,并插入合适的注释来描述隐含的任何内容。

  2. 当你提交每个PR的时候,要思考的不是这个PR能给你的下次升职带来什么,而应该是这个PR可以为你的用户和社区做些什么。要不惜一切代价地避免“狠显眼的贡献”。如果不是很明显的能帮助产品的目的,就请不要添加任何功能。

  3. 代码也是有品位的。品味是由对简单的渴望而产生符合约束-满足规范的过程,要时刻保持着对简单的执念。

  4. 敢于拒绝 - 只是因为有人要求提供功能并不意味着你应该这么做。每项功能的成本都超出了最初的实施范围:因为有维护成本、文档成本和用户的认知成本。如果你总是问:我们真的应该这样做吗?通常答案都会是否定的。

  5. 当你对用户提出的新的功能需求说“是”时,请记住,用户要的东西通常都不是最佳的选择。用户值专注于他们自己的特定的需求,你必须以项目的整体性和自有的原则来应对这一点。通常正确的答案会是扩展现有的功能。

  6. 在持续集成和以全面覆盖单元测试为目标这两件事上多投入。这样可以确保你能处在一个自信的编码环境中;如果不是这样,那么首要任务就应该是关注建立正确的基础设施。

  7. 没有提前计划好一些没有关系。不断地尝试然后看看结果如何。提早纠错,确保你创建了一个没有不可能的环境。

  8. 好的软件会让事情变得简单。仅仅因为一开始问题看起来很困难并不意味着解决方案必须复杂或难以使用。很多时候,在一个会有更容易的方案但可能不是那么明显的情况下,工程师往往会条件反射式地选择引入复杂度很高的解决方案(让我们使用ML!让我们构建一个应用程序!让我们添加区块链!)。在你写下任何代码前,确保你的解决方案不能再简单了。任何事情遵循第一性原理。

  9. 避免很隐晦的规则。当你发现自己开发了一种较为隐晦的规则时,应保持规则明确并与他人共享或自动化。每当你发现自己想出一个反复的,准算法的工作流程时,应该设法将其形式化为一个文档化的流程,以便其他团队成员从该体验中受益。此外,你应该设法在自动化这种任何可被自动化的工作流程部份(例如,正确性检查)。

  10. 在设计过程中应考虑你所做选择的全局影响,而不仅仅是自己想要关注的部分 - 例如收入或增长。除了你当前正在监控的指标之外,你的软件对全球用户的总体影响是什么?是否存在超出价值主张的不良副作用?在保留软件实用性的同时,你可以做些什么来解决它们?

道德设计,将你的价值观融入在你的创作中。

API设计

  1. 你的API是有用户的,所以它应该也要有用户体验。在你做出的每一个决定中,都要牢记你的用户。对用户充满同理心,无论他们是初学者还是经验老道的老司机。

  2. 始终寻求在使用API​​的过程中尽量减少对用户的认知负担。自动化可自动化的内容,最大限度地减少用户所需的操作和选择,不要暴露不重要的选项,设计简单一致的工作流程,以反映简单一致的心理模型。

  3. 简单的事情应该是简单的,复杂的事情应该是可能的(Simple things should be simple, complex things should be possible. —-Alan Kay)。不要为了小众用例增加常见用例的认知负荷,即使影响很小。

  4. 如果工作流的认知负荷足够低,那么用户应该可以在完成一次或两次之后就可以记住它(无需查阅教程或文档)。

  5. 寻求拥有与领域专家和从业者的心理模型相匹配的API。拥有领域经验但对你的API没有经验的人应该能够看最少的文档就能直观地明白你的API,很多时候只需要通过查看几个代码示例并查看哪些对象可用以及它们的签名是什么。

  6. 在没有任何关于底层实现的上下文情况下,参数的含义应该是可理解的。必须由用户指定的参数应该与用户对问题的心理模型有关,而不是跟代码的实现细节有关。API代表的是它解决了什么问题,而不是它背后是如何运行的。

  7. 最强大的心理模型是模块化和分层的:高度简单,但是当你需要了解细节时,又很精细。同样,一个好的API是模块化和分层的:易于使用,但又具有表现力。对象少,但签名复杂,与对象多但签名简单之间存在着一个平衡。一个好的API应该有合理数量的对象,且具有相当简单的签名。

  8. 你的API不可避免地反映了你选择的实现,特别是你选择的数据结构。要实现直观的API,你必须一开始就选择自然适合的数据结构 - 与该领域专家的心智模型相匹配。

  9. 故意设计端到端工作流程,而不是一组原子功能。大多数开发人员会询问:”应该提供哪些功能来处理API设计?让我们为他们配置选项吧。“相反地,应该问:该工具的用例是什么?对于每个用例,用户操作的最佳顺序是什么?什么是可以支持这个工作流程最简单的API? API中的原子选项应该能够满足高级工作流程中出现的明确需求 - 不应该“因为有人可能需要它”就添加它们。

  10. 错误消息以及通常在与API交互过程中向用户提供的任何反馈都是API的一部分。交互性和反馈是用户体验不可或缺的一部分。要为你的API设计错误消息。

  11. 因为代码是一种交流,所以命名很重要 - 无论是项目还是变量的命名。名称反映了你对问题的看法。避免使用过于通用的名称(x,变量,参数),避免 OverlyLongAndSpecificNamingPatterns,避免可能产生不必要的术语(主,从),并确保在命名选择中保持一致。命名一致性意味着内部命名一致性(不要一些地方用”dim“代表坐标轴,在另一些地方用”axis“)以及与问题域的既定约定的一致性。在命名之前,应尽量使用该领域专家(或其他API)已在使用的命名。

  12. 文档是API用户体验的核心,它不是附属品。在高质量的文档上多花时间,你会得到比在更多功能上多花时间更高的回报。

  13. 展示,而不是解释:你的文档不应该讨论软件的工作原理,它应该说明如何使用它。展示端到端工作流的代码示例、显示API的每个常见用例和关键功能的代码示例。

生产力归结为快速决策和执行力。

软件生涯

  1. 职业发展并不是你管理人数的多少,而是你所产生的影响,这个世界有你没你会有多大的差别?

  2. 软件开发是一项团队合作;它与人际关系和技术能力有关。做个好伙伴,当你不断前行时,不要忘了与人保持联系。

  3. 技术永远不会是中立的。如果你的工作对世界有任何影响,那么这种影响就有道德方向。我们在软件产品中看似无害的技术选择都会被充满使用动机,谁将受益,谁将受到影响。技术选择也是道德选择。因此,始终谨慎而明确地表达你想要支持的价值观。道德设计,将您的价值观融入您的创作中。永远不要想,我只是在建立这种能力,这本身是中立的。你构建它的方式无法决定它被使用的方式。

  4. 自我引导,你的工作和环境的力量,是生活满意度的关键。确保你给予周围的人充分的自我引导,并确保你的职业选择能够为你自己带来更多的力量。

  5. 去创造这个世界需要的东西,而不仅仅是你希望拥有的东西。技术人员常常会边过着稀薄的生活,边专注于满足自身特定需求的产品。寻找机会拓宽你的生活体验,让自己能更好地发现这个世界需要什么。

  6. 在做出会有长期影响的任何选择时,请将你的价值观置于短期的自身利益和过度的情绪之上 - 例如贪婪或恐惧。认清你的价值观,并让它们引导你。

  7. 当我们发现自己陷入冲突时,先暂停下来并承认我们的共同价值观和共同目标是个不错的主意,提醒彼此,我们是在同一条船上的。

  8. 生产力归结为高速决策和执行力。这需要a)良好的直觉,这来自以往的经验,它可以帮你在只给出部分信息的情况下就做出普遍正确的决定; b)敏锐地意识到何时应该更谨慎的行动并等待更多信息,因为错误决策会比延期付出更高的代价。最快的速度/最好的质量之间的决策往往会在不同环境而有不同的权衡。

  9. 更快地做出决策意味着你在职业生涯中要做出更多决策,这将使你对正确的选项有更强的直觉。经验是提高生产力的关键,更高的生产力将为你提供更多的经验:良性循环。

  10. 在你意识到缺乏直觉的情况下,请遵守抽象原则。在整个职业生涯中建立尝试过且正确的原则列表。原则是形式化的直觉,相比于原始直觉(需要对类似情况的有直接且丰富的经验),它能适用于更广泛的场景。