TheHiddenCostsThatEngineersIgnoreHours”。但在我们完成后,我们发现每隔几周,我们要么修复功能中的错误,向另一位工程师解释它,要么做客户服务回答问题来解释它是如何工作的。维护功能的总投资远远超过初始开发的时间。软件开发中最难吸取的教训之一是额外复杂性的隐性成本。有时,复杂性只是问题领域固有的。通过调整价格来平衡供需匹配骑手和司机是一个复杂而痛苦的问题,因此,在扩大社群和维护社群质量时,对喜欢回答和阅读问题的人进行问答整理也是如此。或者像开发一个丰富的文档编辑器兼容所有设备以支持实时协作。这是我们需要适应产品才能成功的固有复杂性ul。但其他时候,我们与之竞争的复杂性恰恰是我们这一代人的复杂性。我们用一种新的编程语言编写代码,很少有人理解它,现在我们必须维护它。或者我们添加了额外的基础设施,因为我们尝试了从HackerNews看到的一项热门新技术,但它失败了,这是我们一开始没有想到的。或者我们介绍了一个很少有人使用的功能,但修复和错误报告花费了不成比例的时间。额外的复杂性暴露出许多隐性成本。在开发软件时,我们所做的决定不仅仅决定了我们当前的开发速度。它们还反映了我们在维护上花费的时间和精力。复杂性的隐性成本太多的复杂性会增加认知负担,并对完成工作造成额外的阻力。它以多种不同方式渗透到团队中——主要是直接渗透到代码、系统和产品的复杂性中,但间接渗透到组织的复杂性中。让我们逐一看看这些不同类型的复杂性的隐性成本。代码复杂性代码复杂性不仅仅作为代码行数的线性函数增长——它以组合方式增长。在复杂的代码库中,每一行代码都可能相互作用并影响许多其他代码行。我们没有得到足够的组合增长,这就是为什么我们倾向于严重低估完成大型软件项目所需的时间。这也是重写项目有时会出现明显延迟的主要原因。当代码过于复杂时,就很难扩展、理解原因、修复错误以及跟踪依赖关系和错误起源的数据流。工程师可能会主动避开代码库中最复杂的部分,即使是进行某种更改的最合乎逻辑的地方,并选择绕行。他们可能会避免将这些地方结合起来,即使这项工作有很多分支。系统复杂性工程师喜欢修补新玩具,要么是因为他们很好奇,要么是因为他们认为新技术可能为解决他们紧迫的问题提供灵丹妙药。当Pinterest在2011年首次开始扩展他们的网站以应对快速增长时,他们只有3名工程师的后端团队正在使用6种不同的存储技术(MySQL、Cassandra、Membase、Memcache、Redis和MongoDB)。他们试验的每一项新技术都有望解决现有系统的一些局限性。但是,每个新解决方案都以其特定的方式失败,需要更多的时间和精力来管理和维护。最终,该团队发现通过添加更多机器而不是更多技术更容易扩展,因此他们淘汰了Cassandra和MongoDB等系统,并加强了架构的现有组件。将您的基础设施分割成太多系统会产生很多隐性成本。注意力分散在多个系统中。对于每个系统来说,更难集中资源开发一个可重用的资源库,更难招募新人进行日常工作,更难了解每个系统的具体故障模式和性能特征。每个系统的抽象最终会变弱,因为没有太多时间投入其中。当工具和抽象过于复杂或数量过多时,团队就很难理解和探索。产品复杂性产品复杂性会导致模棱两可的发布,或者导致没有产品重点的肆无忌惮的野心。它希望在很多地方都很棒,而不仅仅是在一个核心领域,这使得无法向新用户清楚地解释产品的意图。产品复杂性导致更多代码和系统复杂性——团队添加更多代码、更多基础设施来支持新功能。当产品范围很广时,添加新功能或修改现有功能将需要付出更大的努力来理解和适应旧功能。过于复杂的产品意味着更多的代码分支、更多的问题需要考虑,以及更多的错误反馈需要团队解决。工程师和数据科学家需要分析更多的变量,做更多的一次性报告,而不是专注于理解核心用户行为。工程师需要投入更多的时间在提供功能空间和提高效率上。每个人最终都会在更多项目之间切换。花在维护所有这些特性上的时间不是再投资于代码、偿还技术债务和强化抽象的时间。组织复杂性代码、系统和产品的复杂性反过来又造成了组织的复杂性。团队需要雇佣更多的人来处理和维护已经开发的一切。更大的团队意味着更多的沟通成本、更多的协调和更低的整体效率。招聘过程本身,包括所有的面试和汇报,占用了团队很大一部分时间。当然,所有新员工都必须接受培训才能上岗。雇用更多人的另一种方法是将工程部分分成更小的团队——甚至可以创建单人团队——以承担更多的代码、系统和产品外围工作。这样可以降低沟通成本,但是单人团队有自己的成本。一旦你陷入困境,你就完全拖延了项目中的唯一一个人,因为分享那些低谷的人越来越少,而且这种经历对士气不利。与他人合作的机会减少会损害工作场所的幸福感和员工保留率。除非每个人都有自我意识并主动征求反馈,否则个人会收到较少的工作反馈,因为更少的人共享相同的项目背景。减少反馈会降低代码质量,或无意中将复杂性引入代码库或基础架构中。如何应对复杂性TonyHoare在1980年的图灵奖演讲中提出,“构建软件设计有两种方法:一种是使其简单且明显没有缺陷;另一种是使其复杂而没有明显缺陷。“”提到由于复杂性导致的非显而易见的缺陷如何伤害我们,我们如何处理这些成本?以下是您可以使用的一些策略:为简单性而优化。抵制增加更多复杂性的声明。深入思考维护成本。问问自己,为解决20%的问题而引入的复杂性是否值得,或者80%的解决方案是否足够。为您的团队或产品定义使命宣言以统一思想。在TeamGeek中,BrianW.Fitzpatric和BenCollins-Sussman解释了他们如何指导GoogleWebToolkit(GWT)团队并鼓励他们编写使命宣言。随后对使命宣言的内容和形式的争论表明,总工程师并不真正认同产品方向!他们被迫面对、协商分歧,最终达成了“GWT的使命是从根本上改善用户的Web体验,并允许开发人员使用现有的Java工具在任何现代浏览器中构建高性能的AJAX”。如果他们无法及早发现这些区别,那么随之而来的努力会分散多少注意力?从较小的构建块组成大型系统。谷歌就是一个例子,致力于构建一个健壮的核心抽象,然后被非常广泛的应用程序广泛使用。它们具有基本的构建块,例如ProtocolBuffers、Google文件系统和用于远程过程调用的Stubby服务器。在这些构建块之上,他们构建了其他抽象,例如MapReduce和BigTable。在此之上,构建了数千个应用程序,包括大规模网络索引、GoogleAnalytics站点跟踪、GoogleNews聚合、GoogleEarth数据处理、GoogleZeitgeist数据分析等等。明确定义模块和服务之间的接口。模块和服务的解耦将降低可以生成一堆代码的组合的复杂性。在亚马逊,JeffBezos于2002年宣布公司将转向面向服务的架构,所有团队只能通过服务级接口相互通信。尽管这种转变本身造成了巨大的开发成本,但它强制分离了代码和服务背后的逻辑,这促进了如今极为成功的AmazonWebServices的建立。优先偿还技术债务。我们总是在信息不完整的情况下开发软件。随着代码库随着条件的变化而增长,熵也在增长。增加的复杂性是以未来发展为代价的。在开发例程中预算时间可以降低此成本。许多工程师和团队会在项目之间对这段时间进行预算,但一次性会议会有所帮助。我过去常常在Quora上组织代码清除日,工程师们都在讨论如何删除死代码。我们在记分板上跟踪代码删除的进度,这让您删除自己的代码更加有趣。使用无用的数据修剪功能。在Yammer,当工程师或产品经理发现在代码重构期间强化或保留某项功能将花费大量时间时,他们将查看使用数据以确定该功能是否实际被使用。如果没有,他们将与团队一起决定是否应该终止该功能以减少整体工作量。这种策略减少技术债务的方式与简化代码减少技术债务的方式相同。根据主题对正在进行的项目进行分组。使同事能够彼此共享相同的环境,从而更轻松地参与设计讨论、代码审查或构建可重用的存储库。所有这些活动都有助于制衡一个人或另一个人提出的问题。当我们为学校课程开发软件时,我们对世界有一个简单化的看法——维持任意复杂性的成本随着离开课堂而消失。但在我们的职业生涯中,糟糕的软件决策将产生多年的沉重影响。不要把事情复杂化。原文链接:http://www.labazhou.net/2015/01/hidden-costs-that-engineers-ignore/
