防御式编程思想的原则
在分析若干线上问题之后,就会发现很多问题并不是非常罕见的难题,而是由一个个较为明显的小错误积累而来,为什么会犯小错误?或许是因为侥幸,或许是因为只看到了局部的利益。
从某一个例子来看,确实会有“偶因一着错,便为人上人的”的侥幸。但是统计大量案例可以发现,大部分情况并不会有正向的结果。
本文汇编我在需求评审、编码、项目上线几个阶段总结的原则。这些原则虽然不能直接解决问题,但是可以尽可能得优化每一步行为,从防御式编程思维方式、行为习惯层面尽量避免错误的发生。
一 交流沟通时的原则
11 打字沟通3分钟还没有进展,立刻更换沟通方式
通过IM(通讯软件)沟通是常见的沟通方式,成本低,适合简单问题的沟通。
如果一个复杂的问题,打字3分钟或10句话还说不清楚、无任何进展,应该立刻换一种更高效的沟通方式,比如语音或见面谈。
有时会看到两个人在群里一人一句,讨论了10分钟,结果发现说的不是一件事情。
12 避免确认偏误,反对的观点可能有更大的启发
确认偏误,指的是只听“好”消息——即能印证自己观念的消息,而对“坏”消息赶尽杀绝——即拒绝接受与自己观念不同的消息、想法。
这种现象会导致自己无法接受一个长远有益的反对意见。
这是因为大脑的结构非常复杂,由理性的我和感性的我组成,对于别人的反对意见,感性的我会默认把别人的反对意见当成攻击。
所以我们就要训练感性的我,对于别人的观点,要仔细思考,再做回复。反对的观点,可能道出了自己想法中潜在的风险,甚至是一个更好方案的启蒙。
13 举例论证,只能作为决策的参考意见
无论是自己思考问题,还是讨论问题的时候,经常会出现以下两种观点。
- 上一个项目,我使用了技术方案A,效果非常好。所以这个项目也使用技术方案A吧。
- 我有一个朋友,遇到这样的问题,他就是这样解决的。所以我们也可以这样做。
把一次偶然的成功,当成必然的结论的现象。既可能出现在讨论中,也可能出现在自己的经验中。
这些经验可以当成决策的一个参考案例,但不能直接当成必然的结论。
科学的方法应该根据不同的场景,不同背景,做大规模的统计,这样才能根据不同场景做更好的决策。
14 创意择优,自己调整权重再做决策
无论是技术方案的讨论,还是一个有争论问题的交流。大家都会从自己的角度有不同的观点。每个人都有自己更关注的点,所处位置不同,关注点则不同。
如果需要自己做决策,那么在讨论的过程中,要理解每一个想法背后的原因。这样才能明白其底层逻辑,从自己的角度判断其权重。综合考虑不同角度的想法,这样才能站在自己的角度,更全面得考虑、决策问题。
李开复在选择毕业论文课题时,就涉及风险与稳定两类课题的决择。
- 如果选择深度学习,可能会延期毕业,风险高,但是如果成功了则一举成名。
- 如果选择普通的课题,能按时毕业,无风险,但是不会取得非常大的成就。
对于不同人,承受的风险是不同的,那么他们的选择也会不同,当然了,其长远收益也不同。
15 打字沟通时,重要的内容单独发一条消息
对于大段的文字内容,对方不一定会仔细看每一句。重要的问题,单独发一句,不和其他内容合并在一起。
实例(1)
在排查一个问题时,一句话问了2个问题:
问题A现在的情况是A1吧?问题B现在的情况是B1吧?
对方回答是,根据对方的结论,我继续排查问题。
排期了一天,我感觉问题B的情况不应该是B1,再次找对方确认,结果对方说他只看到我说的前一句,并没有看到后一句”问题B的情况是B1吧?”。
实例(2)
有一次项目上线之后,我给合作方发了一句话以表示感谢:
这个功能用长链接,抓包困难,涉及五个团队,排查问题非常困难。
感谢你帮忙排查问题,提供了很好的解决思路。
现在已经上线了。
对方回了我一句:“需要我配合要排查什么问题?”
二 需求评审阶段的原则
21 人人都是产品经理,需求决定方向
软件开发,是为了满足真正的需求,解决真正的问题。并不仅仅是完成被安排的一个需求任务。
研发不仅是按照产品经理的需求写代码,也要思考与批判其合理性。只有身处一名用户的角度,仔细分析产品的合理性,才能写出更有价值的代码,而不仅仅是把写代码当成一种体力劳动的交换。
22 技术是手段,而非目的。
需求分析与系统设计是两个递进的步骤。在需求分析阶段,不应该考虑详细的技术细节。否则就会出现“因为技术太困难,所以这个需求做不了”的现象,导致一个优秀想法的流产。刚开始就从技术的角度,而不是从用户真实需求的角度来实现一个更有价值的产品。
产品的价值应该根据用户的需求来决定,而不是技术的难易。当然并不是不考虑技术实现的成本,而是应该有先后顺序,确定需求之后再确认技术实现的成本。毕竟即使技术成本很高,其ROI也可能更高。
虽然有些需求,用某些技术方案,成本非常低。几个小时就做完一个需求,但如果是无意义的需求与无意义的技术方案,做再多又有什么用呢?
23 简化步骤的产品,有更大的价值
对于需要用户交互的产品,用户操作成本越低,时间成本越低,价值越高。这就需要在考虑产品的时候能在保证功能不变的前提下,尽量多的删除冗余功能,而不是增加功能。虽然增加功能更容易。
24 跨团队合作,内部排期全部对齐之后再与外部沟通
排期时间,先确认内部各个方向都同意排期,再与外部沟通。避免先和外部沟通,外部同意了,结果内部有一方时间不合适,再频繁与外部团队调整时间的尴尬。
25 倒排排期的需求,压缩开发时间而不是联调时间
有时候难免会遇到非常紧急的需求,需要倒排排期。如果一定要压缩时间,那就压缩开发时间,要保证联调时间足够。因为联调才能更快的暴露问题。提前暴露问题,才能尽早得协调资源,解决问题。
26 跨越节假日的项目,注意封线与联调时间
开始联调的日期尽量不要定在大型节假日前1天,特别是涉及多团队的大项目,因为一个人休假导致无法联调,最终导致整个流程都无法及时开始联调。
27 不要从自己的角度,给合作方评估工作量
涉及到团队合作,特别是外部团队合作,即使是自认为1分钟可以改完的代码,也不要认为对方很快可以修改。因为整流研发流程不仅仅是改代码,还包括从产品的角度进行需求分析,研发与测试排期,边际交付成本并不会很低。
28 跨团队合作,记录会议记要及待办事项
跨团队合作要明确会议的结论,以及谁下一步要做什么。
特别是跨公司的合作,和自己对接的不是研发的话,可以通过发邮件等更正式的方式。
三 开发阶段的原则
31 没有文档的设计是不完整的设计
“我已完成了技术方案的设计,只剩下写文档了”
研发说这句话就像是一位作家说:
“我已经完成了小说的创作,只剩下它这部作品写下来了”。
虽然在维护他人项目的时候,可以看代码或注释来了解整个项目。但是一些复杂的项目,较复杂的逻辑,很难从代码中看出来整体结构。这时候文档的重要性就显示出来了。
文档的维护确实需要成本,但这是一个短期的成本。长期来看,有了文档,可以避免以后更多的沟通交流成本,避免花更多时间看一个陌生的逻辑的时间。相比编码完成之后推倒重来,文档的修改成本并不高。
大家都讨厌“口口相传”,讨厌别人不写文档。那不如从我做起,完善每一个项目的文档。
把可能涉及项目的整个团队当成一个共同体,而不仅仅是自己一个人。相比于自己写一次文档的局部视野,可以从更全局的视野来看长远收益。
32 维护文档的同时,也要维护文档索引
如果是单独一个项目的文档,其结构比较单一。即使迭代多个版本,其整体结构也比较清晰。
但如果是一个团队大家一起记录,对于一个新主题页面的添加,不同的人会添加到不同的层级,下次在查找的时候非常困难。
此时索引的重要性就很明显了,即文档的添加,不仅仅是单独一篇文档的添加,还需要有相应索引的维护。
33 编码时间占比应低于30%
一个项目从需求评审、技术方案调研、书写文档、编写代码、测试、上线。纯粹编码的时间占比并不大。特别是在设计完美,一气呵成写完代码的情况下。
如果编码时间占比很大,大概率是因为在编码的过程中写了一半、突然不知道后面怎么写了。或者写完之后,才发现设计有严重缺陷,需要推倒重来。
34 代码需要别人能看懂,而不仅仅能正常运行
上学时期,一个课程设计的项目,只要能正常运行即可。至于老师能不能看懂我们变量命名是什么意思,并不重要。因为这个项目,以后再也不会有人关注了。
但是在企业项目中,一个项目不仅仅是要自己维护,还涉及到团队内其他同学来维护。因此代码的要求就不仅仅是能运行了,还要能让别人看懂。
35 先写注释,再写代码
注释不是补出来的,而是在编码的时候就应该写好的。代码和注释是密不可分的统一体,类似知行合一的思想,知和行是密切结合在一起的,两者不可分割。
几秒钟打几个字写一些注释,会为以后查看会节省更多的时间。
有人可能说我能看懂就行,以后看不懂也是别人看不懂,和我没关系。即使只从自己的角度来看,这个还真不一定,不信可以看看自己2个月之前写的代码。况且从更大的共同体利益来看,写注释并不是为了自己,而是为了整体团体的利益。
如果刚开始不习惯写代码的同时写注释,可以在写代码之前先写注释。
36 外交无小事,对外的接口,变量的更改。要尽早同步
对外的接口协议,要慎重,确定之后不应该修改了。但是如果因为早期考虑不全面,必须修改,那就尽早修改。虽然自己全局搜索替换成本很低,但是如果涉及合作方的修改,特别是在测试后期,沟通成本可能要再高一个量级。周知上下游,越早改,大家的成本越小。
37 模块、函数遵循单一目的的原则
每个函数只做一件事,每个模块功能尽量单一。每个模块对外暴露相应的接口,做到“高内聚,低耦合”。
说起来似乎很简单,但是如果在需求第一个版本就写出高耦合的代码,就会导致后续维护不可控,出现一个类上千行的现象。后期想要再改,成本呈指数级上升。
38 命名要表里如一,见名知意
变量是代码中最小的单位,命名的好坏与可读性好坏有较大关系。
比如命名是keyString,看意思应该是key值的字符串,如果在使用的时候用的是value,这会给别人阅读造成很大的障碍。
39 公参保持一致,不要重复命名
在项目迭代版本很多的时候,新需求如果添加参数,要注意公参是否已存在。避免多个key不同,但value一样的公参。
310 编码:不要使用特殊技巧
非常复杂的炫技代码,其价值在可读性面前不值一提。
311 Commit message虽不起眼,但蕴藏大信息
Commit message可以当成一个索引,不仅方便回顾迭代的流程,还方便回滚。
每一个commit message,都应该和模块、函数的单一目的类似,尽量只有一个功能的修改。
并且commit message最好有固定的格式,比如[分支名][修改的内容]。想要更详细的话,可以再加上[代码类型],区分是功能,还是bug的修复。甚至可以再加上[bugId],可以链接到相应的bug空间。
这种习惯在集成,回滚时就体会到价值。
312 解决所有警告
看似IDE中影响不大的警告,正如同一个个小错误。等到积累到一定量之后,就会增加崩溃的概率。
313 代码评审,是输入、输出之后的反馈,是进步最快的方式之一
代码评审:别人发现我的错误时,应该心存感激,而不是试图辩解。
类似于健身的时候,先看书中的理论,再实践,最后找教练反馈。
有时候我们认为自己做的动作是对的,但是在输出之后找教练反馈,可以纠正很多细节。
代码评审也是如此,有时候认为自己写得完美无缺,但实际可能漏洞百出。
314 if else平衡,失败的数据也需要
对于一些逻辑的处理,输出成功结果进行透传。虽然只需要处理成功的情况,但是失败的情况也要处理。失败的数据虽然暂时没用,但可以统计失败率,以及分析问题。
四 测试阶段的原则
41 涉及用户操作的产品功能,交叉测试更容易发现问题
每个用户都有不同的操作习惯,对于涉及用户操作的产品功能,如果一直是同一名QA测试,可能因为其固定的操作习惯,导致不容易发现一些隐藏的bug。
这个时候尽可量多的“用户”亲自操作,从不同的操作习惯来体验,则更容易发现问题。
如果能进一步扩大范围,不同岗位的同学一起使用、体验、吐槽,则能更早的发现真实用户的体验问题。
42 无法解决的困难的问题,试试研究的思路
有时候会遇到一个很困难的问题,一天都解决不了。主要是两种情况
- 思路错了。
- 未知的知识太多了。
如果是思路错了,这个时候不妨以退为退,想一想为什么要开发这个功能,甚至退回到需求层面,是不是可以有其他更简单的解决思路。从更高的视野来看,可以避免陷入局部细节而无法自拨。
如果是未知的知识太多,那就不要直接解决这个问题,而是带着研究的想法,把相关的知识全部学一遍,这样这个困难可能就迎刃而解了。
五 上线之后的原则
51 以正合,以奇胜
几乎没有靠运气的出奇制胜。只有以拥有足够正面实力为前提,并且有一些额外的力量,才能取得最终的胜利。
在做项目时也是如此。要想更快,更高效的完成一个项目,离不开日常的积累。项目上线不是结束,而是成长的开始。一个项目可以提供很多经验与素材,当成自己输入、输出的一种反馈,找到不足之处,避免下次犯同样的错误。
52 在公司光环之下成长,而不是单打独斗
闲暇时间干什么,找个副业?短期来看,确实能有一些额外的收入。
但是长远来看,即使多出来30%的收入,也不会有本质的变化。无非是能更换一些更高档的电子产品、服饰、日用品。自己一个人的力量太弱小。单打独斗不如在公司的光环之下,更高效的完成项目的前提下获得个人的成长。
53 面对线上问题,担忧过去或焦虑未来都没有用。关注当下,在平静中积极地寻求解决方案
失败既能化为动力,驱动个人进化,也能毁掉自己,这取决自己如何应对失败。
出了线上问题,有两件事。
- 分析原因,是谁的问题。
- 针对现状,如何解决。
针对”线上已出现了事故”的现状,需要立刻寻找解决方案。这是遇到线上事故时优先级最高的事情。止损非常重要,特别是损失在不停的扩大时,此时每一步操作都非常重要,如果问题非常复杂,记得多喊人。把老板,经验丰富的同学都拉到群里,如果大家有事都没看到消息,就直接打电话。毕竟在慌忙之中,自己考虑的可能不全面。
分析原因的时候,如果是复杂、涉及非常多团队合作的项目,其实有非常多的原因,从不同的角度分析会有不同的结果。在分析总结会上,要尽量多的表达客观的实际情况,而不是害怕,特别是对于内向的同学。避免在会上不说话,会后又感到后悔。该表达的时候不要吝啬表达。
从自己的角度来说,并不是“只要这个线上问题,和我无关”,我就不用关注了。这类问题正是非常好的反面教材,即使和自己无关,也可以成为避免下次自己犯错的素材。比如本文的各个原则,有些和我没有关系,但是在见到这些问题的时候,我做了总结,避免自己也犯同样的错误。
54 谋事在人,成事在天。将付出的努力与结果课题分离,它们之间的关系类似追求的目标与附属品
有时候我们付出了很多,但是结果却非常悲伤。
这个时候就要用上阿德勒的课题分离了。付出是我们能掌控的,但结果不是,所以要把付出的努力与结果分离。
从另一个角度,也可以从目标与附属品的角度来考虑。如果我们追求的是结果,那一旦失败确实会非常悲伤。但如果结果是附属品,而过程是追求的目标,那其实结果如何已经不重要了。