软件设计的哲学:第三章 编程的战术和战略
“作者简介: 常柱,微信公众号【架构未来】作者,十多年一线互联网研发从业经验;前五八同城商业会员技术负责人,宝驾租车技术总监,现58 到家业务中台技术负责人。
好的软件设计最重要的元素之一是在处理编程任务时采用的思维方式。许多组织鼓励一种战术心态,专注于让特性尽可能快地工作。然而,如果你想要一个好的设计,你必须采取一种更有策略的方法,投入时间来产生干净的设计并解决问题。本章讨论了为什么战略方法可以产生更好的设计,并且从长远来看实际上比战术方法更便宜。
3.1 战术的编程
大多数程序员使用一种我称为战术编程的思维方式来进行软件开发。在战术方法中,您的主要关注点是让某些东西工作起来,比如一个新特性或一个 bug 修复。乍一看,这似乎是完全合理的:还有什么比编写有效的代码更重要呢?然而,战术规划使它几乎不可能产生一个良好的系统设计。
战术编程的问题在于它是短视的。 如果你在战术上编程,你是在试图尽快完成一项任务。也许你有一个艰难的最后期限。因此,规划未来并不是优先事项。你不会花很多时间去寻找最好的设计;你只是想尽快开始工作。你告诉自己,如果可以让当前的任务更快完成,增加一点复杂性或者引入一两个小的封装是可以的。
这就是系统变得复杂的原因。如前一章所述,复杂性是递增的。使一个系统变得复杂的不是某一件特定的事情,而是数十个或数百个小事情的积累。如果你有策略地编程,每个编程任务都会增加一些复杂性。为了快速完成当前的任务,它们中的每一个似乎都是合理的妥协。然而,复杂性迅速增加,特别是如果每个人都在战术上编程。
不久之后,一些复杂的问题就会开始产生,你会开始后悔当初没有走那些捷径。但是,您会告诉自己,让下一个功能正常工作比返回并重构现有代码更重要。从长远来看,重构可能会有所帮助,但它肯定会降低当前任务的速度。因此,您需要寻找快速补丁来解决遇到的任何问题。这只会产生更多的复杂性,这就需要更多的补丁。很快,代码就乱成一团了,但到目前为止,情况已经非常糟糕,需要几个月的时间才能清理干净。你的时间表不可能容忍这样的延迟,而且解决一两个问题似乎也不会有太大的不同,所以你只是在战术上继续编程。
如果您已经在一个大型软件项目中工作了很长时间,我怀疑您已经在工作中看到过战术编程,并体验过它所导致的问题。一旦你开始走上战术道路,就很难改变。
几乎每个软件开发组织都至少有一个将战术编程发挥到极致的开发人员:战术旋风。战术旋风是一个多产的程序员谁泵出的代码比别人快得多,但工作在一个完全战术的方式。 当涉及到实现快速特性时,没有人比战术性 tornado 完成得更快。在一些组织中,管理层将战术旋风视为英雄。然而,战术旋风留下了破坏的尾迹。他们很少被将来必须使用他们的代码的工程师视为英雄。通常,其他工程师必须清理战术旋风留下的混乱,这使得那些工程师(真正的英雄)看起来比战术旋风进展缓慢。
3.2 战略规划
成为一名优秀的软件设计师的第一步是认识到仅仅为了完成工作编写代码是不够的。为了更快地完成当前的任务而引入不必要的复杂性是不可接受的。最重要的是这个系统的长期结构。 任何系统中的大多数代码都是通过扩展现有的代码库来编写的,因此作为开发人员,您最重要的工作就是促进这些未来的扩展。因此,您不应该认为“工作代码”是您的主要目标,尽管您的代码当然必须工作。您的主要目标必须是产生一个伟大的设计,这也碰巧工作。这是战略规划。
战略规划需要一种投资心态。 您必须投入时间来改进系统的设计,而不是以最快的方式来完成当前的项目。这些投资在短期内会让你慢下来一点,但在长期内会让你加快速度,如图 3.1 所示。
一些投资将是积极的。例如,花一点额外的时间为每个新类找到一个简单的设计是值得的;与其实施第一个出现在脑海中的想法,不如尝试几个替代的设计,选择最干净的一个。试着想象一些系统在未来可能需要改变的方式,并确保你的设计是简单的。编写好的文档是主动投资的另一个例子。
其他投资将是被动的。 无论您预先投入多少,在您的设计决策中都会不可避免地出现错误。随着时间的推移,这些错误将变得显而易见。当你发现一个设计问题时,不要忽视它或修补它;花一点额外的时间来修复它。如果您有策略地进行编程,您将不断地对系统设计进行小的改进。这与战术编程相反,在战术编程中,您不断地添加小的复杂性,这些复杂性会在将来导致问题。
3.3 投资多少?
那么,正确的投资额是多少呢?一笔巨大的前期投资,比如试图设计整个系统,是不会有效的。这就是瀑布法,我们知道它行不通。随着您对系统的经验的积累,理想的设计往往会零零碎碎地出现。因此,最好的方法是在连续的基础上进行大量的小额投资。 我建议您将总开发时间的 10-20%用于投资。这个量足够小,不会对您的日程安排产生重大影响,但是足够大,随着时间的推移会产生显著的好处。因此,您最初的项目将比纯战术方法多花费 10-20%的时间。这些额外的时间将导致更好的软件设计,并且您将在几个月内开始体验这些好处。用不了多久,你的开发速度就会比战术编程至少快 10-20%。在这一点上,你的投资是免费的:从你过去的投资中获得的收益将节省足够的时间来弥补未来投资的成本。你将很快收回最初投资的成本。图 3.1 说明了这种现象。
图 3.1:在开始阶段,一种战术的编程方法将比一种战略方法更快地取得进展。然而,在战术方法下,复杂性积累得更快,从而降低了生产率。随着时间的推移,战略方法取得了更大的进展。注意:这个数字只是一个定性的说明;我不知道任何经验测量的准确曲线形状。
相反,如果你有策略地进行编程,你将会以 10-20%的速度完成你的第一个项目,但是随着时间的推移,你的开发速度会随着复杂性的增加而减慢。用不了多久,您的编程速度至少会降低 10-20%。您将很快地归还您在开始时节省的所有时间,并且在剩下的系统生命周期中,您将比采用策略方法时开发得更慢。如果您从未在严重降级的代码库中工作过,请与曾经工作过的人交谈;他们会告诉你,糟糕的代码质量至少会降低 20%的开发速度。
3.4 创业与投资
在某些环境中,有强大的力量反对战略方法。例如,处于早期阶段的初创公司会感到巨大的压力,要求他们尽快发布自己的早期版本。在这些公司,似乎 10-20%的投资都是负担不起的。因此,许多初创公司采取战术性的方法,在设计上花费的精力很少,在出现问题时进行清理的时间更少。他们认为这样做是合理的,如果他们成功了,他们就会有足够的钱聘请更多的工程师来清理垃圾。
如果您所在的公司倾向于这个方向,那么您应该意识到,一旦代码库变成了意大利面条,就几乎不可能修复了。您可能要为产品的生命周期支付高昂的开发成本。此外,好的(或坏的)设计的回报来得很快,所以很有可能战术方法甚至不会加快您的第一个产品发布。
另一件需要考虑的事情是,公司成功最重要的因素之一是工程师的素质。降低开发成本的最佳方法是雇佣优秀的工程师:他们的成本并不比平庸的工程师高多少,但却拥有惊人的高生产率。 然而,最好的工程师都非常关心好的设计。如果你的代码库是一个残骸,消息会传出去,这将使你更难招募。结果,你很可能以平庸的工程师而告终。这将增加您未来的成本,并可能导致系统结构进一步退化。
Facebook 就是一个鼓励战术编程的初创公司。多年来,公司的座右铭是“快速行动,打破常规”。“刚从大学毕业的新工程师被鼓励立即投入到公司的代码库中;对于工程师来说,在工作的第一周将提交推进到生产中是很正常的。从积极的一面来看,Facebook 建立了一个赋予员工权力的公司声誉。工程师有很大的自由度,几乎没有什么规则和限制来阻碍他们。
Facebook 作为一家公司已经取得了惊人的成功,但它的代码基础却因为公司的战术方法而受损;很多代码都不稳定,很难理解,只有很少的注释和测试,而且很难处理。随着时间的推移,公司意识到它的文化是不可持续的。最终,Facebook 改变了它的座右铭,“以坚实的基础设施快速发展”,以鼓励它的工程师在好的设计上投入更多。Facebook 能否成功地解决多年战术编程积累的问题仍有待观察。
公平地说,我应该指出,Facebook 的代码可能并不比创业公司的平均水平差多少。战术编程在初创公司中很常见;Facebook 就是一个特别明显的例子。
幸运的是,在硅谷,战略方法也有可能获得成功。谷歌和 VMware 与 Facebook 差不多是在同一时期成长起来的,但这两家公司都采取了更具战略性的策略。两家公司都非常重视高质量的代码和良好的设计,并且都构建了能够用可靠的软件系统解决复杂问题的复杂产品。两家公司强大的技术文化在硅谷广为人知。很少有公司能与他们竞争顶尖的技术人才。
这些例子表明,一个公司可以用任何一种方法取得成功。然而,在一家关心软件设计并拥有干净代码库的公司工作要有趣得多。
3.5 结论
好的设计不是免费的。它必须是你不断投资的东西,这样小问题就不会积累成大问题。 幸运的是,好的设计最终会收回成本,而且比你想象的要快。
在应用战略方法时保持一致是至关重要的,要把投资看作是今天要做的事情,而不是明天要做的事情。当你陷入困境时,很容易把清理工作推迟到危机结束后。然而,这是一个滑坡;在当前的危机之后,几乎可以肯定会有另一个危机,在那之后又会有一个。一旦你开始延迟设计改进,延迟就很容易变成永久性的,你的文化也很容易滑入战术方法。解决设计问题的时间越长,问题就越大;解决方案变得更加令人生畏,这使得人们更容易把它们推迟。最有效的方法是让每个工程师持续地对好的设计进行小的投资。
“免责声明:本文翻译仅供学习使用,本文的版权归英文原作者或出版方,若有侵权,请联系删除。