编者按:今天,Keras作者、Google深度学习François Chollet又发表了一篇新文章。他把里面的内容称为“自我提醒清单”,这是他多年来目睹、亲身经历的软件工程师经验总结。虽然在实际操作中,不少人会被公司管理层的各种要求逼上歧途,但凡事重在不忘初心。值此秋招之际,论智也把这篇佳作分享给大家。
代码不仅仅意味着运行,它也是团队人员之间的一种沟通方式,是向他人描述问题解决方案的一种方式。代码的可阅读性不应是软件工程师的优点,它该是所有人的基础能力。这包括清楚地分解代码,选择不言自明的变量名,以及插入注释来描述隐含的任何内容。
pull request的时候,不要纠结代码能怎么帮你营销自己,多想想它可以为读者和社区做什么。你必须不惜一切避免“招摇的贡献”,如果这么做对你的产品毫无用处,就不要去画蛇添足。
个人品位也适用于代码。它是一种约束,如果你追求代码的简洁优雅,你就会规范自己的编码过程。因此,要始终对简洁性保持偏爱。
学会拒绝——别人提出需求不等于你就应该照他们的想法去做。他们每提出一个新功能,它的成本就会超出最初的实施范围:维护成本,文档成本和用户的认知成本。所以要一直问自己:我们真的应该这样做吗?通常情况下,这个问题的回答是否定的。
当你答应按照用户请求开发新功能块(use case)时,记住,只按字面理解开发新功能是不够的。用户只关心自己用的特定功能块,但你必须站在全局性和原则性的视角下去看待这项更新,一般情况下,这意味着扩展现有功能。
向持续集成和所有代码的单元测试全覆盖“投资”。时刻确保自己处于优质编程环境中,如果这点都做不到,先关注怎么准备正确设备。
如果没有做计划,没关系。你可以多多尝试,看看结果如何。要尽早发现错误选择,确保自己最终能发现一种可行的方法。
好的软件可以化难为易。问题看起来可以很复杂,但这并不意味着它的解决方案一定是复杂的、难以实现的。有时工程师们会忽视简单易用但不怎么明显的解决方案,转而投向另外一些复杂的、有副作用的方法(我们用机器学习吧!写个APP吧!加上区块链吧!)。在编写任何代码之前,请确保你选择的解决方案不能更简单,牢记编程首要原则:Keep It Simple。
避免隐含规则。如果你自己开发了一些隐含规则,请确保它们是共享的,可以被其他开发者明确理解,或是可以自动化。当你想出了一些频繁出现的、类似算法的东西,你应该设法将其形式化为一个文档化的流程,以便其他团队成员从中受益。此外,你也应该设法寻找一些工具,使这个方法的任何部分都能被自动化(如debug)。
这么做会有什么影响?这才是你在程序设计过程中应该考虑的事,而不是收入怎么样,会带来什么个人成长。除了正在监控的指标之外,你的软件对全球用户的总体影响是什么?是否存在预期之外的不良副作用?在保留实用性的同时,你能做什么改善?
你设计的API会有用户,所以时刻关注用户体验。每当你做出一个决定,你都应该关注用户的感受,关注他们中的每一个人,无论是他们是初学者还是资深开发人员。
降低用户在使用你的API时的认知负荷。自动化一切可自动化的内容,最大限度地减少用户所需的操作和选择量,不要暴露不重要的选项,设计简单一致的工作流程,以反映简单一致的心智模型。
简单的事应该是简单的,复杂的事应该是有可能性的。不要为了利基功能块把简单问题复杂化,即便是最低限度。
如果工作流的认知负荷足够低,那么用户应该能在一次或两次亲身事件后就掌握整个流程(无需查阅教程或文档)。
设计一个符合领域专家和从业者心智模型的API。有这个领域经验但没有API经验的人应该能通过阅读最少的文档直观地理解你的API,比如查看代码示例、查找可用对象和它们的签名。
在没有任何关于底层实现的上下文的情况下,参数的含义应该是直观的、易于理解的。用户指定的参数应该和他们的需求有关,而不是和代码中的实现细节有关。API就是解决问题的工具,它不需要涉及软件如何在后台运行。
最强大的心智模型是模块化和层次化的:既注重高级别的简洁性,又兼顾精确性,包含需要了解详细信息。同样的,一个好的API也应该是模块化和层次化的:易于连接,又具有相当的表现力。它内部应该保持平衡,在较少对象上具有复杂签名,并且在简单签名上具有更多对象。
你的API会不可避免地反映你的实现选择,特别是数据结构。为了实现直观的API,你选择的数据结构必须适合对应的领域——这也是符合专家心智模型的一个方面。
设计一个端到端的工作流程,而不是一系列atomic features。大多数开发人员在设计API时,遵循的思路是“应该提供哪些功能?让我们为他们配置选项”。这并不合适,相反地,你应该问问自己“这个工具有哪些功能块?”“对于每个功能块,用户操作的最佳顺序是什么?”“为了支持整个工作流程,最方便的API是哪个?”API中的Atomic options应该能满足高级工作流程中出现的明确需求——所以,切记不要出现“人们也许需要它”的东西。
在用户使用API的过程中,错误信息是对他们操作的一种重要反馈。交互性和反馈是用户体验不可或缺的一部分,因此,设计它们时务必深思熟虑。
代码即通信,命名很重要——无论是项目还是变量,名称反映了你对问题的看法。在设计过程中,你不应使用过于通用的名称(如x, variable, parameter),也不要用过长、有特指含义的术语,或是会产生不必要误解的词(如master/slave)。遵循命名一致性,这意味着内部命名的一致性(如dim/axis)以及和问题内容保持一致。在确定名称前,参考其他API的命名方式是一种好办法。
文档是API用户体验的核心,它不是附加组件。在文档写作上花费一点精力,你也许会收获意料之外的惊喜。
图像是生动的,言语是苍白的:你的文档不应该只是介绍软件如何工作,它应该显示如何使用它。比如显示端到端工作流的代码示例,显示API的每个常见用例和关键功能的代码示例。
职业发展并不是指你管理的人数,而是指你的工作所产生的影响,它能否对世界造成些许改变。
软件开发需要团队合作,其中人际关系与技术能力同样重要。当你在职业道路上前行时,请保持团队间联系,做好团队中的一员。
技术永远不会是中立的。如果你的工作对世界产生了影响,那么这种影响就会存在道德取向。在软件产品中看似无害的技术选择会导致技术获取条件的调整,它决定着谁将受益、谁将受害:技术选择也是道德选择。因此,在道德设计上请始终谨慎而明确地表达你的价值观,并把你的价值观融入创作中。永远不要想“我只是搞技术,它是中立的”,因为你构建它的方式决定了它将被如何使用。
自我导向——为你工作和环境提供指引——它是生活满意度的关键。帮助身边的人建立起属于他们的自我导向,并确保你的职业选择能够让你成为更好的自己。
构建这个世界需要的东西,而不仅仅是你个人的小希望。很多时候,技术人员过着单纯的生活,只专注于满足产品的特定需求。但你应该尝试去寻求扩大生活体验的机会,让自己更好地了解世界的需求。
在面对长期问题时,你在做任何选择之前都应该进行长远打算,并把价值观置于短期自身利益和情绪之上,例如贪婪或恐惧。明确你的价值观,并让它指导你。
当发现自己和他人产生矛盾时,停一停,反思一下双方是否有着相同的价值观和共同目标,请提醒自己,大家是戮力同心的。
生产力由决策力和工作重心决定。这需要 a)良好的直觉,它来自经验,以便你可以在知之甚少的情况下做出尽可能靠谱的决定; b)能敏锐地意识到什么时候该谨慎行事,保持观望,因为一个错误决策的成本可能会比延期的成本更高。在不同环境中,时间至上和质量至上的决策权衡可能会带来很大的差异。
更快地决策意味着你在职业生涯中可以做出更多决策,这能锻炼你的“直觉”。经验是提高生产力的关键,而更高的生产力可以为你提供更多经验,这是一个良性循环。
如果你感觉自己不具备“直觉”,那么请把经验抽象成理论。你可以在整个职业生涯中建立一系列可靠且真实的原则:原则是形式化的直觉,它所适应的情景比原始的模式识别(需要大量直接的、类似的经验)更广泛。
原文地址:medium.com/@francois.chollet/notes-to-myself-on-software-engineering-c890f16f4e4d