团队内的架构师如果要参加技术设计评审,建议要有反馈机制,通过别人对设计评审的阐述,你自己的判断是什么?你自己坚持的点是什么?要有自己的观点,要能够去理解团队同学做设计背后的原因,作为架构师,必须要检出给一些反馈。无法给出反馈时要么是自己听不懂,要么不了解相关的模块,要么没有一些明确的原则,需要自己进行反思。
3.2.9 领域模型状态机
面向复杂的业务逻辑,我们设计了哪些模型,每个模型都有哪些状态,这些状态变迁是怎么变迁的,还有可能是要对现有模型和状态机做调整。业务系统的精髓我觉得就是在模型和状态机上,模型和状态机设计的好,就能够不断支持未来各种需求的演变,就能通过模型和状态机反推出业务形态,所以我们在设计文档里面需要重点阐述我们的模型和状态机。
这个点很关键,也是架构师必须要关注的,为什么要建立这模型,这个模型里面为什么有这么多字段,这些字段的含义是否都明确没有二义性,这个状态机里面为什么有这么多状态,每个状态到状态之间的变迁是否合理,每个状态的设计是否合理等等。所以每个写设计文档的同学,需要重点关注自己的领域模型和状态机。
3.2.10 数值、状态等多变的字段建议不要跨单据冗余
A单据上有个金额字段,B单据就不要冗余这个金额字段,冗余好A单据的编号就行,一旦两个单据上都冗余了金额,所有金额更新的逻辑里面都需要更新A单据和B单据,带来不必要的麻烦,甚至有些地方会查询A单据上的金额,然后去更新B单据上的金额,如何保证你查询到的金额一定是最新的呢?除非这两个单据上的金额在同一个事务里面去更新,否则数据一致性问题随时可能发生,甚至会让业务逻辑崩溃。状态字段也一样,一些多变字段不建议冗余。
3.2.11 对客表达和系统实现解耦
我们的系统实现是确定的,面对千差万别的客户诉求,一定要做好转换和抽象。抽象就是所谓的领域模型,这里重点提一下转换,用户侧的表达基于系统里的模型数据,我们没有必要把用户看到的一些点100%落到系统实现里去(这里不是指用户看到的数据和我们的系统数据不一致),比如用户看到页面上有个业务动作是处理中,我们模型里面有多个状态对客而言都是处理中,这个时候就需要把这些状态统一对客描述成处理中即可,不用说我们的状态机里面必须要有一个处理中的状态,因为这个处理中的状态可能涵盖了太多的情况,会导致我们系统实现时非常复杂。
3.2.12 架构让代码组织更有序
在写设计文档的时候,我们也要考虑相关业务逻辑的代码在编码阶段写到哪里,也需要提炼一些原则,就像画系统架构图时把系统划分成几个框,那些代码写到框里,这也是我们在设计评审阶段要考虑到的,避免代码放得乱七八糟,公共的代码放哪里,差异化的代码放哪里,相信每一个业务开发都喜欢有秩序的代码结构,而不是随意的代码结构。
3.2.13 离线在线切分开
在线的业务逻辑和批处理任务需要切分开,避免跑批任务导致机器负载高影响在线接口的响应。如果批任务本身就是异步的,失败了还可以重复跑,但一些在线响应中断或者无法在指定时间内反馈就会有客诉。业务逻辑不建议强依赖离线数据,分布式系统中引入网络节点越少系统稳定性越好,过多的网络节点引入会让系统复杂度、数据一致性保障的难度加剧。
3.2.14 前端重点关注交互和渲染即可
很多产品需要有对客交互和操作,不建议把太多的业务逻辑放在前端。对客的页面渲染和用户操作的交互逻辑放在前端,流程的控制全部放在后端。如果前端有很多业务逻辑,则运行情况难以监控,一旦出问题需要去看客户端的运行情况,较难发现。最重要的是无法通过后端链路看清楚整体的业务逻辑,出现到底哪些业务逻辑放前端哪些业务逻辑放后端的讨论(一些常规的表单校验这种放前端没有问题)。
前端后端交互时,不要把整个后端的领域模型给前端,按需返回前端做交互和渲染的数据,返回一个字段容易,下掉或者更改一个字段就很难。
3.2.15 新技术新框架的引入要慎重
作为技术同学,我们渴望把自己学到的新技术新框架在项目中去落地使用,这本身没有什么问题。但是在引入新技术新框架时,请仔细评估一下新技术新框架的引入要解决什么问题?当下业务形态面临的问题能否被解决?如果只是为了引入新技术新框架炫技,大可不必,因为这些新技术新框架没有被广泛使用,可能会出现一些问题短时间内无法解决,有些问题运行一段时间才会暴露,暴露的时候发现引入的新技术和新框架已经不再维护了,只能自己去研究底层源码解决问题。
重要的业务系统千万不要为了追随潮流第一时间引入新技术和框架,用最成熟的技术和框架去解业务需求。当然,我们就是为了要尝试新技术新框架,可以建议在一些非核心链路去尝试,等这些新技术新框架广泛推广开后再逐步引入到核心系统核心链路中。
3.2.16 离线数据的依赖需要评估
我们经常会调整一些单据的状态,比如原来有三个状态,由于后续一些需求无法满足需要新增一个状态,这个时候一般对代码里的逻辑做兼容性处理,评估的时候也需要考虑离线的数据是否有其他下游依赖,避免离线数据的下游依赖没有及时感知到状态的变化而产生错误的离线数据,对业务分析、离线业务逻辑产生意料之外的影响。
3.2.17 跨团队跨部门的交互要非常细致
如果项目有跨团队跨部门的协作,那么设计文档要非常细致,定义好每个接口的出入参以及里面的每个字段,要定义好消息的TOPIC和TYPE以及消息体的每个字段和含义,以及系统之间在哪个节点交互,同步还是异步等等。这些一定要在设计阶段明确清楚并且形成文档,这样各团队和各部门就可以各自按照约定并行开发并行内部联调,如果这阶段约定不清楚或者不详细,后续协调收尾的成本会非常高。
3.3 项目计划
做完设计评审后,需要把明确的项目计划同步到需求发起人,当前在设计评审前可能已经预估了大概的计划,但是设计评审后,我们的研发计划会更加清楚,需要及时反馈给需求发起人,让他们对需求的交付时间有一个明确的认知,如果在交付过程中发现计划有变,或者有更高优先级的事时,要第一时间和需求发起人商量计划变更的事情,千万不要自己默默改变了计划,需求发起人不知道计划变了,也不知道计划为啥变了。
大的项目一般都有非常正式的项目推进过程,计划有变都会通过项目周报体现出来,一些小的需求也需要把计划的变更及时同步,往往这些小的需求计划是容易被忽略的,导致业务方可能对我们交付过程不满意。所以不管大项目还是小需求,一旦自己的承诺发生不可抗力的变化后,要及时同步出来,避免别人误解。
3.4 代码编写
能够安安静静写一天代码对开发同学来讲真的是一件很幸福的事情,没人打扰自己,相关业务逻辑也是一气呵成不中断,能够全身心的沉浸到代码和逻辑里面,但是我们的环境好像挺难做到的,写着写着旁边同学问你个问题,写着写着业务找你讨论个问题,写着写着有个突发的线上报警需要看,貌似上班时间一直都是写会代码,中断一会,中断后继续写,虽然感觉这不是很好,但是没有更好的办法,我这么多年貌似只有周六周末才能有这样安静的环境。做好设计评审后,我们如何做高效高质量的代码编写呢?
3.4.1 代码写出了PRD里面没考虑到的逻辑
代码写着写着发现有些逻辑在PRD里面没考虑到,这时候千万不要自己默默承担下所有,优先找PD看要不要做一些需求上的变更,和项目组或者需求涉及到的同学一起沟通,决策要不要调整逻辑重新开发还是保持现状,这个点很关键,做法也很简单。但大家经常自己默默抗下了所有,或者自己和PD默默扛下来了所有,没有其他人知道,这是非常不可取的做法,没考虑到的逻辑还是需要和项目组所有同学达成共识并且留好痕迹,一些对工期影响较大的变更,需要做好正式的需求变更,把信息让更多的干系人知道。
3.4.2 没把握不要重构别人的代码
不太建议在项目开发过程,没有做设计评审就直接重构别人的代码。有时候写着写着,发现别人写了一堆if/else,或者日志乱打,或者代码结构不清晰,非常有想重构的冲动,这种想法每个开发都会有,而且对代码越有追求的开发越有重构的冲动。重构是好事情,但是不要做冲动式的重构,要有准备的重构。怎么才算有准备,设计评审时我们提出了要重构、对应的代码有单测做保障、已经提前和测试打好招呼说要重构。一时冲动的重构往往会带来各种问题,需要自己去收场,甚至会产生线上问题和资损,所以大家要做有准备的重构。
3.4.3 follow现有系统中的代码风格
在开发时经常发现当下系统里面的代码风格和自己的代码风格不匹配,是坚持自己的代码风格还是学着去匹配当下系统的代码风格呢?个人不太建议一个系统中,代码风格有多种,如果无法把当前系统代码组织形式做重构,那就匹配当前代码的组织形式,不要创造第二种风格。避免系统里面的代码看上去无法理解为什么这么写,带来后续理解的成本。
3.4.4 断言式的校验不要忘
在日常开发过程中,一定要具备悲观意识,大家经常提的一个词就是「兜底逻辑」。我们除了通过代码去实现正常的业务逻辑外,还需要考虑一些兜底的逻辑校验,比如在执行某些逻辑之前,有一些金额恒等式、前置状态等等,先做断言式的校验,然后再开始写正常业务逻辑,不要觉得有些断言是显而易见的,代码里面不用多此一举,这些显而易见的逻辑就是系统里面的断言,这些断言无论如何都要保障,一旦断言被击穿,就需要立即中断后续业务逻辑的执行,因为系统中的一些规则被打破了,在继续执行只会在错误的基础上继续错误,这些断言往往就是我们救命的稻草。
3.4.5 先写模型状态机再写数据存储层
一般复杂的业务逻辑中我们会抽象很多模型,同时会有很多单据出来,有些开发同学喜欢先把DAO层写好,再组织业务逻辑,其实这是不可取的,因为这层是最简单的一层,如果你先把最简单的一层写好,再写业务逻辑层,这相对比较复杂,会发现你的数据存储层要么字段不全、要么方法不全,就会来回改DAO层,导致不断修改和反复,最合理的方式就是先把模型状态机写好,再把业务逻辑写好,这时候存储层就是一些接口,等这些逻辑稳定后再去写存储层的代码,存储层的代码以及SQL语句非常不建议用工具生成,有些工具生成了大的update语句,一行update下去你都不知道更新了啥,要写符合业务逻辑需求的DAO层,每个数据存储层要更新的字段都是明确的,甚至在更新前对前一个值要有约束的,不是任何情况下都可以更新的,相关金额的更新一般建议都走增量更新,不要全量覆盖。
3.4.6 唯一性约束
我们抽象好模型后,要做一些数据存储,每个存储的单据必须要有唯一性约束字段,这个非常重要,一些核心的单据在数据库里面重复出现会出现一些意想不到的问题,如果已经出现了,解决起来非常麻烦,所以单据要存储的时候必须要考虑好自己的唯一性约束是什么,不允许存在没有唯一性约束的单据。
3.4.7 本地事务
我们的代码有时候需要更新多个单据,不要心存侥幸,更新多个单据的时候请务必开启事务,不然线上出现数据不一致的问题时需要耗费很大的精力去查。不建议通过注解的方式开启,建议通过代码的方式开启,这样更加直观。另外事务里不要嵌套事务,嵌套的事务出现线上问题时,有时候很难想清楚到底有没有提交,特别是遇到一些紧张的线上问题时,同时又是嵌套事务,排查成本就更高了,能简单解决的问题不要引入复杂解法。
开启事务前,请对数据的隔离级别有一个清晰的认知,不要想当然的认为数据库的隔离级别是什么,自己通过SQL查询一下。在事务中不要包含远程调用(涉及到通过网络的交互),一旦事务中包含了远程调用就不纯粹是个事务了,一来远程调用RT过大会导致事务长时间无法提交,导致DB链接长时间占用,二来本地事务回滚后,远程调用可能已经发生,跨系统的数据不一致性就这么产生了。曾和别人联调项目时遇到一个逻辑分支,监听到别人MQ消息后去反查单据,在日常环境有时候能反查到,有时候反查不到,重试还总能查到,非常神奇,前前后后不断地定位,不断地DEBUG,问题无法复现,查来查去发现上游的MQ消息是在事务中发的,单据是在事务中创建的,那问题就很明显了,有时候MQ消息投递后事务提交了,那就能查到,有时候MQ消息投递时,事务没提交,那就查不到了。
3.4.8 幂等重试
在日常代码编写过程中为了完成一段业务逻辑,除了本地操作,有时候还需要发起远程调用,这里幂等非常关键,幂等不仅仅要考虑本地业务逻辑执行的幂等,还需要考虑自己调用的远程接口是否也幂等,是否支持重试,我们代码在执行时完全可能遇到系统宕机、网络遇阻等等,导致一些逻辑执行了一部分,当系统恢复的时候这些已经执行了的部分逻辑还能否继续执行,这是每个开发都要在写代码时要考虑的问题。
任何一个接口调用,都有可能出现三种情况,一个是明确的成功,一种是明确的失败,一种无法判断成功还是失败,明确的成功与失败都好处理,无法判断的成功和失败都有可能需要我们做重试,因此一定要思考代码重复执行时能否正确地执行下去,在分布式系统中幂等和重试非常关键,写代码时需要时刻考虑,按照分布式系统的CAP理论,我们目前的分布式系统大家都很难做到C,只能做最终一致性,而最终一致性就要靠幂等重试来保障。
3.4.9 批处理任务要隔离环境并且具备单条执行的能力
我们经常会写一些跑批任务,写跑批任务时要注意捞取预发环境产生的数据、线上环境产生的数据,如果数据不区分的话,可能线上会把预发的数据执行掉,导致你在预发测试的时候还要把线上的任务停下来;另外,有可能测试需要执行单条数据去观察相关单据的情况,所以批处理任务需要具备能够执行单条任务的能力,不然全量执行可能会污染测试构造的好的一些用例。
3.4.10 基于查询结果做满足条件的更新
经常会写先查询某个值,如果满足一定的条件再去做更新,需要思考一下去更新的时候,更新前查询到的那个值还是原来的样子吗?有没有可能在查询后、更新前被别的线程更新掉,导致查询到的值和实际持久化存储的值不一样。这个点很关键,写代码时一定要考虑,不然遇到类似问题排查验证成本非常高。如果查询后值有可能变化,而且还要把这个变化的值冗余到其他单据上,这个是非常危险的动作,会导致冗余的值和实际的值不一样,产生各种各样奇怪的问题。
3.4.11 单元测试
单元测试,这又是老话题了,写单测需要时间,那就把写单测的时间评估到项目计划里去,这是软件工程发展的经验,单元测试是针对自己所负责的模块以及系统内部的逻辑做测试,怎么保证自己的所负责的模块和系统没问题,就是靠丰富的单测,确保程序的输入和输出都符合设计时的约定,然后再和别人去集成,避免多人集成时,调用到自己负责的代码时,一调一个BUG,一修修半天,严重阻塞项目的联调进度,给别人也会留下不好的影响,单测是自己保障自己代码质量的重要手段。
另外,自己写完代码同时有单测保障,过了一个月后,自己又需要修改一些代码的时候,修改完跑过了所有的单测用例,你会觉得就算出问题也不是什么大的问题,别人改你代码的时候,可以通过单测去熟悉你的逻辑,改完之后,你的代码也自带保障机制,因为有单测覆盖。通过单测,你可能会构造出非常丰富的用例,最后测试同学测试的用例可能都没有你构造的丰富。所以,写单测是一个开发工程师良好的修养,也是为自己未来再去修改代码奠定了良好的基础和保障,也是正常业务逻辑的一个免疫体系,无非就是会多花一些时间,但是这些时间是值得的,毕竟现在用程序证明程序的正确性也只有写测试用例这一条路,写程序很爽,写单测证明自己的写的程序没问题也很爽。
有时候写写单测,发现自己写的代码单测一把过,这种成就感会让自己写代码的信心也会越来越强,越来越觉得自己写的代码就是高质量的,有时候写完单测调用一下,发现有问题,自己也反过来需要思考一下为啥写代码时没有注意到,通过这种小闭环的思考机制,让自己的写的代码经常一把过,也会让自己的信心和成就感逐步增强,也会让自己非常渴望去用单测验证自己代码的正确性,这就把写单测变成了自己的习惯,甚至下意识。
3.4.12 debug你的代码
记得刚毕业写代码的时候,写代码经常有时候考虑不全一些逻辑分支,当时就问了一位有经验的同学,说我们怎么保证自己写的代码没问题?他说把自己的写的代码全部debug一遍,当时也没多想,我就照做了,结果打开了debug的欢乐世界。debug代码时,当断点停留在那行代码上时,你会发现自己可以check很多数据,甚至改变一些数据去看一些异常case的执行是否符合预期,后面自己写完代码不管用什么方式我都争取把自己的代码debug一遍,确实问题少了很多,有时候基本上没有任何问题,上线时也非常自信,同时也积累了好多debug的经验。
3.4.13 代码不会骗人
自己负责的系统,如果有哪些逻辑不确定或者没把握,不要做自信的猜测,要找到代码或者线上运行的数据去证明理解是对的,千万不要猜一些逻辑,知识通过人传人会失真,但是线上代码一直在哪里,不会骗人。
3.5 项目联调
一个复杂的项目,往往是需要跨团队跨部门去协作完成的,这时就经常需要进行大规模的联调,那怎么做好高效高质量的联调呢?
3.5.1 定好计划及时报风险
在联调前先约定好要联调的链路以及计划,什么时候需要联调完哪些用例哪些case,如果没有按时间联调完,需要及时同步风险。个人认为联调阶段需要靠日报来保证联调的进度,今天要联调哪些case,今天结束后需要做个总结,实际联调了哪些内容,卡点是什么,需要哪些帮助,预计产生的影响是什么,这些都需要在每天联调的日报里面体现出来,避免调了两周一个case还没调通,为啥没调通呢,原因也一时半会说不出来,所以联调阶段的计划以及日报很关键,特别是跨团队跨部门的联调,想推进联调进度,没有其他更好的办法。
3.5.2 先内部联调再外部联调
在跨团队跨部门联调时,尽量先把内部的逻辑都调完,避免跨团队或者跨部门等待,有可能我们一个case不通,上游团队只能一直等待我们定位问题、修复问题,这样联调的进度会被拉的很长很长。设计评审阶段对于跨团队跨部门的描述一定要讲清楚,这样在内部联调阶段就可以按照当时的约定做联调。
3.6 代码评审
自己写完代码后,早点找团队内有经验的同学做代码评审,就像设计评审一样,需要一些他人视角的补充,帮自己保障线上的质量,补充自己的盲区,那代码评审该怎么做呢?
3.6.1 尽早发起代码评审
别要发布上线了,突然找到相关同学说帮自己做个CR,CR也需要时间,CR给出来的建议是改还是不改呢?改的话测试要重测,不改的话,CR的同学说你这不符合规范,也有潜在风险,自己很为难,CR的同学也很为难,所以尽早发起代码评审。
3.6.2 代码评审别秒过
如果别人找自己做代码评审,不要啥也不看直接过,一些明显的业务逻辑漏洞就出现在了生产环境,所以评审的同学一定要注意评审的质量,当然可能有同学担心代码评审浪费自己的时间去保障别人代码的质量,有点得不偿失,这么想我觉得肯定做不好CR,CR也是自我学习的一个方法,有些业务逻辑是其他同学写的,我通过CR也学到了这块业务逻辑;自己是这么写代码,通过CR别人的代码,发现了更优雅的写法;通过CR能够发现别人代码里面的问题,那你自己写代码肯定也不会出类似的问题,别人会一直相信你的CR质量,在团队内的影响力不一样。
3.7 用例评审
一般项目和一些大型需求,我们都会做所谓的TC评审,TC评审主要是测试同学主导,那怎么做好用例编写和TC评审呢?
3.7.1 对客用例来自于自己对PRD的理解
我们写用例的时候经常会写用户做了什么操作,页面上要看到什么,希望这些用例是自己通过需求评审自己总结出来的,不是开发告诉测试的,如果开发告诉测试,那没有意义,因为开发是按照自己的理解写代码,测试按照开发的理解写用例,这是有逻辑盲区的,万一开发理解错了呢?所以写测试用例的同学一定要对PRD内容有深刻的理解和认知,这样才能写出高质量的对客用例。
内容剩余60%,完整内容可点击下方链接查看:十年业务开发总结,如何做好高效高质量的价值交付-阿里云开发者社区
阿里云开发者社区,千万开发者的选择。百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,尽在:阿里云开发者社区-云计算社区-阿里云