Open javanli opened 3 years ago
由于本文涉及比较多的负面评价,因此以Q项目代称。
简单交代一下背景。
Q项目是腾讯一个iOS应用,代码规模大概中百万行,开发人数几百人。
本人17年下半年本科毕业应届入职腾讯进入Q项目做iOS开发,20年初离职,现在回头想想这个项目的环境,仍是十分感慨。
大概从以下几个方面稍作探讨:
这个项目一直是没有一个明确框架的,但存在一些模糊的演进。
项目在19年之前是没有引入CocoaPods的,所有代码都堆在一个仓库,xcode项目下有几个子工程结构。
管理上有模糊的团队职责划分,比如好友团队、聊天团队等等,但映射到代码层面,边界是不清晰的,比如很多工具类就没有明确负责人,谁都不想管。
另外由于代码层面没有按团队职责解耦,很多代码是各个团队都经常去改一下的,比如聊天页面,虽然是聊天团队负责,但是好友团队也可能会往里面加功能,跳出来之后看,比较合理的做法是让其它业务团队向聊天团队提需求,让聊天团队去修改,才能保证代码的长期可维护。但当时就是各个业务团队自己直接进去改的。
总之,一个非常混乱的项目,完全不该是一个21世纪的大型工程该有的水平。当时我们隐隐约约能够意识到这种问题,但对这个项目到底有多烂其实并没有明确的认知,可能也没有烂太多呢?稀里糊涂地过呗。
众所周知,这种规模特别大的项目放在一个工程里,编译耗时非常久,IDE压力也很大,导致拖慢开发效率。因此大概是在19年的时候,此项目开始进行模块化拆分。
其实模块化拆分的更大意义在于逻辑解耦,能够很大程度上提升整体项目质量的下限。但当时Q项目团队还不太有这个意识,主要目的是为了优化编译耗时。
现在回头看,同时期业界大型应用已经有了比较成熟的基于CocoaPods的模块化方案。但Q项目这个闭塞的环境下,大家普遍对此认知非常浅薄。当时可能有个别同学有一定认知,但由于Q项目烂透了的技术管理体系并没能发声。
总之,这任务被摊派给了平台组的R同学。R同学经过一番冥思苦想和组内小范围的技术讨论后,硬怼出了一套模块管理方案。也不能算是完整的一套了,主要是写了一些Python脚本,能够帮助拆分好的模块编译.a静态库,并让主工程的每个模块在静态库编译和源码编译间切换。
.a
我个人对R同学的评价是很高的,在不了解一个正常的模块化方案应该如何搭建的情况下,凭自己的理解,硬撸一套这样的脚本,并凭一己之力拆分出大部分业务模块,这个底子是真的很扎实,只是受限于当时Q项目整体的狭窄视野不知道怎么做才能更好,他好像也是应届进来没几年,强求他有更高的认知是不现实的。
总之,搞出了个缝合怪,对手Q算是进步,但仍然远远落后于时代。另外同阶段也引入了CocoaPods,但除了引入少量开源库,以及极少量的无依赖代码拆过去之外,没有对整体造成什么影响。
其实那个时候我们是知道CocoaPods的,只是没有深度使用,也不知道基于CocoaPods的成熟模块化方案。我没有跟R同学深入交流过,也就不清楚他为什么不选择在CocoaPods上做模块化。但后来经过回忆复盘,我觉得可能是因为一个小问题:CocoaPods的模块是不能依赖主工程的。几百万行的代码意味着没有人有能力一次性拆完 ,而现有代码的耦合非常严重,拆出来的模块和主工程间仍有错综复杂的耦合,不解决这些耦合,就没办法用CocoaPods管理这些拆出来的模块。比较直接的思路是从无依赖的底层库开始,慢慢迁移到CocoaPods,但这无法短时间内解决编译耗时的问题。所以R同学选择自己撸一套,模块通过子工程引入,是可以反向依赖主工程的。其实基于CocoaPods的话,解决方案也非常简单,开一个叫Main之类的库,把主工程整个丢进去,再往外拆,就可以渐进式地搞了。但是当时处在思维盲区,其实非常非常难想到。
出来之后发现,Q项目的基础组件跟开源组件比起来,匪夷所思的难用。以配置系统为例(当时ABTest也是基于这套配置系统),配置key必须先在系统上申请,端上的组件只在初次下载和有更新时通知出去,业务方自己存储自己的配置。但其实存储这块逻辑是很容易写出点问题的,业务方一旦没存好配置,就真的丢了。
而业界常用的配置系统,存储是组件方做的,key也不需要先申请,可以在端上写个默认配置先用着,需要改的时候再去系统上下发就是了。就好用很多,对比之下Q项目的配置系统简直连个demo都不如。
前面提到的烂都集中在代码层面,但代码层面的烂,意义是不够的。从技术品味角度,一切烂代码都是不对的。但落在实际项目上,我们需要更深入的思考,烂代码带来的负面影响是什么,有多大的必要去优化。
我曾经一度怀疑,提升代码质量有什么意义?看看这几百万行的shit,还能更烂吗?但是它也好好跑着不是吗?
如今跳出去一年半了,我大概可以回答这个问题了:有意义,即使抛开技术品味这类虚的理由,以及可扩展性这种当前不太痛的理由,仍然很有意义。
腐烂的代码给Q项目的负面影响主要有如下几点:
影响开发效率,拖慢版本节奏。
Q项目大体上是一月一版。这一个月的时间里,通常纯开发时间在2周左右,1周是边测试边修bug,还有一周说不清道不明,当初换领导的时候项目经理盘了半天没搞明白这一周到底是怎么被吃掉的,其实就是代码太烂,解决各种乱七八糟问题去了,这些问题太杂以至于很难明确分类,但是摊下来确实每个版本差不多能吃掉一周工作量。跳出来后的经历告诉我,同样的工作量,在一个质量优秀的项目里,完全可以压缩到2周一版。
这个影响其实比成本更大。在大型互联网项目中,人力成本占比虽然不小,但是高一点低一点其实影响没有那么明显。但移动互联网时代迭代速度是非常大的优势,本就有些掉队的Q项目,迭代还慢,那不就更赶不上了?
增加测试成本。
影响产品功能AB。
线上bug频出。
总而言之,虽然项目仍然可用,但劣质代码客观上拖累了Q项目的产品竞争力(虽然抛开技术问题,竞争力也不行,但这是另外的问题)。
最后追溯一下深层原因吧,为什么腾讯这样一个互联网巨头,里面一个这种体量的项目,能搞得这么烂呢?
由于本文涉及比较多的负面评价,因此以Q项目代称。
简单交代一下背景。
Q项目是腾讯一个iOS应用,代码规模大概中百万行,开发人数几百人。
本人17年下半年本科毕业应届入职腾讯进入Q项目做iOS开发,20年初离职,现在回头想想这个项目的环境,仍是十分感慨。
大概从以下几个方面稍作探讨:
1. 项目框架和模块化
这个项目一直是没有一个明确框架的,但存在一些模糊的演进。
混沌阶段
项目在19年之前是没有引入CocoaPods的,所有代码都堆在一个仓库,xcode项目下有几个子工程结构。
管理上有模糊的团队职责划分,比如好友团队、聊天团队等等,但映射到代码层面,边界是不清晰的,比如很多工具类就没有明确负责人,谁都不想管。
另外由于代码层面没有按团队职责解耦,很多代码是各个团队都经常去改一下的,比如聊天页面,虽然是聊天团队负责,但是好友团队也可能会往里面加功能,跳出来之后看,比较合理的做法是让其它业务团队向聊天团队提需求,让聊天团队去修改,才能保证代码的长期可维护。但当时就是各个业务团队自己直接进去改的。
总之,一个非常混乱的项目,完全不该是一个21世纪的大型工程该有的水平。当时我们隐隐约约能够意识到这种问题,但对这个项目到底有多烂其实并没有明确的认知,可能也没有烂太多呢?稀里糊涂地过呗。
跑偏了的模块化
众所周知,这种规模特别大的项目放在一个工程里,编译耗时非常久,IDE压力也很大,导致拖慢开发效率。因此大概是在19年的时候,此项目开始进行模块化拆分。
其实模块化拆分的更大意义在于逻辑解耦,能够很大程度上提升整体项目质量的下限。但当时Q项目团队还不太有这个意识,主要目的是为了优化编译耗时。
现在回头看,同时期业界大型应用已经有了比较成熟的基于CocoaPods的模块化方案。但Q项目这个闭塞的环境下,大家普遍对此认知非常浅薄。当时可能有个别同学有一定认知,但由于Q项目烂透了的技术管理体系并没能发声。
总之,这任务被摊派给了平台组的R同学。R同学经过一番冥思苦想和组内小范围的技术讨论后,硬怼出了一套模块管理方案。也不能算是完整的一套了,主要是写了一些Python脚本,能够帮助拆分好的模块编译
.a
静态库,并让主工程的每个模块在静态库编译和源码编译间切换。我个人对R同学的评价是很高的,在不了解一个正常的模块化方案应该如何搭建的情况下,凭自己的理解,硬撸一套这样的脚本,并凭一己之力拆分出大部分业务模块,这个底子是真的很扎实,只是受限于当时Q项目整体的狭窄视野不知道怎么做才能更好,他好像也是应届进来没几年,强求他有更高的认知是不现实的。
总之,搞出了个缝合怪,对手Q算是进步,但仍然远远落后于时代。另外同阶段也引入了CocoaPods,但除了引入少量开源库,以及极少量的无依赖代码拆过去之外,没有对整体造成什么影响。
其实那个时候我们是知道CocoaPods的,只是没有深度使用,也不知道基于CocoaPods的成熟模块化方案。我没有跟R同学深入交流过,也就不清楚他为什么不选择在CocoaPods上做模块化。但后来经过回忆复盘,我觉得可能是因为一个小问题:CocoaPods的模块是不能依赖主工程的。几百万行的代码意味着没有人有能力一次性拆完 ,而现有代码的耦合非常严重,拆出来的模块和主工程间仍有错综复杂的耦合,不解决这些耦合,就没办法用CocoaPods管理这些拆出来的模块。比较直接的思路是从无依赖的底层库开始,慢慢迁移到CocoaPods,但这无法短时间内解决编译耗时的问题。所以R同学选择自己撸一套,模块通过子工程引入,是可以反向依赖主工程的。其实基于CocoaPods的话,解决方案也非常简单,开一个叫Main之类的库,把主工程整个丢进去,再往外拆,就可以渐进式地搞了。但是当时处在思维盲区,其实非常非常难想到。
2.基础组件
出来之后发现,Q项目的基础组件跟开源组件比起来,匪夷所思的难用。以配置系统为例(当时ABTest也是基于这套配置系统),配置key必须先在系统上申请,端上的组件只在初次下载和有更新时通知出去,业务方自己存储自己的配置。但其实存储这块逻辑是很容易写出点问题的,业务方一旦没存好配置,就真的丢了。
而业界常用的配置系统,存储是组件方做的,key也不需要先申请,可以在端上写个默认配置先用着,需要改的时候再去系统上下发就是了。就好用很多,对比之下Q项目的配置系统简直连个demo都不如。
3.影响和溯源
前面提到的烂都集中在代码层面,但代码层面的烂,意义是不够的。从技术品味角度,一切烂代码都是不对的。但落在实际项目上,我们需要更深入的思考,烂代码带来的负面影响是什么,有多大的必要去优化。
我曾经一度怀疑,提升代码质量有什么意义?看看这几百万行的shit,还能更烂吗?但是它也好好跑着不是吗?
如今跳出去一年半了,我大概可以回答这个问题了:有意义,即使抛开技术品味这类虚的理由,以及可扩展性这种当前不太痛的理由,仍然很有意义。
腐烂的代码给Q项目的负面影响主要有如下几点:
影响开发效率,拖慢版本节奏。
Q项目大体上是一月一版。这一个月的时间里,通常纯开发时间在2周左右,1周是边测试边修bug,还有一周说不清道不明,当初换领导的时候项目经理盘了半天没搞明白这一周到底是怎么被吃掉的,其实就是代码太烂,解决各种乱七八糟问题去了,这些问题太杂以至于很难明确分类,但是摊下来确实每个版本差不多能吃掉一周工作量。跳出来后的经历告诉我,同样的工作量,在一个质量优秀的项目里,完全可以压缩到2周一版。
这个影响其实比成本更大。在大型互联网项目中,人力成本占比虽然不小,但是高一点低一点其实影响没有那么明显。但移动互联网时代迭代速度是非常大的优势,本就有些掉队的Q项目,迭代还慢,那不就更赶不上了?
增加测试成本。
影响产品功能AB。
线上bug频出。
总而言之,虽然项目仍然可用,但劣质代码客观上拖累了Q项目的产品竞争力(虽然抛开技术问题,竞争力也不行,但这是另外的问题)。
最后追溯一下深层原因吧,为什么腾讯这样一个互联网巨头,里面一个这种体量的项目,能搞得这么烂呢?