分情况,大部分时候如果你完全不明白这个代码是怎么回事,那可能是因为你的水平和经验及不上最初设计这套东西的人,许多在他看来非常浅显易懂的设计理由是你完全没有想过的,还有一些奇怪的地方可能是遇到了问题但没能完全解决掉的workaround,或者因为需求的特殊性(比如特别苛刻的性能要求)无可奈何做的取舍。这种没有搞明白的情况下为了省去搞明白的功夫直接重写(这种不应该叫重构)是非常危险的,已知你的水平不及或者至少是不高于前代设计者,而前代设计者踩过的坑你也完全不知晓,而周边与这段代码配合的代码或者软件已经变成和它耦合的形状了,你要在这种摸瞎的情况下做出个一模一样完全匹配的东西出来是几乎不可能的,最常见的结果是要么花了大时间做了新版本最后一直测试不过上不了线,要么强行上线然后天天出问题。
如果你的水平显著高于之前的设计者,你应该能从代码里很清晰地明白他到底为什么做了这样的设计,并且同时明白这种设计远非最优选择,犯的错误主要有哪几点,并且自己尝试添加或者修复的功能为什么会直接和这个设计冲突;甚至最初的设计思想是什么、途中开发时变成了什么样、后期维护时又变成了什么样,中间哪一段是明显为了应付出现的问题随手糊上去的,哪一段是后来的维护者不理解最初设计做出的错误变更等等。这种时候才是一个适合真正重构的时机,你可以将当前代码的设计和更优的设计之间做一个折衷,在尽量保留现有成果的情况下,提供一个渐进式过渡到新设计的方案,然后逐步逐步替换掉实现错误的代码,并且有针对性地补充测试。
注意,完全不修改、继续沿用现在的设计也是一种折衷的方式,这完全取决于你对未来这段代码发展方向的预期。另外,这种理解不代表逐字逐句理解每一行,只要在整体上明白思路、有分隔影响范围的手段、然后在需要改动的局部进行细致分析即可。
别说几千行了,几千万行劳资都可以
维护过一个神奇的软件叫3ds Max,总代码量大概八千万行,其中据统计活代码三千万行(就是用户经常能使用到的代码)
不过具体到工作经常需要接触到的模块,也就几百万行的样子,有些很成熟的部分,比如图像读写之类的,基本上不用管它
然后整体大规模的重构基本不太可能,主要不是做不到,是因为没这个动力,搞的再牛逼也不一定能给公司带来更多的收入,而且我觉得没人有这个魄力和实力下决策也是个很关键的因素
其实各种小规模的重构也一直在做,不过吃力不讨好,如果不产出产品价值,用户也会觉得你没做啥,但是事实上你让这个软件的可维护性可能延长了五年
不重构能不能驾驭,当然临时驾驭一下是可以的,比如离发布时间比较近了,需要快速解决问题,我就一般会开启黑客模式,我就一个办法,先把关键路径(业务工作流)反复调试,调试到对这个路径上面的局部逻辑非常熟悉,然后开始Hack,把相关代码按照功能需求尝试着改一遍,可以就送测试,不行就回退,然后我会拿失败的数据再调试一遍,加深下对业务流程的理解,然后重新Hack一遍,这样每遍都会越写越好,最终总能搞定(除非证明这个功能需求就是错的,一般比较罕见)
不过大部分时候还是建议能重构就重构一下的,而且要持续重构,保持这软件的可维护性,不然自己以后回到同样的逻辑再来一次也会很痛苦,我为人人,人人为我嘛。当时团队是使用相互审核代码(peer review)的制度,反正太恶心的代码别人也不想维护,所以基本上review这关也过不了。
不得不表扬下3ds Max的插件式开放架构,虽然灵活性无敌,但是因为最初的逻辑和结构设计还是非常清晰的,所以后续可以一直扩展,有些时候通过插件接口把复杂性隔离开了,有些模块可以独立分析,或者基于接口分析改动的影响范围。
3ds Max虽然代码量大还能维护这么多年的另一个重要原因是有一套非常成熟的测试机制,包括了自动化的分布式回归测试平台和非常有经验的艺术家测试,这套测试系统和数据也是有专门的团队在维护的。离开了这些的保障,软件工程师也不敢随便改代码,毕竟全球那么大用户量呢(doggy)
PS:跑一趟CICD + 分布式回归需要一个小时左右,切分支干别的事不是不可以,但是太麻烦,劳资一般喝咖啡去了^o^
后来,有些合作方谈到某个项目,我说你们为什么不这样这样做?他们说因为我们的系统太复杂了,我们的代码量blah blah,结果我一看代码,就这???
这个是因为你工作时间比较短…
你工作久了跳槽多了就会发现,大多数公司你都是半路进来,大多数项目你进来的时候这系统已经“稳定”运行好几年了,大公司甚至都10多年了。
你认为是屎山,当时第一波编写者看看这代码也会觉得是屎山。不,每一个接收的人都会觉得是屎山,然后他用尽全力继续拉屎。
在长时间多人数得接力赛之后,这个代码早就不是这个代码了。
每一次换新人,一看这代码都忍不住重构一波,但因为时间不够,总有一些代码没用重构,敢动得只有非核心代码,所以,在kpi的压力下,不得不妥协。
这样来最保险得做法
:自己新写的代码使用新的架构(自己熟悉的),旧的代码不变,需要用老的代码那就桥接过来。
每一个人都这么想,然后新的需求又桥接到已经桥接的代码..然后桥接到桥接的桥接的桥接的代码上..
所以这个项目中有了大量的桥接代码。
所以修改核心代码的难度
:不是每次核心代码相加
而是:第一次核心代码+每一次代码桥接的阶乘…
所以到你头上了,你最好的办法就是继续桥接。
明明直接调用functionA就好了的 需要调用一个functionM 间接调用e,间接调用d,间接调用c ….然后才是A..我只能再加一个function H。
一个1w行的大方法就是对我最大的仁慈。。。
当然还有一个问题
每一波人的编程能力或者编程思想不一样。
———————-
举一个例子
比如最开始的作者喜欢kiss原则,代码非常舒服,性能也很好,小而美。
第二代喜欢设计模式,
策略模式,代理模式,工厂方法,责任链..
23种设计模式,牛逼牛逼 代码虽然复杂了,但是拓展性非常好,堪比教科书式的案例,
前一人虽然性能好,容易懂但是拓展性很差。
老子精心设计得代码,可保证10年轻松拓展。
第三代不怎么熟悉设计模式,这代码看的他云里雾里,他说:。这什么垃圾代码,一个简单的查询跳了7-8层?一个对象继承了2-3个父级?
老子重构, 这写的啥啊,为啥我改了就不行了,改回去就好了,那这样吧,我来面向切面编程,用切面追加我需要加的功能。比如 在输出结果中追加这几个新字段,这样可以非侵入的增加我的业务,而且保证了老代码的稳定性。
还有这块,引用得是jar,都不知道源码在哪..
我这个字段加不进去..妈的..
还有这个方法是私有的..好了,我用反射来调用..
这场景没有5年8年经验还解决不了..
多少人都搞不定,被他顺利的高效的完成了任务,领导非常高兴给他发了三个月年终奖。。。
第四代 这代码好复杂啊.还有各种切面我终于搞清楚了..
。可是明明着bean10个属性,怎最后输出了15个属性?
而我的业务需要这几个突然出现得属性..
这样好了,我隔离这个代码,使用一个函数屏蔽。所有都和这个函数打交道。
除了老代码之外,其他代码舒服了,可是老业务却都还在跑
第五代 这代码怎么会这么奇怪?
…
….
第n代 这屎山,老子要重构..卧槽,本人不才,去知乎问下:
注意,每一代都是绞尽脑汁,发挥他们得聪明才智解决问题才导致这样的。如果大家痘傻逼一点,老老实实按照老版本逻辑写,或者一不做二不休,彻底重构底,这问题还会存在?质量时间成本不可能三角下,老板可不会允许。
所以别怪前任,他们尽力了,其他大多数都尽力了。
——————————————————
大哥,摆脱,说这么多没卵用啊,我现在急着干活呢?
我tm怕撑不过试用期呀。
来了来了别慌。
对于你我来说,想要玩起来这坨屎需要做这些事情
1搞清楚如何启动这项目,因为通常这样的项目配置项,中间件依靠很多很杂。大概率会有大量无用配置文件,以及废代码,引入了并没使用的框架,以及莫名其妙的报错,比代码还长的警告..
顺利启动这个项目是最重要的事情。
2搞清楚如何在ide中,比如idea中打开这项目
假如这个项目可以在测试环境,生产环境启动,恭喜你..,但是这不代表你就可以开始干活了。
你需要在ide中成功编译..成功run,成功debug..
3搞清楚代码,执行链路
入参-入口-》业务-〉数据》出参
比如
某web页面上有一个按钮:放款
你要做的事情是在放款得时候追加一点业务,比如说黑名单检测。
首先找到控制器,如果找不到,打开浏览器控制台,network,看url
然后双shift在idea中搜索这个url后面的内容
大概率你能找到控制器(ssh项目)
如果找不到,比如是gwt,或者其他玩意,那就全文搜索,或者找代码中的配置文件。
这样你找到了入口
从入口出发 你可以看到宛如瀑布般的川流不息的函数调用..甚至一句注释都没
那你统一简化为:数据库操作,非数据库操作两大类
数据库操作就可以知道有哪些表。
有了表,这样就好多了,就可以分析业务了
比如有贷款表,贷款详情表,放款表,利率表,等上十个表。
这样再分类成两类,贷款业务强依靠表,弱依靠表。
强依靠只记录贷款本身数据,弱依赖⌚️指通知,历史记录等表。
在这些表里面找入参和出参数。
顺着字段找代码。
如果能够从如参找到出参,差不多了。
如果不能全部找到,先只找几个。
到这个时候你应该可以一两句话概述主要内容了。
4搞清楚数据库中表的关系
当然大概率啥文档都没..或者文档太久没更新了..
因此需要有点想象力..毕竟不是写的博士毕业论文,还是可以猜出来4-6分。
5复习下你的业务需求,你需要增加啥入参,增加啥出参,这些参数再哪些表?算法先别管。
然后结合搜索代码..
到这儿业务自我的差不多了。
6学习那些非数据库操作。
比如发短信通知,调用接口,写队列,调用非存储中间件代码。
到这一步差不多了,然后试图加一个入参和出参。串起来这些代码,表,接口,中间件。
如果能完成,差不多了,如果不能,寻找熟悉得人,演示给他看,让他指导,如果他不愿意(大概率不愿意)那就让他在他的电脑上debug讲解一下流程。
当然这些都完成不了,也没关系,最重要得是跟你领导同步你的计划-执行-结果,然后求助他,他大概率解决不了会给你一个人帮你,这样可以吧锅甩给他(别太直接,别欺人太甚就行)如果领导说没人,让你自己看。那你为自己争取了更多时间了。
记得主动汇报,不要等到deadline再说你不行..不然这deadline真的就是deadline了..
未完待续…
看的爽麻烦来个三连呗,谢谢。
最好说一下,
还想继续看我装逼or想学习如何装逼推荐看这几个
现已推出付费咨询服务,有需求的可以来找我。
驾驭屎山的唯一方法,不是重构,而是不重构。
为什么有人可以在屎山里加功能?
很简单:把屎山扒拉开,每块闻一闻,找出和你要改的功能对应的那坨屎,把这坨屎套个塑料袋(封装),你就可以假装塑料袋里不是屎,是巧克力。然后,在旁边拉一泡新的屎,等它风干成型(测试通过)就可以收工了。
当然,怎样优雅地在一座屎山里闪转腾挪,做到“百屎丛中过,片屎不沾身”,还是需要很多经验和技巧的。不过说白了,就算失误了摔个嘴啃屎,臭也只臭你程序员一个,老板还觉得你很狼性,客户还觉得真香。
很多愣头青觉得自己是天才,可以把屎山重构了。他们中的大部分人引发了屎崩,永远埋在了几千米高的屎山之下。
极少数人在泥屎流的洗礼中活了下来,浴屎重生。他们真的做到了,他们真的重构了整座屎山!
重构之后的那个东西,被后人称为——
屎山2.0。
当一个系统复杂度增加的时候,它的熵也会增加,这是宇宙规律。而某些有洁癖的码农,他们非要强行降低这个复杂系统的熵——不是做不到,但需要巨大的能量,也就是成本。
你想让谁来付这个成本?老板还是客户?
屎山不是一天拉成的。每一代屎山的建设者,都是非常聪明的人。他们非常清楚继续堆高屎山,未来将产生的代价。他们理性中立客观地评估了推翻屎山重建的成本,然后做出了一个充满智慧的决定:
在屎山上继续拉屎。
这个决定对于每一个人都是最优解,因为每一个人只需要对他当下的目标负责。每一次“继续拉屎”的决定都是正确的,不这样做才令人匪夷所思。如果重构屎山,客户很生气,因为交付时间更长了,还会冒出许多以前没有的bug。老板很生气,因为成本大增,而客户毫无多付钱的意思。就连重构屎山的人自己也很生气:天天钻在屎山里996,搞得浑身屎味,工资却一分钱没涨。
尿海不择细流,故能成其大;屎山不拒细壤,方能就其高。屎山就这样一天天长大,终于令最后接手的码农感叹:高山仰止,景行行止。
其实,屎山,是任何复杂系统的终极归宿。无论你使用何种语言,师从什么流派,哪怕23种设计模式样样精通,最终还是会踏上前往屎山的道路。因为,条条大路通屎山。
就连微软、谷歌、Oracle这样的大公司,他们的产品也都是屎山。Oracle每改一个功能,全公司的服务器需要全速测试几个月。chromium浏览器的源码有十几个G,虽然是开源的,但没有人敢在如此险峻的屎峰上提交自己微不足道的屎坷垃,高处不胜寒。
至于微软,呵呵……从win8到win11,10年了,控制面板还是有两种口味:一种是古早味的,一种是苹果味的。office从2016到2021,5年了,不拿放大镜都看不出有什么区别。
这段话可能会让你误以为,微软、谷歌都是垃圾。但实际上,他们是地球上最强的软件公司。世界上最高的屎山,都是最聪明的屁股拉出来的。换了一般的程序员,屎山还没垒出一个小屎包,就屎崩了,就这点水平还天天嚷嚷着要重构屎山。
乔布斯曾说:死亡是最好的创新。所有代码的最终归宿都是坟墓,而绝大多数代码早已死无葬身之地。屎山是不可能重构的,这辈子都不可能重构的。打败屎山的唯一方法,唯有另起炉灶,建一座新的屎山。这就是为什么在巨头们巍峨连绵的屎山脚下,总能有新的小屎包崛起。
当然,对于血气方刚的少年,我知道,你很可能觉得我在扯淡,屎山有什么好怕的,重构就是了。
明知山有屎,偏向屎山行。来,壮士,干了这碗屎,我绝不会拦着你。
我只会献上一本秘籍,祝你早日成功:
作为一名有理想的程序猿,我也在努力建造自己的屎山。
我想做一个能直接翻译英文pdf的工具,不是划词翻译那种,而是一次性翻译整个文件,英文pdf进,中文pdf出。这样看论文就可以偷懒了~
链接如下,感兴趣的同学可以试试。目前只有PC版,APP的坑一直没填:
大中华区Rust语言代言人 @张汉东 老师在试吃之后,只评论了两个字:
神器。
他翻译的pdf是这样的:
说实话,得到这样的评价实在出乎我的意料,因为我还从没试过翻译编程领域的文章,初看貌似效果还行。不过,我在受宠若惊的同时,也暗暗感到惭愧。因为只有我自己知道,屎山里还有多少坑没填……
不说了,我要进屎山填坑了……
评论区有人说反对我当年退出文坛,其实我进入文坛也没多久……如果你喜欢我这种用脱口秀style把道理讲通透的风格,可以尝尝我今年出版的两本科普书。这两本书都入选了2021年度CCF“科普阅读推荐图书”榜单。CCF是什么,对于程序员就不需要介绍了吧……
一本既不吹捧科学家的伟大、也不纠结历史细节、只想让你无废话看懂量子、还吭哧吭哧手绘100多张彩色漫画实验图解的硬核科普:
一本不屑于教Python和调参、只想和你探讨人类命运和智能本质的AI科普:
还有不赚钱、只求交个朋友的套装:
我讲个无关的吧。做根雕的师傅一般都会将就树根本体的形状来造型。
维护别人写的旧代码也是如此,一般都是照着原来的架构设计和编码风格改。有时候实在看不下去了,才会重写一大段。而且为了给以后的人看明白,这时候文档和注释还需要更详细。
而且我看别的裁缝补衣服,也会用类似的办法。如果是西服之类的,他们会在西服内衬上切下一块一样的料,往外面补。除非西服内衬和外面用的料不一样,才考虑用相近颜色的面料修补。要是谁都来补一个自己的风格,牛仔布、皮革……什么都来,您搁这儿做百衲衣呢?
包括对于飞机蒙皮的维修也是如此,没有说一架飞机,蒙皮破了就整体拆了重新做蒙皮的。一般都是先切掉损坏的部位,然后用面积更大的相同的材料补上去,用铆钉铆上。