1 前言
从微信支付离职,我能带走什么?文档,代码,设计方案还是微信支付的漏洞?
如果我带走这些资产,那我现在就在深圳的看守所里面吃着公家饭了。
既然这些资产不能带走,那么我能带走什么?
如果沉下心思考,就会发现,这些资产价值并不大,对于工程师而言,也没有领导想象中的那么重要,除非我们试图将代码放在黑市售卖。
对于业务开发而言,也可能是同样的道理。业务开发每天对着业务需求做CRUD,可能会羡慕开发底层组件的工程师,可以学习并提升技术水平,而自己技术水平还是在原地打转,能学习到的东西随着时间的推移,越来越少。
王安石的《游褒禅山记》有这样的感叹:
夫夷以近,则遊者众;险以远,则至者少;而世之奇伟瑰怪非常之观,常在于险远,而人之所罕至焉;故非有志者,不能至也。
所谓的「险以远」,并不特指深奥难懂的底层组件技术,也指思考的深度;
如果多去思考技术和业务,挖掘背后的本质,我们也可以看到许多「世之奇伟瑰怪非常之观」
1.1 鱼与渔
文档,代码,设计都是针对特定问题的解决方案,如果离职到新公司之后,我们遇到的问题肯定不会完全一样,或者手头可用的工具不一样,那么这些资产的价值就会打折扣。
更何况这些资产都是「一次性的」,用完即止;是属于「授人以鱼不如授人以渔」中的「鱼」;是「生产线」上的「成品」,而我对能生产「成品」的「生产线」更感兴趣。
二战结束以后,美国把1600多名德国科学家、工程师、技术人员带到美国,包括沃纳.冯.布劳恩和他的V-2火箭研究团队;
而苏联凭借地理位置靠近德国占领了一些重要的工厂,比如著名的德国光学巨头卡尔蔡司公司,苏联几乎搬空了该公司的设备,把1万多台设备中的9000多台都搬到了苏联。
有人对现成的「鱼」感兴趣,也有人对未来的「渔」感兴趣,我属于后者。
2 思路
既然选择「渔」,那么,要怎么挑选适合的「渔」来丰富自己的「渔库」呢?
两千多年前的老师孔子就已经给出自己的答案:
见贤思齐焉,见不贤而内自省也
见到那些优秀的实践和思路,就学下来;对于有弊端的实践,就要分析弊端形成的原因,再想办法避免和改进,别人掉进去的坑,我们就不要进去凑热闹了。
3 贤
3.1 模式化
1994年,4个博士合著了一本书,书中对常见的设计问题进行了分类,归纳与总结,并且针对每一类问题,给出可重用的解决方案。他们将这些可以复用的解决方案,称之为设计模式(design pattern)。
这本书也成为软件工程和面向对象设计经久不衰的经典。
这本书即是《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software),这四位博士也被称为Gang of Four (GoF)
A design pattern is the re-usable form of a solution to a design problem.
那么什么是模式呢?
按照另外一本经典名著《面向模式的软件架构卷一》的定义:
当专家求解一个问题时,他们一般不会发明一种和已有解决方案完全不同的方案来处理这个问题。他们往往想起已解决过的相似的问题,并重用其解法的精华来解决新问题。
在微信支付研发理念中,程序设计和开发,很多问题都是类似,或者是重复出现的。
针对此类重复问题,直接复制代码来解决,是下下策。
对代码进行抽象,复用代码来解决重复问题,也是下策。因为使用公共库会导致代码之间无法隔离,并且把逻辑隐藏在公共库,会导致无法分析代码的调用关系。
微信支付研发理念推崇的上策是对问题进行抽象,归纳出这类问题的通用解法,即模式;更进一步的是,为模式定义对应的代码模板,直接生成代码。
即使不生成代码,也可以将模式实现成对应的组件或库,方便直接调用。
具体例子如:
微信支付就总结常见的分布式事务场景,设计和开发了分布式事务编排中间件。通过在画板编排事务资源,即可生成对应的代码模板,开发者只需要在指定的地方编写个性化代码即可。
针对常见的领域服务,抽象了基于状态机和事件驱动的模型,设计了领域服务的代码生成组件。可通过绘制状态机UML图,直接生成接口代码,由开发者填充实现。
以上算是技术组件的模式化,对业务开发而言,还有对业务的模式化。
比如对扣款模式进行抽象,扣款时开启事务,进行风控校验,创建(或不创建)业务单,查询支付方式,轮询支付方式进行扣款,异常关单等。
当时组里的大神龙哥,就是对已有的扣款模式进行了抽象,基于面向对象,设计成同步扣款框架,定义了以上的接口,由业务进行继承和扩展。再使用同步扣款框架对已有的3个类似但不完全一致的代扣扣款业务进行了重构,把扣款模式都统一了。
3.2 复盘
没有人能保证自己写的代码绝对不会出错,当错误与问题不期而至的时候,我们能做的就是将「错误」的效益最大化,即从「错误」中吸引教训,做到「不二过」。
复盘,就是在「错误」中吸引教训,做到「不二过」的手段。Amazon 也有类似的概念与机制,称为 Correctness Of Error(COE)
我们一直说「失败是成功之母」,但根据生物学常识,只有「成功才是成功之母」,或者说「小步的成功才是大步成功之母」,别人踩过的坑,我们就不要进去了。
复盘的一般步骤:
- 回顾目标
- 故障影响
- 时间精确到分钟(甚至秒级别)的过程回顾。比如是新需求写出一级故障的bug, 就从拿到需求,设计方案,开发,部署上线,流量灰度,问题告警,处理手段,到故障排除,每个时间点操作都写下来。
- 分析问题原因,挖掘导致故障的表面原因与根本原因
- 总结针对问题的改进措施。
- 落实改进措施
通过这样的复盘过程,确保同样的问题不会再次出现。
这样的工作方式和理念,无论是对个人还是组织,才同样适用。
3.3 持续学习
微信支付一直在推广全栈工程师,认为只从自己做的事情来思考问题,容易导致盲维和短板,看待问题的眼光容易受限。
此外,根据《人月神话》的理念,工程师之间的沟通成本,会随着人数的增加,呈指数水平上涨。而成为全栈工程师,可以一个人处理完需求,沟通成本就下降到0,极大地提交工作效率。
微信支付的全栈工程师定义是前端工程师 + 服务端工程师 + 数据开发工程师。
当然,某一端的开发工程师,不会某天突然自己变成全栈工程师,这些都是需要持续学习的,人总是需要不断提升自己的。
不能人为能给自己设限,把自己定义成「前端工程师」,「后端工程师」,或者「数据工程师」,应该是「工程师」。
3.4 需求分析
每个工程师都需要做需求,与正确地做需求相比,做正确的需求显然更重要。
如何确保做正确的需求呢?
微信支付选择的方法论是:需求分析与业务建模,脱胎自UML专家潘家宇的著作《软件方法》。大概的流程是:
- 寻找老大(需要满足谁的诉求)
- 寻找业务用例(业务执行者做什么事情,比如QQ音乐用户购买QQ音乐会员,就是一个业务用例)
- 根据业务用例,寻找系统用例。(例如商户发起扣款是一个系统用例;扣款成功回调通知商户也是一个系统用例)
- 将需求的业务规则,总结归纳成系统用例的规则。
当然,业务用例和系统用例这套东西,可能只有微信支付用。但找准客户,帮客户解决真正的痛点,创造真正的价值,这个是有普适性的。
做需求时,可以多问这两个问题:
- 谁是我们的客户。
- 我们在帮他们解决什么问题。
3.5 “云雨伞”
“云雨伞”这个概念来自内部的一份PPT,讲述的是如何更好地向别人提出建议,内容大概是:
屋外乌云密布,儿子要出门,妈妈对儿子说,马上要下雨,淋雨容易生病,把伞带上吧。
“云雨伞”的步骤就是:
- 指出现状:乌云密布,马上要下雨
- 导致的问题与影响:淋雨容易生病
- 提出措施和建议:把伞带上。
通过这样的表述方式,会比「把伞带上」这样直接命令的话,更容易让人接受。
当然,如果阅读过《非暴力沟通》,会发现“云雨伞”的表述,其实是《非暴力沟通》总结的有效沟通方式的简化版本:
- 清楚地表达观察结果
- 表达感受
- 说出是什么需求和原因导致了这样的感受
- 具体的请求
当然,总是强调「云雨伞」的做法,把问题归咎到提问者身上,我是不赞同的。
领导经常说,提问题的时候,要把自己的解决方案也提出来,没有人喜欢听吐槽。
话虽如此,但是我想起之间还在蚂蚁时,一位P10工程师的文章,《没有答案,也可以提问题》。
提问题是为了帮助组织发现问题,如果不能吐槽的话,很多问题也不会被发现,自然也得不到解决,毕竟也没有人喜欢帮别人的问题提解决方案。
针对如何高效交流,我写了一篇自己的心得文章:软件工程师的软技能指北(三):高效交流篇
3.6 一致性
领导总说,软件工程的本质就是管理和控制复杂度,而一致性就是减少复杂度的有力工具。所谓的一致性,可以理解成统一的流程,统一的组件等等
在这种理念的驱动下,微信支付内部使用统一的编程语言,统一的工具库,统一的存储组件(使用别的存储需要特殊审批和说明),统一的数据访问组件,使用统一的研发流程。
保证每个研发工程师,即使调到微信支付的其他团队,也是使用同样的工具,即插即用,和车床生产的螺丝一样。
开始我对这样的理念是持支持态度的,但到AWS以后,我的想法发生了动摇。
因为我发现AWS的工具真的是琳琅满目,应有尽有,而Amazon也并未对使用什么样的组件作要求。
反正AWS对各种组件的支持都很好,所以业务团队可以自行选择适合自身业务的任意组件,能完成需求就好。
所以我现在不确定,通过追求一致性来降低复杂度这样的做法是否合理。
3.7 设计优于实现
从2020年初起,微信支付内部的需求都需要先写设计文档,Leader 评审通过才能开发。
设计时有个非常关键的点,就是列出所有能想到的可行方法,而后比较各个方案的优劣,再作出取舍,选择最终方案。
软件工程没有银弹,系统/软件设计就是不断地在做取舍,当然,人生也是。
设计才是最重要的,而编码和实现都是简单的,因为这只是水到渠成的事(我也不是说可以不用重视代码质量,毕竟这是吃饭的手艺)
我个人觉得,对于业务开发(或者对于软件工程师)而言,不要过多花时间关注在编码上,而应该是花时间思考需求和问题,找到好的设计上。
良好设计带来的红利,是要多于良好编码带来的红利的。
如果把编码比作战术,设计就是战略,不要让战术的勤奋,掩盖了战略上的懒惰。
编码算是建筑的外墙和玻璃,而设计就是承重墙和地基,毕竟换皮容易换根难。
微信支付对于业务代码的态度是,能生成就尽量生成,就不要人写了,要多花些时间在设计上。
4 总结
拿走「代码,文档」终究是术,学走「思想和理念」才是道。