上世纪60年代后,由于“软件危机”的推动,人们开始借助工程化的方式解决软件开发中质量、成本和工期不可控的问题,形成了软件工程学。
软件工程学从其他人类工程学(如建筑学和硬件开发)中吸收了许多成功的经验,明确提出了软件生命周期模型,并发展出了各种在软件开发与维护阶段适用的技术方法与工程实践,这些都在各种软件项目中取得了一定的效果。记得当年大学计算机软件工程课老师最喜欢用建筑学做类比,讲到工程、复用和模式之时必提及Christopher Alexander及其在建筑学上的相关著作。
遗憾的是,虽然软件工程将科学的项目管理方式引入到软件开发交付过程中,但是并没有很好解决软件的一个核心问题,那就是 “变化” 的问题。软件在其全生命流程中(无论交付后还是交付过程中),都会面临由于需求变更或者技术演进带来的相对更高频的变化。
正因为软件在开发和交付后一直充满着变化,所以人们往往隐喻软件是“具有生命”的。从这个角度来说,软件开发有些像“养小孩”而非“做雕塑”。单纯用“硬件式”的工程方法被证明是难以做好复杂软件的。
现代软件工程开始强调“敏捷”和“演进式设计”的能力,软件公司也更愿意重视“人”和“文化”等因素,正是以动态生长体的方式来看待软件。
如果继续对软件领域进行细分的话,会发现不同领域的软件变化频率其实是不同的。
软件门槛变化率曲线上图展示了软件的”门槛变化率“曲线:交付门槛越低的软件,变化频率越高,而交付门槛越高的软件,变化频率越低。这也是软件工程复杂的原因之一:很难有一条可以完全复制的成功经验。务实的做法是根据自己的定位,探索适合自己的方式。
对于变化频率低的领域,“管控”、“规划”和“度量”为主的工程和设计方法仍然是有效的;而对于高频变化的不确定性较强的领域,以“引导”、“反馈”和“持续演进”为主的工程设计方法则往往会更加有效。
但是无论如何,软件要求能够复用的特点决定了,软件要能基于自身进行持续的生长。变化频率低只是生长速度慢,微小但是不断累积的变化放到足够长的时间里面看也是不容小觑的。
所以即使变化频率低的领域,只要软件生命周期足够长,向下兼容的时间久,就需要具备让软件持续响应变化,长期保持健康生命力的能力。
这种能力被称为 “演进式设计”。
关于演进式设计,Martin Fowler在2004年写过一篇文章《Is Design Dead?》,辩证的阐述了其与“规划式设计”的关系。这篇文章是关于演进式设计不可多得的佳作,这里是其中文翻译版。
演进式设计被认为主要由以下六部分组成:
演进式设计其中,“完善测试”、“及时重构”和“持续反馈”,组成了演进式设计的工程能力部分。具体包括完善的测试体系、重构能力以及能够快速反馈的持续集成和交付流水线。
上述能力也是现代软件工程的基础,有大量的著作对此做介绍,如《敏捷测试》、《开发者测试》、《测试驱动开发》、《重构》、《持续集成》、《持续交付》、《演进式架构》等等。如 Martin Fowler所说,这些工程实践的目的是 “拉低软件的变化成本曲线” ,保证设计的调整可以发生的足够频繁。
而“保持简单”、“设计技能”和“延迟决策”则组成了演进式设计的设计能力部分。《敏捷软件开发原则、模式与实践》以及《浮现式设计:专业软件开发的演进本质》等著作有关于这方面的阐述。
不过演进式设计中关于设计方面的优秀著作相比工程实践方面的还是少很多。庆幸的是袁英杰写过几篇极佳的文章,非常适合用来阐述演进式设计所需要的设计能力。
-
这篇文章《简单设计》,通过讲解“简单设计原则”,很好阐述了什么是演进式设计中强调的“保持简单”。
-
这篇文章《变化驱动:正交设计》非常经典的总结了演进式设计中需要的可以应对变化的“设计技能”。
-
另外一篇《第一颗子弹》极佳的解释了什么是正确的“延迟决策”。
最后我们总结一下演进式设计:
演进式设计强调让设计尽量 “保持简单” ,对不确定的变化需要 “延迟决策” ,直到被某个方向上的“第一颗子弹”击中时,再通过应对变化的 “设计能力” 进行设计调整,并同步将新的设计 “及时重构”到代码中,让代码从此在对应的方向上可以保持弹性。
演进式设计强调上述过程要能够及时频繁的发生,不要堆积。这需要自动化的 “完善测试” 和 “持续反馈” 来持续保证设计和代码变更尽量的低成本和安全有效。
总结完演进式设计后,下一篇中我们着重谈谈在软件编码中如何解决具体代码中存在的变化,作为对演进式设计中代码实践的补充。