以下是笔者最近一年在开发新系统,维护老系统,重构过程中总结出的经验。

软件系统的生命周期可以归纳为以下五个大方面:

  • 问题定义
  • 可行性研究
  • 需求分析
  • 开发阶段
  • 维护

开发阶段依照软件开发的瀑布模型,我们会需要重要的几步:

  • 可行性分析
  • 概要设计与详细设计
  • 开发实施
  • 测试
  • 文档编撰

由于大家都知道的原因,也即以各种理由推脱重要环节的必要性,从而节略过部分环节来进行开发,这样做的后果严重一些可以说是遗祸无穷。

首先是,我们不可小觑这些基础理论,认为已经过时,不适合如今互联网方面快速迭代开发的节奏。笔者认为并非如此,可以这样说,快速迭代开发的过程本身即是对瀑布模型的改进,在每一个迭代周期中,对系统反复地进行需求、可行性、实现方案的重新审视,改进,这并不过时。甚至可以这样说,越是频繁变更的系统,越是需要持续不断的审视

在实际生产过程中,由于制度、团队管理风格上的差异,导致概念并未完全传达,看上去好像不再有这些环节,其实由于大多数情景下并不需要作出改动,所以某些环节跳过。这并不代表整个环节不再需要。作为团队的管理者或者系统的负责人,要认识到问题的定义,需求的确定,数据模型与数据关系,系统的实现在每次系统变动后是否仍旧适用,需要进行持续不断地审视与重新规划

四个要点

为此,笔者结合在中大型团队(各部门超过30人,总计超过200人)的约四年经验,最近一年多在相对较小团队(小于10个人,小于3个团队)内的运维与开发经验,得出以下四个要点

  • 好理解
  • 好实现
  • 好调试
  • 好维护

好理解

好理解即,是否能用一句话或者一段话,向另外一个人说清系统或模块或组件,完成的功能、满足的需求

借用蟒之禅

If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.

一个(小型)系统,一个模块或者一个组件的功能应该清晰、明了,并应该简洁、单一。且思路应该尽量直白,符合常人一般的思路(避免否定、反向、嵌套等)。

反面例子:想想看自己参与的系统里是否有大段的函数或模块,无法理解其中功能的细枝末节;是否有一个流程中的环节不符合思路的直觉,理解需要转脑筋

这些现象都有一个共同的特点:缺乏注释或文档,只能够口口相传,甚至严重者,连现任维护负责人亦无法道尽其中“奥妙”

本环节的恶性后果可谓最为严重,即遗祸无穷的类型,设计环节的纰漏、不合理将很有可能导致系统、模块整个出现流程上的僵化,最终别说重构,直接推翻重做的情况更是屡见不鲜。

好实现

好实现即,在数据存储、数据结构与模型,代码组织,代码实现方式上尽量以直白,符合思路直觉的形式避免过分讨巧的手段,完成预计目标满足需求。

设计思路是否好实现,归咎于多个方面:

  • 数据存储、数据结构与模型的形式
  • 算法实现与理解的难易程度
  • 使用工具的广泛性与团队内的实际可行性

反面例子:想想是否有系统中数据关系不合理实体关系模型存在不合理的情况?应用框架的设计思路、强行堆砌、照搬设计模式,导致不好理解又不好开发,新加入成员无从下手的情况?应用新设计,新思路时不顾场合当作实验田肆意折腾

挑选语言、使用工具时应尽力避免平台、操作系统相关性,采取应用更为广泛,易获取,满足需求又好用的软件工具。

好调试

好调试,即在开发(也包括部分后续维护)的过程中,定位问题,观测组件、模块间的数据与代码组件应尽力保持清晰,工具容易获取和使用,展现出的内容应是调试者易于理解的形式

反面例子:想想存储若使用分布式,或者数据查找有嵌套的情况下,查找一条数据是否正常显得异常繁琐?有多少系统在生产环境出现问题时,监控显示异常,却无从下手定位(往往是代码缺陷)问题根源?

调试的难易程度可以体现在

  • 挑选语言本身的调试难度,例如静态语言的工具链,动态语言的调试方法等
  • 调试信息设施,例如调试时的数据监控与日志
  • 展现出来的数据可以做形式上的转换,使得调试者更容易理解

以上这些问题在开发大型系统,例如分布式系统与SOA、微服务架构中较为突出,例如

  • 分布式系统节点间交换信息时如何提升调试便捷程度
  • SOA与微服务架构设计中,如何追踪同一条请求的流向与各环节的情况

同时由于大型系统在性能上的要求,数据存储形式很可能采用分布式、二进制形式等手段,此时应提供便捷的手段来快速查看数据情况,等等。

好维护

好维护,即在设计、开发阶段即考虑后期的维护特性,提高灵活性,降低多方面的耦合,为数据、代码、部署等多方面预留改动空间数据、代码运行环境、部署等方面做好监控、报警与告警的设计。对于系统应有一份从使用到开发维护所必要的文档。

维护虽然放在最后一点,却是综合起来最重要的,因为前面的各个环境都决定了维护难易程度。

反面例子:将不确定的假设设计成硬编码的实现;实现时,未将频繁变更的组件拆解成松耦合,导致不仅代码需要动,甚至连模块结构,数据甚至部署都要跟着动;系统运行情况未知,错误往往需要最终用户才能发现。

问题定义、需求分析到设计阶段来看,需要留下系统解决问题的目标,手段和范围划分

系统组件结构、模块划分、代码安排角度看,应将数据、软件系统代码、呈现结果(输入、输出)隔离,必要时拆开为松耦合的组件以便于改动模块、系统组件时,保持数据交换的灵活性

知晓系统运行时状态角度出发,应评估何谓运行正常并给出可以量化的值作为监控进行观测,同时针对重要的业务、系统指标,出现异常时告知负责人,方为有效的报警

最后一点是前面三点的综合体现,并同时具有特有的属性,是重中之重。

__END__