Measure twice, cut once.
谋定而后动。
对于软件构建而言,
- 前期准备的意义是什么?
- 前期准备的活动有哪些?
- 各个活动的时间花费应如何安排?
- 相关好书推荐
意义何在?
就像修建建筑物一样,项目的成败很大程度上在构建活动开始之前就已经注定了。如果地基没打好,或者计划不充分,那么你在构建期间能做得无非是尽量让损害最小罢了。
软件构建活动差不多占整个项目成本的65%。最糟糕的软件项目最终会进行两三次(甚至更多)构建。而良好的前期准备可以很大程度降低这种风险。其实它的中心目标就是降低风险。首先确保你在做正确的事情,后续正确地做事(构建)才有意义和产生正向价值。
诉诸逻辑
从管理角度看,做计划意味着确定项目所需要用的时间,人数以及计算机台数。
从技术角度看,做计划意味着弄清楚你想要建造的是什么,以防止浪费钱去建造错误的东西。
而且,与“先做一个错误的东西出来,然后扔掉并从头来过”的成本相比,花费比理想情况下更多的力气,找出他们真正想要的东西这种方式成本更加低廉,更值得投入。
诉诸类比
以食物链作为类比,如果食物链各层都受到了污染,那么越是食物链顶层的生物,最后被感染的程度就越严重。
诉诸数据
25年的研究数据证明,在一开始就把事情做好是最合算的。
惠普,IBM,休斯飞机公司,TRW以及其他组织的研究人员发现,在建构活动开始之前清除一个错误,那么返工的成本仅仅是在开发过程的最后阶段(在系统测试期间或者发布之后)做同样事情的十分之一但百分之一。
引入缺陷时间与找到缺陷时间,与修复缺陷的费用,之间的关系。(说明前期准备的意义)
对自己负责
只有当你习惯了这样的思维,你才有能力打造高质量的系统,因为未来的软件系统是极其复杂的,没有科学的思维和方法是没有办法在未来软件行业走得更远的。
总而言之
目标其实简单来讲是围绕以下三个子目标开展的:
- 降低风险;
- 降低成本;
- 提高质量。
前期准备的活动有哪些?
都有哪些活动可以保证我们做到降风险,降成本以及提高质量?让我们先来认识下都有哪些活动,然后再分别介绍它们都起到什么样的作用:
- 辨明软件类型
- 定义问题(确定正确的靶子,即方向)
- 定义需求(确定正确的靶心)
- 定义架构(用正确方法击中靶心,使用了正确的解决方案瞄准正确的问题)
辨明软件类型
迭代开发法对前期准备的影响
通过两个表对比,我们就可以知道有无进行前期准备的成本大致消耗。
由两个表的数据(需要合理怀疑数据的准确性)说明,跳过前期准备,使用迭代方法,成本将会在整个项目过程当中分次支付,而不会聚集在末尾一次性支付。整个项目尘埃落定之后,实际的总成本是相似的,但看起来没有那么高,因为开发费用是在整个项目进行过程中分期支付的,而不是项目最后一次性结账,心理作用?
但有合理的前期准备,无论迭代式开发法或序列式开发法,都可以减少成本。(而为什么现在大家都选用迭代式开发法,就是为了可以持续的交付,适应市场竞争。)
一条很有用的经验规则是,对于序列式开发法,计划好预先对大约80%的需求做出详细说明,并给“稍后再进行详细说明的额外需求”分配一定的时间。然后在项目进行过程中,实施系统化的变更控制措施——只接受哪些最有价值的新需求。另一种替代方案(迭代式开发法)是,预先只对最重要的20%的需求做详细说明,并且计划以小幅增量开发软件的剩余部分,随着项目的进行,对额外的需求和设计做出详细说明。如图所示,说明了两种方法活动情况。
定义问题
对系统需要解决的问题作出清楚的陈述。问题定义只涉及问题是什么,而不涉及任何可能的解决方案。
需要使用客户的语言,而且从客户角度来描述问题。因为最好的解决方案未必是一个计算机程序。
没有好的问题定义,你努力解决的可能是一个错误的问题。
定义需求
构建期间处理需求变更
在构建期间遇到需求变更,可以有以下几种方式评估和处理:
-
使用需求核对表来评估需求的质量
- 确保每个人都知道需求变更的代价
- 建立一套变更控制程序
- 使用能适应变更的开发方法
- 放弃这个项目
- 注意项目的商业价值
定义架构
意义何在?
架构的质量决定了系统的“概念完整性”。一个经过慎重考虑的架构为“从顶层到底层维护系统的概念完整性”提供了必备的结构和体系,它为程序员提供了指引——其细节程度与程序员的技能和手边的工作相配。它将工作分为几个部分,使多个开发者或者多个开发团队可以独立工作。
架构的典型组成部分:
程序组织
对系统进行综述(这个综述指什么?图示?文字?)。你首先要了解完整的框架,这样你才能理解你正在开发的那个类对系统有何贡献。
要了解选定当前组织结构的缘由。经过与其他备选组织结构方案的对比,你可以清楚知道为什么要选定这样一个方案,准确把握它的出发点和完整清晰的构思对后续的设计细化等很重要。
定义程序的主要构造块。构造块负责自己区域的事,且对其他构造块负责的区域知道越少越好。通过使各构造块对其他构造块的了解最小,你能将设计的信息局限于各个构造块之内。
明确定义构造块间的通信规则。架构应该描述它能直接使用哪些构造块,能间接使用哪些构造块,不能使用哪些构造块。
主要的类
详细定义所用的主要的类。指出每个主要类的责任,以及该类如何与其他类交互。包含对类的继承体系、状态转换、对象持久化等的描述。如果系统足够大,它应该描述如何将这些类组织成一个个子系统。
详细说明构成系统80%行为的20%类。
数据设计
描述所用到的主要文件和数据表的设计。与曾经考虑过的其他方案对比,说明做出选择的理由。有这些理由和对比,在构建期间,可以让程序员能洞察架构师的思想。在维护阶段,这种洞察力是无价之宝。离开它,你就像看一部没有字幕的外语片。
数据隐秘。只应该由一个子系统或一个类直接访问,或其他受控且抽象的方式来访问。
详细定义数据库高层组织结构和内容。解释为什么使用单个(多个)数据库。解释为什么不用平坦的文件而用数据库。指出与其他访问统一数据的程序的可能交互方式等。
业务规则
详细描述架构依赖的业务规则。(业务规则包含哪些?例如客户信息及时更新且同步中更新时间不得超过30s)用户界面设计
将用户界面模块化。如果需求阶段没有进行详细说明,就应该在架构中详细说明。模块化可以使我们很容易做到:砍掉交互式界面的类,插入一组命令行的类。因为命令行界面便于单元级别和子系统级别的软件测试。
资源管理
描述管理稀缺资源的计划。稀缺资源包括数据库连接、线程、句柄等。架构应该估算在正常情况和极端情况下的资源使用量。
安全性
描述实现设计层面和代码层面的安全性方法。建立威胁模型。制定编码规范的时候把安全性牢记在心,包括处理缓冲区的方法、处理非受信数据的规则、加密、错误消息的细致程度、保护内存中的秘密数据。
性能
如果关注性能,需在需求中详细定义性能目标。性能目标应该详细定义资源(速度、内存、成本)之间的优先顺序。
架构应该提供估计的数据,并解释为什么能达到性能目标。如果某些部分存在达不到性能目标的风险,架构也应该指出来。如果为了满足性能目标,需要在某些部分使用特定的算法或数据类型,架构也应该说清楚。架构中也可以包括各个类或各个对象的空间和时间预算。可伸缩性
描述系统增长以满足未来需求的能力。描述系统如何应对用户数量、服务器数量、网络节点数量、数据库记录数、数据库记录的长度、交易量等的增长。如果预计系统不会增长,那么也应该明确列出这一假设。互用性
如果预计系统会与其他软件或硬件共享数据或资源,架构应该描述如何完成这一任务。国际化/本地化
描述程序支持多个地域/文化的技术活动,包括支持当地特定的语言,字符集等工作。架构应该决定,是在代码中直接嵌入字符串;还是将这些字符串封入某个类,并透过接口来使用它;或者将这些字符串存入资源文件。并解释选用某种方案的原因。输入输出
描述读取策略:先做、后做、还是即时做,而且描述在哪一层检测I/O错误:字段、记录、流,或者文件的层次。错误处理
架构中应描述一种“一致地处理错误”的策略。一些需考虑的问题:容错性
详细定义所期望的容错种类。包括错误检测;如果可能从错误中恢复;如果不能从错误中恢复,则包容其不利影响。架构的可行性
论证系统的技术可行性。架构师会关注系统各种能力,例如性能目标,能否在有限资源下运转,运行环境是否有足够的支持。架构应该说明“这些问题是如何经过研究的”——通过验证概念的原型、研究、或其他手段,而且必须在全面开展构建之前解决掉这些风险。过度工程
通常架构详细描述的系统会比需求详细描述的系统更健壮。软件链条的强度不是取决于最薄弱的一环,而是等于所有薄弱环节的乘积。那么架构应该清楚指出程序员应该“为了谨慎起见宁可进行过度工程”,还是应该做出最简单的能工作的东西。
还应该明确设立期望目标,避免“某些类异常健壮,而其他类勉强够健壮”的现象。关于“买”还是“造”的决策
如果架构不采用现货供应的组件,那么就应该说明“自己定制的组件应该在哪些方面胜过线程的程序库和组件”。关于复用的决策
架构应该说明如何对复用的软件进行加工,使之符合其他架构目标。变更策略
为应对可能出现的变更,架构应足够灵活,适应可能出现的变化。这些变更来自不稳定的数据类型和文件格式、功能需求的变更、新的功能特性等。
架构应清楚描述处理变更的策略。列出可能出现变更的功能。说明变更已经被预料到了,并且任何单一的变更都智慧影响少数几个类。
“延迟提交”策略。比如使用表驱动技术,也许表中的数据是保存在外部文件中,而非直接写在代码中,这样就能做到不重新编译的情况下修改程序。架构的总体质量
大型系统本质问题是维持其“概念完整性”。
一个好的架构定义应该要关注上述内容,并将上述内容描述清楚,为设计和实现阶段铺好一个框架。还有一个架构核对表,优秀的架构应该关注这些问题。
前期准备的时间花费应如何安排?
一般而言,花费在问题定义、需求分析、软件架构上的时间,一句项目的需要而变化,一个运作良好的项目会在前期计划方面投入10%20%的工作量和20%30%的时间。这不包含详细设计的时间,因为详细设计是构建活动的一部分。
对于需求不稳定,如果是大型的正式项目,需要与需求分析师合作,解决需求分析的问题,这样你就需要为与需求分析师协商预留一定时间。
如果是小型非正式的项目,那你可能需要自己解决需求分析问题,要预留时间将需求定义足够清晰,将需求的不稳定性对构建活动的负面影响降至最低。
如果软件是你以前没有做过的类型,应当为“在新的领域中做设计”的不确定性预留更多时间。目标是创建良好的架构,所以预估时间后,不要被“为做好其他方面工作所需要的时间”所挤占。
如果有必要,可以把需求和架构工作当做独立的项目来对待。
相关好书推荐
需求类:
- Wiegers, Karl.《Software Requirements》 这是一本实用的、面向从业者的书籍,它描述了“需求”活动的具体细节,包括需求启发、需求分析、需求规格、需求验证、需求管理等;
- 更高阶的“需求”从业人员的书:Robertson, Suzanne and James Robertson. 《Mastering the Requirements Process》。Wiegers书的很好替代品。
软件架构类:
- 原文版 Bass, Len, Paul Clements, and Rick Kazman. 《Software Architecture in Practice》,中译版《软件架构实践》,清华大学出版社。
- Buschman, Frank, et al. 《Pattern-Oriented Architecture, Volumn 1: A System of Patterns》,中译版《面向模式的软件体系架构 卷1:模式系统》,机械工业出版社。
- Clements, Paul, Rick Kazman, and Mark Klein. 《Evaluating Software Architectures: Methods and Case Studies》,中译版《软件架构评估》,清华大学出版社。