十八年了,还要继续干程序员吗?
时光荏苒,我在华为十八年了,一直从事着软件开发工作。
2021年下半年,长沙研究所存量维护开发部VAS Cloud团队招聘Committer(代码提交者),作为一名湖南人,这是我能回长沙工作的难得机会。转岗申请通过后,我拨通了家里的电话,妻子反复确认:“是真的吗?真的有机会可以调回长沙?”在得到我肯定的回答后,电话那头,妻子对女儿兴奋地说:“你爸爸很快就要回长沙了!”“太棒了!”女儿的欢呼声和笑声传到了我的耳朵里。
然而,来到新岗位后,新环境、新业务的各种挑战接踵而来。虽然从事的依旧是“老本行”软件开发,但这次是完全陌生的业务领域,我有点应接不暇。工作进展不如人意的时候,我内心焦灼不已,不禁有些沮丧地想:我是不是不该调动?难道真如网上的传言,年龄大了不适合干程序员了?干了十几年软件开发,还要继续吗?
第二天检视代码时,我发现同事芳芳写的代码虽然看起来比较简短,但在一个函数中多个功能逻辑互相搅和在一起,成了一团乱麻。如果要看懂每个功能逻辑,要么得完整看一遍函数,要么得牢牢记住每个功能的处理逻辑,一旦出现“记忆丢失”,就不得不往前翻看已经看过的代码逻辑。这种代码的可读性不高,后期维护起来更困难。我发现芳芳同学并不是特例,其他人的代码或多或少也有些类似的问题。
我将问题抛到群里:“请伙伴们一起看看这个代码,这个函数职责不单一,不同功能逻辑耦合在一起,很难阅读和维护。大家有什么改进建议?”
“陈哥,这个代码是我写的,我这样写主要是考虑能尽量复用相同的条件语句,也能减少一些代码行。” 芳芳回应道。
“我们做软件可信能力建设,除了代码的功能实现,很重要的一个工作就是把不同逻辑的代码解耦开来,提高代码的可读性、可维护性。如果碰到大块重复代码,应该提取出来放到公共函数中,给不同功能逻辑代码共用调用。”
其他同事也开始在群里七嘴八舌地发表意见,有支持芳芳的,也有支持我的,一时间群消息闪个不停。看到大家讨论热烈,我决定趁热打铁把这个问题解决了,于是提议道:“走,我请大家喝咖啡,咱们当面聊!”
我点了几杯咖啡,将大家拉到会议室,这里瞬间成了热闹的“罗马广场”。最终,大部分同事同意了我的观点,函数职责要单一,要在上层先把不同逻辑的代码解耦,底层再按需要提取公共函数避免重复代码,这样整体代码就更清晰,易于阅读和维护,短时间内可能开发起来更繁琐一些,但长期来看是有收益的。
这件事给了我灵感。抛开之前的焦虑沮丧,我忽然意识到,只要换一个思路便能豁然开朗——我这些年软件开发的经验,能不能转化成在Committer岗位优势呢?公司对软件Committer的核心职责要求有三点:看护软件架构、守护代码质量、赋能团队。我们团队的业务刚从东莞迁移到长沙不久,团队成员都比较新,编程技术能力参差不齐,规范性不足,写的代码有很多可以改进的地方。我觉得可以利用自己十几年的编码经验,从“赋能团队”入手,将一些常用的编程知识和经验方法分享给大家。
于是我组织起软件技术分享交流会,开始每月进行分享,陆续总结了以“单一职责、开闭、里氏替换、迪米特、接口隔离、依赖倒置”五大原则为核心的设计原则指导书(简称“SOLID”原则)和“重构之代码坏味道”“代码抽象”“消除冗余代码”“代码检视常见问题”“高效编码过程”等知识专题。
慢慢地,我在检视代码时发现一些共性和基础问题明显减少,团队内的技术氛围变得浓厚起来了,参会的人也越来越多。大家在工作中也习惯把自己的好想法、新知识补充到知识专题文档中,帮助我共同完善。
我们部门的产品应用服务大部分是用C++开发的。有一段时间,用C++实现的IP消息网关模块出现了内存泄漏问题,一时间没有找到很好的解决方法,项目负责人罗旋为解决这类问题,头皮都快挠破了。正好我之前有这方面的经验,也输出过一个内存泄漏定位经验和方法的总结文档,便发给了他参考。罗旋按照我的文档进行排查,很快发现两处内存泄漏的代码问题。但修改并合入代码后,在极限性能测试场景下,仍有缓慢的内存泄漏问题。他找到我:”陈哥,我按你的操作指导又排查了一遍,也使用业界常用的几种内存泄漏检查工具做了扫描分析,还是没有发现最后的泄漏点在哪里,你有经验,要不帮我一起再排查一下?”我欣然同意,放下手头的工作,开始和罗旋一起分析代码。
过了一个下午,我突然有一个不太成熟的想法,和罗旋商量后,决定扔到团队群里请大家一起提提意见:“我们分析了IP消息网关的代码,发现这个模块的部分内存是用内存池管理的,而且自己封装了内存申请释放函数。我们可以尝试自己写一个内存泄漏检测工具,针对这些函数进行插桩,这样可能会效果更好。”
大家觉得我的这个想法很有创意,可实现性也好,对工具的设计也有益。赞良是团队中开发工具的一把好手,主动请缨和罗旋一起配合,差不多两天时间就完成了这个小工具的开发和调试。工具上线后,很快将最后一处内存泄漏问题给挖掘了出来,排除了代码中的隐患。同时,赞良和罗旋在我的文档基础上,合入了这个工具的相关使用内容,进一步完善了内存泄漏检测的场景和工具操作指导。
在近两年的时间里,我一直在坚持总结技术和业务知识,在团队社区创建了70篇WiKi(维基),其中一篇的浏览次数达到近600次。算起来,我在团队内举办了近20场“软件技术分享交流会”,累计参与500人次,利用自己的软件经验主动进行技术分享,不仅活跃了团队技术氛围,让大家有所收获和成长,团队交付质量也得到了提升。这些便是团队成员给我的最大回馈,主管也多次对我的技术分享给予肯定。而我自己更是乐在其中,逐步走出了当初的焦虑和迷茫。
织一张严密的代码防护网
这几年公司一直在推动可信文化变革,加强Committer的运作,营造 “人人写出高质量代码,人人写出Clean Code(整洁代码)”的可信氛围。我刚来团队时,发现部门现有的开发流程中,有些代码是由几个PL(项目负责人)合入的,检视意见很少。这种缺乏专业Committer的运作方式,导致代码审核流于形式,存在较大风险。
前期的软件质量赋能建设,使团队的代码规范、质量意识、软件能力都有了提升,但在软件检视和代码质量保障上,还缺乏系统化、专业化的运作机制。我决心改变这种现状,首要挑选和培养一批合格的Committer,将团队Committer机制运转起来。
一个合格的Committer除了自己有很强的技术能力、能写出“好代码”之外,还要有敏锐发现代码“坏味道”的能力,更要能站在开发人员的角度去思考,在检视意见中给出修改建议,辅导开发人员尽快完成代码修改优化,达到“好代码”要求后再合入,确保架构不腐化,最终帮助开发人员高质高效完成项目目标。这是一个综合能力要求非常高的岗位,短时间内从哪去找人呢?
任总提到“要在战场中识别英雄”,Committer选拔也一样。我决定通过代码检视来找人,先找自己代码能写好的人,元辉首先进入我的视线。通过检视他合入的代码,我发现他自己写的代码简洁、层次抽象合理,命名清晰易懂,完全满足“好代码”的要求,是个好苗子。留心观察后,我发现他日常工作中也很热衷于帮忙大家检视代码,检视意见中除了提出问题之外,还有详细的修改建议。简直就是“天选”Committer啊!
可我又担心元辉业务太忙,怕他拒绝。我忐忑地找到元辉,问道:“你是我们团队的软件高手,代码质量和检视意见都很好。现在我们正在构建Committer机制,你是我第一个推荐的。你想不想和我一起,共同看护好我们的代码?”
元辉有些困惑:“我自己是很喜欢编码的,也喜欢帮别人检视代码,但不太了解Committer的要求,不知道能不能干好。”
我马上开展“公关”,和他聊到Committer的职责和对以后对职业发展的帮助,最后嘱咐道:“放心,我和你的主管也沟通过了,他非常支持你!”
元辉最终点头同意:“其实我也想用自己的技术能力去影响他人、帮助他人”。
通过持续观察,我陆续又吸引了付辉、概彬加入到Committer队伍中来。至此,我们的小组终于建立起来了,万里长征迈出了关键的第一步。
Committer小组逐步运作起来后,给大家提交的代码提了很多检视意见,但上库的代码质量却并没有太大改进,反而在代码合入时激发了不小的“矛盾”。Committer要求严格按检视意见修改后再提交,但开发人员觉得有些意见是“小题大做”,双方展开了激烈的交锋。
“元辉,你提意见要求代码自注释是什么意思,我不是写了很多注释吗?”小黄质疑道。
“你的这些函数和变量命名都很简单,可以直接把这些注释通过函数和变量命名表达出来,这才是代码自注释。”
“那遇到一些复杂的条件语句怎么做到自注释呢?也太麻烦了吧?”
“遇到复杂的条件语句时可以先定义一些布尔变量,取一个详细的命名,用来承接复杂的条件语句的值,这样既做到了代码自注释,又提高了可读性。”元辉耐心解释。
“OK,懂了,建议可以把这些要求写到团队的代码规范里,其他小伙伴也要学习!”
除了编码上的小交锋,代码检视和项目节奏要求的适配性也是我们要逐步适应和磨合的地方。记得有一次迭代版本转测试,因为有紧急需求开发,导致版本转测试延期两天,项目经理莉姐和PL龙哥十分焦虑,每天和开发人员过进展,催促大家尽快合入代码,启动转测试流程。转测试当天,我和几位Committer的消息闪个不停,全是催代码合入的。“怎么办?压力全在我们这里,根本检视不完,今天就要转测试了。”元辉被催得有些扛不住了。
我找到龙哥:“之前我们版本转测试时总是有些基本功能问题或代码修改引入问题,老是被测试团队诟病。现在我们好不容易把Committer团队运作起来,就是希望在源头上保证代码质量,避免把问题遗漏到下一环节,造成大量返工。这次合入这么多需求代码,你看看能不能给我们争取一天的检视时间。”
龙哥无奈道:“这次版本的需求负载确实比较高,当前已经延期两天,如果再不转测试,后面的交付计划就乱了,我担心最终对一线和客户承诺的交付时间守不住。有没有办法在保证质量的同时能按时转测?”
我沉吟片刻:“这样吧,我们几个Committer手上的任务先缓一缓,今天集中到12楼的会议室,封闭检视代码,确保今天能转测试。但我有一个要求,后续一定要给我们预留充分的检视时间,这是确保代码质量的最终防线!”龙哥也不再犹豫:“行,我去跟项目经理和测试经理沟通。你们先启动,项目组其他成员随时待命进行代码修改和自验。”
忙到晚上11点,我们发现了多个代码规范和修改代码引入的问题,并进行了整改和自验,版本最终赶在12点前顺利转测试。从后面的验证情况来看,没有出现一个代码修改引入的问题,更没有出现返工。相比以前经常转测试后再加班返工修改情况,其实总时间反而减少了,大家也充分认识到了代码检视的重要性,Committer是项目高质量、按时交付不可或缺的一道保障。
随着时间的推移,Committer和开发人员一次次交流碰撞,大家的目标也越来越一致,不要把“坏代码”提交到库上成了默认的共识。
从经验猜测到有档可查
Committer工作逐步走上正轨之后,我开始兼任团队MDE(模块设计师),主要职责就是带领大家解决现网的各种疑难问题。华为VAS Cloud是一款能支持短信、彩信、USSD(非结构化补充业务数据)和VMS(语音邮箱业务)等多业务的产品,可在消息增值业务领域为运营商提供更高效、更低成本的解决方案。VAS Colud产品已有20多年历史,在全球商用局点有上百个,我们团队的主要工作之一就是做这个产品的维护开发。
2022年9月,A移动客户局点短信业务出现了软件复位问题,虽然有容灾备份阶段,暂时没有引起业务中断。但该局点业务量大,不及时排除隐患的话,一旦出现意外,极有可能引发用户大面积投诉。
接到一线问题定位需求后,我和短信小组成员经过一下午的紧张分析,初步怀疑与内存越界有关,也就是说程序访问了超出其所分配内存范围的位置。沿着这个方向,我们各自分工排查内存操作相关代码。但经过一晚上的鏖战,我们仔细走读了近5000行可疑代码,却没发现任何异常。难道方向错了?
“有没有可能出现重复释放的问题?” 大家再次集中到会议室头脑风暴,总结代码排查情况。
“短信分包时,计算的分包长度超过160字节时,会不会有些地方没有进行超长判断?”
“一线同事上线了长消息所有分包都写话单的新功能,这个功能的相关代码都排查了吗?”
当一个个疑问点提出又被我们给予肯定的回答后,能想到的方向的都已经排查过了。就在此时,一线同事反馈,现网再次发生了软件复位问题。面对客户高层的质疑,一线的压力越来越大,要求当天必须定位出问题原因。
正所谓绝处逢生,通过分析第二次的复位信息,我们惊喜地发现,第二次的复位信息和第一次不一样,两次复位可以交叉锁定一个较小的代码范围。于是大家集中排查这个确定的代码范围,最终专家华哥发现一处代码在设置对象的“状态报表状态”时出问题可疑性最大。顺藤摸瓜进一步分析相关的代码后,我们发现这个对象的内存在某些场景下已经被释放了,但指针没有被设置为空,导致后续流程继续使用该指针设置“状态报表状态”时,会出现复位问题。原因终于水落石出,我们很快就解决了问题。
经过这次,我发现团队解决现网突发问题缺乏系统的指导性方法,往往凭借经验猜测逐一排查,运气好一次就找到,运气差点可能要排查几天。疑难问题的定位,使用辅助定位工具、详细总结定位方法非常必要。接下来我制定了复位问题改进的专项工作计划,拟制了coredump(核心转储)定位指导和编程规范,并开展全员培训和考试,输出16篇复位和疑难问题分析总结案例。接下来,我组织在17个C++模块落地地址消毒工具,挖掘出18个内存异常问题,并定期在SIT(系统集成测试)阶段通过该工具进行全量自动化用例测试,保证了版本出口质量。
担任疑难问题攻关组组长的这几年,我主导了35个版本疑难问题定位处理,基本没有再求助团队外部专家,大部分问题基本上当天就能定位出来,定位效率大幅提升了。同时,我总结了70篇WiKi文档,覆盖复位问题、性能问题、疑难业务问题场景的处理,组织资产得到有效积累和传承。
VAS Cloud团队(左一为作者)
做码农还是程序员
有人说写代码吃的是青春饭,这话其实不准确,在软件开发领域,经验是极其宝贵的。随着年龄的增长,开发者会积累更多的软件开发经验、解决问题的技能和对复杂系统的整体设计思路,这些能力有助于在解决技术难题时提供独到的见解,也有利于高效开发出更高质量的软件。
十几年的软件编码工作让我逐渐明白——作为一个程序员,如果能确保自己输出的每一行代码的质量,你可以称自己为一名“Coder(码农)”。但能时刻心存程序员的情怀和理想,把自己的编程知识、经验方法进行分享和复制,帮助团队建立“Clean Code”的信念,才能称为一名真正的“Programmer(程序员)”。
无论是过去还是未来,我都会坚定地用自己的行动去诠释Programmer这一让我骄傲的身份。