他引用了Netscape的例子,结果在重写过程中公司就倒闭了,那么我认为你不应该重写

日期:2020-06-10 15:08:17 来源:互联网 编辑:小狐 阅读人数:456

CSDN编者按Stack Overflow创始人Joel Spolsky发布过一篇很有名的文章,他的观点是永远不要从零开始重写一段代码。他引用了Netscape的例子,他们花费好多年重写软件,结果在重写过程中公司就倒闭了。但是,本文已然选择从零开始重写应用程序,其中的原因是什么呢?

他引用了Netscape的例子,结果在重写过程中公司就倒闭了,那么我认为你不应该重写(图1)

Nicholas Tietz-Sokolsky

出品 CSDN(ID:CSDNnews)

大概一年多以前,我重读了这篇文章

事情发生在2019年1月,当时Remash还是一家很小的公司。我们雇佣了几名工程师,一共有5个人负责产品,还有一些工程师负责机器学习或运维。尽管雇佣了许多工程师,我们的速度依然很慢。即使是添加很简单的功能也要花很长时间。我们的产品中有许多bug,我们只能标记为“已知”而无力修复。而整个产品像是很久没有任何新改进一样。

重要的是要理解为什么会出现这些问题。我们假设(并在重写之后验证了这个假设)这些问题并不是人员的问题,我们雇佣的都是很强力的工程师。问题主要出在代码和过程上。旧的代码很差,因为我们以前的技术力不高,解决问题的方式也不好;而我们的过程会鼓励并依赖于孤岛的知识,因为我们没有真正的“全栈”

2019年1月代码的状态

旧的应用程序的设计目的与现在有很大差异。最初,Remesh的目的是在两个组或个人与组之间建立双向的对话。例如,你可以让民主党和党互相对话来理解互相的观点并求同存异。或者,可以让市场与市民对话以理解市民的需要和观点。但是,在调查了产品与市场的契合度之后,我们改变了用例。我们开始着力于一个人与一组人的对话。

由于这个变化,一些旧的设计决策不再适用,数据库结构也需要大幅改动。除了数据库问题之外,代码本身也很难理解,因为一直在添加新功能,而没有做过大的重构。最需要重构的地方,覆盖率很低,因为那些都是最古老的代码,都是在我们建立良好的实践之前编写的。

除了这些之外,语言和框架也并不适合团队。后台代码采用了Elixir,只有很少几名工程师理解。前端代码之一完全是使用旧版本的Angular编写的,我们还有另外两个前端,使用的是React。我们几乎没有工程师能精通一种技术,更别说三种了。语言和框架并不适合我们的团队,也不适合要解决的问题,这大大影响了我们的速度。

有什么选择?

当时,我们知道代码需要大的改变。在面对一堆很棘手的代码时,你的选择只有三个:

重构,直到问题解决

一次性重写

一点点重写

对于前端来说,重构并不可行。Angular的版本太老了,甚至都找不到合理的路径升级到现代的Angular(实际上我们也不想使用任何版本的Angular)由于我们的UI和API经历了重大变更,因此重构并不可行。所以对于前端而言,我们的选择只能是一次性重写,或者一点点重写。

后端有几个我们想解决的问题:数据库结构、语言以及那些无法再解决问题的代码。我们使用Elixir的原因是它强大的并发支持,但我们根本没有用到并发,所以它带来的只有痛苦—由于它在Erlang VM中处理并发的方式,导致性能非常困难,你只能看出做了什么,而无法看出从哪里调用。所以,性能优化只能是碰运气。Elixir的代码也限制了机器学习工程师对于后台代码的贡献程度,因为他们日常使用的是Python,没有时间深入学习Elixir。长话短说,我们希望放弃Elixir,完全迁移至Python,这样整个团队都可以贡献力量,该语言能支持所有问题,性能也更容易一些。

我们还有一些“产品债务”自于一些用户接受但不是太理想的概念。这些都是局限范围内的最优解。如果能打破这个局限并选择最好的方法,我们也许会一步到位。去掉这些不理想的概念需要一次性做很多事情。

最后我们选择重写,由于以下这些因素:

我们希望团队每个成员都能为后端贡献代码,因此最合适的语言是Python,它易习易用,而且在团队内有广泛的接受度。

旧的代码太不稳定,也不全面,因此重构风险很大。

采用一个有强烈意见的框架(如Django)可以获得效率,还能节省许多时间(如Django Admin)

我们有机会根据从客户那里收集到的信息制作一个全新的版本,而不必让每个客户都经历大量的小改动。这样客户培训也很容易,团队也只需要努力一次就可以,而不需要一直向客户灌输新概念。

最后,我们对于这个决定十分有信心,并且说服了公司的所有人。我们决定要重写,因为这样做可以改正多年来的错误,同时可以让产品前进一大步。

所以,让我们开始重写吧。

进展情况

在确定了需要包括哪些功能后,我们从2019年2月开始重写。所以我们的计划非常周密,特别是在需要做什么方面。这个计划并不敏捷,但做好计划,可以帮助我们一直朝着正确的方向前进。因为我们曾在用户阶段偏离过方向,而且在那之后偏离得越来越多。

在度过了坎坷的开头后,新版本的实际构建过程非常顺利。让所有人都切换到新的技术栈很痛苦。尽管我们为了让整个团队都能使用而选择了Python,但还是有些人需要学习。而且我们的后端或全栈工程师都不懂Django(尽管前端的首席工程师很熟悉)前端也类似,许多人都了解React,但很少有人有Type的经验。尽管如此,在度过了最初的一段学习期之后,大家的效率都很快提高了,能够更快地一起学习。这就是第一个验证:即使对于新技术栈的经验很少,我们也能以远远超过以前的速度来构建功能。我们花了很长时间才确定,这些效率来自于新的技术栈和新的代码,而不仅仅是因为开了一个新项目。

我们首先做的一件事情就是让所有人熟悉数据库。因为目标之一就是减少技术孤岛,让工程师在整个技术栈中感到舒适,因此我们带领不熟悉数据库的前端工程师思考数据库,并设计数据库的结构,与整个团队迭代。这样整个团队都会思考数据库。即使他们一段时间不处理数据库问题,也拥有了处理数据库的能力,并能提出有价值的问题。

在那之后,我们以很快的节奏进行了几个月,重写了旧版本中我们喜爱的功能,并做了改进。我们在合理的时间内建立了一个非常好的项目。最初我们对于时间计划十分乐观,直到六月份都能按部就班地完成工作。此时,我们不断添加并修改功能,因为我们知道,没有这些修改,新版本不会成功。这降低了我们的速度,但根据我们的研究人员、客户团队和一些值得信赖的客户的信息,做这些是值得的。

完成这一切后,我们有了一些非常引以为豪的成就,而且并不完全是技术成就:

我们大幅扩展了团队。最初产品团队只有4名工程师,现在有9名,其中还不包括一个全职的QA/SDET团队,还有机器学习工程师团队,以及几名运维人员。在这个成长中,我们不仅避免了通常项目中增加人手带来的延误,甚至还提高了速度。我认为主要是因为这是一个新项目。

我们改变了工程在整个公司中的地位。尽管最初降低了一些发布新功能的速度,但人们看到我们能够非常迅速地重写已有功能,并看到新功能依然能迅速添加。有一次我们实时演示了Django Admin添加的一个功能,告诉大家我们现在的速度比以前更快。尽管是一个很小的演示,但非常有效。

我们从包含几个服务的面向服务的架构转变成了仅有一个服务的单一服务架构,而且可以开始设计容错性和横向扩展等。这在以前是一件非常痛苦的事情。

我们极大地提高了迭代的速度,很大一部分原因是因为新架构可以很好地适应我们的问题,现在每个人都可以舒适地做贡献。最好的一点是,机器学习团队偶尔也可以给真正的产品后台贡献代码了。

经验和教训

我们认为,成功的原因有以下几个。此外,我们从几个非常重大的错误中学到了一些教训。

另一个成功的原因是尽早获取反馈、尽可能经常地获取反馈。在重写过程中,我们非常频繁地在内部使用产品,以发现重大错误和性能问题。我们还经常为整个公司进行演示,从客户团队、研究以及早期试用的客户那里获得反馈。

那么,我们有哪些失误呢?我们决定采用两种以前没有怎么用过的技术。我们之前在一个原型中用过Type,但并没有太多经验。当时用得不错,但还没有完全的把握它能提高生产率并降低出错的概率。时间会证明一切,我认为静态类型依然是正确的。另一个错误是采用了GraphQL。我们有很多REST和Redux的经验,但只在原型中使用过GraphQL。现在回想起来,有了GraphQL,最初的原型阶段很快,但需要付出长期的代价,因为Apollo中的一些关键性的决策我们并不能沟通(例如不给前端暴露检测连接断开或重新连接的接口)而且我们在后端调优方敏没有任何经验。我只能说,那一两个月是富有的一两个月,我绝不想再尝试第二次。我们现在正在逐步去掉GraphQL,一方面是为了提高性能,另一方面让代码能够更好地承受慢速请求。

关于重写,需要提及的最后一点就是,团队的士气会受到很大影响,你需要主动应对。刚开始一个新项目时大家的兴致都很高涨,但在构建了一些已有功能并修复一些bug之后,士气就会逐步滑落。从构建已有功能转到探索新功能,大家都很活跃,但重新构建已有功能就会很失落。尽管我们完成了重新构建,但一部分原因是我们在重新构建的同时也兼顾了探索新功能(毕竟这是重新构建的原因之一)而不仅仅是将旧的代码迁移到新的平台上。尽管如此,我们在这方面应该能平衡得更好。如果还有下次,我会着重确保让一些值得信赖的客户参与早期内部,并获得反馈和鼓励,让每个人都能对重新构建充满动力。我还会确保我们能在前期就一些新功能,而不是让旧功能拖得大家精疲力尽。一些无聊是不可避免的,但可以尽力减轻。

应该重写吗?

根据我的经验,如果你认为重写是错误的话,那么我认为你不应该重写。在任何情况下,你都应该默认选择“否”尽最大努力判断重写是否真的有必要。

如果是如下情况,则可能有必要重写:

如果这些问题严重地降低了团队的速度

如果当前技术栈非常限制团队的贡献,而且很难训练团队学习技术栈

即使满足以上所有条件,你也要考虑业务上的现状,以及重写是否适合你的团队和公司。

除了上述几点之外,也许还要考虑其他因素。作判断很难,但我们需要三思而后行才能成功。

本文相关词条概念解析:

重写

当一个子类继承一父类,而子类中的方法与父类中的方法的名称,参数个数、类型都完全一致时,就称子类中的这个方法重写了父类中的方法。通常,派生类继承基类的方法,因此,在调用对象继承方法的时候,调用和执行的是基类的实现.但是,有时需要对派生类中的继承方法有不同的实现.

网友评论
相关文章
弄得公司快倒闭了,拉着女朋友就跑

弄得公司快倒闭了,拉着女朋友就跑

弄得公司快倒闭了,拉着女朋友就跑[详情]

武磊的租借期是半年,可以肯定的是,我们等他回来一起打西甲

武磊的租借期是半年,可以肯定的是,我们等他回来一起打西甲

武磊的租借期是半年,可以肯定的是,我们等他回来一起打西甲[详情]

高中生读红楼,推陈出新,红楼梦中引用了大量的诗词文学作品

高中生读红楼,推陈出新,红楼梦中引用了大量的诗词文学作品

高中生读红楼,推陈出新,红楼梦中引用了大量的诗词文学作品[详情]

网站地图    Copyright     2016-2018  资讯网   All rights reserved.