一线开发人员,你对项目了解多少?
对于一个程序开发人员来说,对项目的熟悉程度意味着你对工作内容的掌握程度。那么你是否可以问一下自己,你对你的项目了解吗?
为什么了解
当然,了解是一个模糊的概念。同样,对于不同职责的人员来说,判断这个概念的准则是不一样的。而对于不同类型的系统来说,这个概念也有所不同。如果你是一位一线的代码工作人员的话,你可能对接的就是单独的几个领域服务的项目,那么了解他们的调用和库表关系可能就算了解了,你甚至能熟悉到哪个 bug 是在哪次版本迭代之后产生的;而对于一位团队的管理人员来说,尽管他可能对代码中的所有分界实现并不了解,但是他却能对业务的发展迭代了如指掌,比如那个功能特性依托于几个服务而来的。
抛开了边界,就无法做出回答,所以本文给出了这样一个定义,用来描述你对项目的了解程度这一概念:
拥有足够支撑对若干个具有互相依赖关系的项目进行重构的熟悉程度。
显然,这个对于一线开发来说可能较为苛责,却又并不是管理人员依靠单纯的业务跟踪就能了解的。所以我觉得,对项目的了解是需要从业务层面和 coding 层面两方面了解的。
怎么了解
只是有一个定义,对概念的支撑仍然略显单薄。但是换一个角度来说,如果定义是足够支撑重构的话,那么显然需要掌握一下几个东西:
对原有业务足够了解;
对原有代码足够了解;
对原有项目的下游支撑足够了解;
对业务的上游影响足够了解;
业务是本身非工程性质的,而上下游和代码本身则将你的项目包裹起来,我们划分了边界,先保证不会有东西遗漏。但尽管进行了目标拆分,他仍然是一个抽象的过程。因此我们需要一个 Check List,从而确当我们完成了这个列表之后原则上有能力可以守护住这些边界。
那么我们的 Check List 如下:
用例视图
领域模型
数据库设计
接口、代码数量、代码库
微服务和领域划分
微服务拓扑
你可以看到,上面的内容是基本涵盖了业务,以及代码的上下游(库表)的依赖关系。当然,如果你并不是一个后端项目、或者并不是一个微服务项目,那么这些 Check List 可以根据你的实际情况进行调整。
尝试了解
大家的项目细节可能不同,本文仅以其中一个时间进行一下分享,用于提供思虑。对于已经明确了的 Check List 我们下一步要进行的就是梳理工作了。
用例视图
用例视图的目的是对业务的一种收敛,以当前时间维度的角度,对过往的若干需求进行重新评估。除了和已有功能需求相对应的用例外,经常出现的是两种额外状况:
发现某个功能已经没有用例了。
发现两个功能的用例是一样的。
对于前者来说说明这个功能因为无法匹配用例,说明他是无效的了,那么如果重构的话,这部分的功能是可以不予理会的。对于后者来说,用例相同的情况下,说明两个功能其实是可以进行抽象的,通过调整参数,或者进行底层复用,两个功能合并成一个功能(不过要确保两个的用例确实一样)。
同时,用例图可以辅助梳理用户们的实际需求,进而描述了业务。
领域模型
领域模型的构建,主要是通过模型将复杂的领域逻辑以模型的概念和模型元素的形式清晰地表达出来。在项目长时间的进行开发和迭代的时候,不可避免地出现很多意外的情况。而重新梳理领域模型的目的,就可以从长时间迭代的(已经腐化的)代码中重新构建模型的关系。
举例而言,当我们在第一版本的时候随意地开发了一个短信发送需求,功能开发完毕了之后可能设计了一个短信模版表。而后来又有了新的需求,要对用户进行手机的 push 推动功能,再后来就可能是内部人员的钉钉或者飞书通知功能。那么对于以上的需求,我们发现可以抽象出来一个新的领域模型。
通过对领域模型的重新确定我们可以用新的模型去检验项目内部中的实体抽象、功能复用的情况是否理想,从而确定对于该项目是否需要写。
数据库设计
经过领域模型的梳理后,我们可以确认数据库的库表设计是否和领域实体一致。如果不一致则可以考虑进行调整或者重新设计(但是要考虑双写成本)。和领域模型一样,由于业务的发展,很多时候我们在进行数据库设计的时候 都无法预知到后续的业务方向变化,没有足够恰当的抽象的情况下,其结果可能就是会产生不那么优雅的解决方案:
对一些数据库通过添加字段的形式完成需求
通过对相类似的表的 copy 的形式来实现需求(xxx_order,zzz_order)
无论哪种原因,都会导致数据库中的实体内容不纯粹,导致后续扩展性降低,或者业务开发工作量变大。
数据数据库设计也可以从最底层的角度上看到业务的边界,因为最底层的支持数据是通过数据库完成的,如果数据库不包含其中的数据,则说明在业务中产生的数据是通过非本系统持久化的方式获得的,可以一定程度上认为是中间数据。同时数据库上的引用分析也可以暴露出领域服务是否干净,是否有直接使用非自己领域的实体表数据。
接口、代码数量、代码库
接口一般可以从 api 的分组上体现用例的关系。同时每一次接口的调用可以视为一次数据流传(cosmic 功能规模度量方法),可以通过接口数量的规模对软件开发工作量进行评估。与此同时,代码行数则可以直观地标示出项目的大小,也是评估工作量的体现。而代码库则可以根据软件迭代总时长,和 commit 的次数,判断出项目的腐化程度,和业务耦合难度。
微服务和领域划分
如果你的项目里有微服务的话,那就要确认一下微服务之间和领域模型之间是划分明确。同样的,划分不明确的问题经常会出现在持续迭代的过程里,例如当新添加的一个功能由于某些不可抗原因被添加到了错误的服务里,而后续依赖这个功能的服务直接引用了后,这个功能就再也分不开了。另一种情况是,微服务之间的领域关系一开始是明确的,但是由于业务的转型,在逐渐地迭代工程中,会有一些模凌两可的功能被添加到了看起来所属的服务中,但当后续的业务转型完毕后,新的服务划分出来,但是老的功能却留在了那里。
但不论怎样,当我们分析出微服务和领域模型的划分关系后,我们就知道了哪些看起来位置不对的功能到底应该放到哪里了。
微服务拓扑
当我们分析完了所有服务向下依赖后,我们就能得到一张所有服务间的依赖关系拓扑图(DAG 图)。那么,这种拓扑图中是否存在环结构就是关键了。如果你发现所有服务间调用的关系是一个完整原型,那么恭喜你,你需要好好地设计一下了。但如果仅仅是简单一个环,那说明只有少数服务存在循环依赖问题,我们可以针对这部分依赖进行拆分,为循环依赖部分寻找一下新的归属,或者直接将其拆解成一个新的服务。
最后
完成了 Chec kList 的梳理的,我才你会惊叹于自己原先的设计是多么完美,又或者是吐槽原来的开发人员是多么不规矩。
不过无论是哪种情况,梳理完毕的你我猜已经对你的项目有了一个大概,即便无法进行项目的重构,至少在别人再问起你为什么开发要这么长时间的时候,你就可以很硬气的怼回去“这项目我了解,就是要这么长时间啊!”。
版权声明: 本文为 InfoQ 作者【蜜糖的代码注释】的原创文章。
原文链接:【http://xie.infoq.cn/article/aabed94a67e94a6f2f4ffb42d】。文章转载请联系作者。
评论