《构建之法:现代软件工程》笔记

第一章 概论

程序 = 数据结构 + 算法,软件 = 程序 + 软件工程
软件企业= 软件 + 商业模式

软件工程是什么?

  1. 软件工程是把系统的、有序的、可量化的方法应用到软件的开发运营和维护的过程。

  2. 软件工程包括下列领域:软件需求分析、软件设计、软件构建、软件测试和维护。

  3. 软件工程和下列学科相关:计算机科学、计算机工程、管理学、数学、项目管理学、质量管理、软件人体工学、系统工程、工业设计 和用户界面设计。

人们在开发、运营、维护软件的过程中有很多技术、做法、习惯和思想体系。软件工程把这些相关的技术和过程统一到一个体系中,叫“软件开发流程”。软件开发流程的目的是为了提高软件开发、运营、维护的效率,并提高软件的质量、用户满意度、可靠性和软件的可维护性。

软件开发难题(5点)

  1. 复杂性。
    软件的各个模块之间有各种显性或者隐性的依赖关系,随着系统的成长和规模的增多,这些关系的数量往往以几何级数的增长。

  2. 不可见性。
    商业软件出现错误,工程师可以看到出错的痕迹(错误代号、大致的目标代码位置、错误信息等),但是几乎无法完整重现程序到底出了什么问题。

  3. 易变性。
    a) 让软件做新的事情;
    b) 让软件适应新的硬件。
    与此同时,正确地修改软件是一件很困难的事情。

  4. 服从性。
    软件不能独立存在,总是要运行在硬件上,它要服从系统中其它组件的要求,服从用户要求,服从行业系统的要求(例如银行利率的变化)。

  5. 非连续性。
    人们比较容易理解连续的系统:增加输入,就能看到相应的输出增加。但是很多软件系统却没有这样的特性,有时输入上很小的变化,会引起输出上极大的变化。

软件工程的目标 —— 创造足够好的软件 (3点)

  1. 研发出符合用户需求的软件。

  2. 通过一定的软件流程,在预计的时间内发布“足够好”的软件。

  3. 能证明所开发的软件是可维护和继续发展的。

能做到这三点,就是初步学会了软件工程。

第2章 个人技术和流程

知识点:单元测试、回归测试、效能分析、个人团结开发流程(PSP, Personal Software Process)

单元测试标准:

  1. 单元测试应该在最基本的功能/参数上验证程序的正确性。

  2. 单元测试必须由最熟悉代码的人(程序作者)来写。

  3. 单元测试过后,机器状态保持不变。(删除或者恢复记录、状态)

  4. 单元测试要快(一个测试圆形时间是几秒钟,而不是几分钟)

  5. 单元测试应该产生可重复、一致的结果。

  6. 独立性 —— 单元测试的运行/通过/失败不依赖于别的测试,可以认为构造数据,以保持单元测试的独立性。

  7. 单元测试可以覆盖所有代码路径。(包括正处处理代码路径、错误处理代码路径、必须测试所有公开的和私有的函数和方法)

  8. 单元测试应该集成到自动测试框架中。

  9. 单元测试必须和产品代码一起保存和维护。

回归测试

模块或功能之前是正常的,在新的模块构建中除了问题,该模块“退步”(Regression),从稳定变成不稳定。要重新测试。
BugFix做回归测试的目的

  1. 验证新的代码的确改正了缺陷。

  2. 同时要验证新的代码有没有颇高模块的现有功能,有没有Regression(倒退,退化)。

效能分析工具

  1. 抽样 Sampling;

  2. 代码注入 Instrumentation;
    如果我们不经分析就盲目优化,也许会事倍功半。

个人开发流程

软件工程师的任务清单(PSP, Personal Software Process)

PSP2.1
Planning
计划
Development
- Analysis
- Design Spec
- Design Review
- Coding Standard
- Design
- Coding
- Code Review
- Test
开发
- 分析需求
- 生成设计文档
- 设计复审(和同时审核设计文档)
- 代码规范(为目前的开发指定合适的规范)
- 具体设计
- 具体编码
- 代码复审
- 测试(包括自测,修改代码,提交修改)
Record Time Spent 记录用时
Test Report 测试报告
Size Measurement 计算工作量
Postmortem 事后总结
Process Improvement Plan 提出过程改进计划

第3章 软件工程师的成长

评价软件工程师水平的主要方法、技能的反面、TSP 对个人的要求。

初级软件工程师成长

  1. 积累软件开发相关的只是,提升技术能力(对具体技术的掌握,动手能力)。

  2. 积累问题领域的知识和经验(例如:对游戏、医疗或金融行业的了解)。

  3. 对通用的软件设计思想和软件工程思想的理解。

  4. 提升职业技能(区别于技术技能)。
    包括:自我管理,表达和交流的能力,与人合作的能力,按质按量完成任务的执行力。

  5. 实际成果。(产品用户评价,市场占有率,用户价值,个人的作用)

软件开发工作量和质量衡量

a. 项目/任务有多大?
b. 花了多少时间?
c. 质量如何?
d. 是否按时交付?

团队对个人的期望

  1. 交流;

  2. 说到做到;

  3. 接受团队赋予的角色并按角色完成工作;

  4. 全力投入团队活动;

  5. 按照团队流程的要求工作;

  6. 准备;在开会讨论前,新功能,新项目前,要做好准备工作;

  7. 理性的工作:抛开个人和感情因素,从事实和数据出发,按照流程,理性的工作;

第4章 两人合作

代码规范、极限编程、结对编程、两人合作的不同阶段,影响他人的技巧

代码规范

  1. 代码风格规范:简明,易读,无二义性。
    缩进,行宽,括号,断行与空白的 {} 行,分行,命名,下划线,大小写,注释。

  2. 代码设计规范。程序书写格式,程序设计、模块之间的关系,设计模式等。

代码复审 Code Review

代码复审的正确定义:看代码是否在“代码规范”的框架内正确地解决了问题。(见表4-1)
表 4-1 代码复审的形式:

名称 形式 目的
自我复审 自己 vs. 自己 用同伴复审的标准来要求自己。
不一定最有效,因为开发者对自己总是过于自信。
如果能持之以恒,则对个人有很大的好处
同伴复审 复审者 vs. 开发者 简便易行
团队复审 团队 vs. 开发者 有比较严格的规范和流程,适用于关键的代码,以及复审后不再更新的代码。

软件工程中最基本的复审手段,就是同伴复审

代码复审的目的

  1. 找出代码的错误,比如:
    1) 编码错误,比如一些碰巧骗过了编译器的错误;
    2) 不符合团队代码规范的地方;

  2. 发现逻辑错误,程序可以编译通过,但是代码逻辑是错误的。

  3. 发现算法错误,比如使用的算法不够优化,边界条件没有处理好等。

  4. 发现潜在的错误和回归性错误——当前的修改导致以前修复的缺陷又重新出现。

  5. 发现可能需要改进的地方。

  6. 教育(互相教育)开发人员,传授经验,让更多的成员熟悉项目各部分的代码,同时熟悉和应用领域相关的实际知识。

代码复审有“教育”和“传播知识”的作用。

代码复审的步骤

在复审前——

  1. 代码必须成功地编译,在所有要求的平台上,同时要编译 Debug | Retail 版本。编译要用团队规定的最严格的编译警告等级(例如C/C++中的W4)。

  2. 程序员必须测试过代码。什么叫测试过?最好的方法是在调试器中单步执行。

  3. 程序员必须提供新的代码,以及文件差异分析工具。

  4. 在面对面复审中,一般是开发者控制流程,讲述修改的前因后果。但是复审者有权在任何时候打断叙述,提出自己的意见。

  5. 复审者必须之一提供反馈意见。注意,复审者有权提出很多看似吹毛求疵的问题,复审者不必亲自调查每一件事,开发者有义务给出详尽的回答。

  6. 开发者必须负责让所有的问题都得到满意的解释或回答,或者在TFS中创建类似的工作以确保这些问题会得到处理。

  7. 对于复审的结果,双方必须达成一致的意见。
    1) 打回去——复审发现致命问题,这些问题在解决之前不能签入代码。
    2) 有条件地同意——发现了一些小问题,在这些问题得到解决或者记录之后,代码可以签入,不需要再次复审。
    3) 放行——代码可以不加新的改动,签入源码控制服务器。

要注意避免不必要的繁文缛节,我们做代码复查的目的是为了减少错误的发生,而不是找一个人来对着你的代码点头。一些简单的修改不是非得要一个复审者来走一遍形式。

代码复审核查表

  1. 概要部分
    1) 代码符合需求和规格说明么?
    2) 代码设计是否考虑周全?
    3) 代码可读性如何?
    4) 代码容易维护么?
    5) 代码的每一行都执行并检查过了吗?

  2. 设计规范部分
    1) 设计是否遵从已知的设计模式或者项目中常用的模式?
    2) 有没有硬编码或字符串/数字等存在?
    3) 代码有没有依赖于某一平台,是否会影响将来的移植(如Win32到Win64)?
    4) 开发者新写的代码能否用已有的 Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以调用而不用全部重新实现?
    5) 有没有无用的代码可以清除?(很多人想保留尽可能多的代码,因为以后可能会用上,这样导致程序文件中有很多注释掉的代码,这些代码都可以删掉,因为源代码控制已经保存了原来的老代码。)

  3. 代码规范部分
    修改的部分符合代码标准和风格么?

  4. 具体代码部分
    1) 有没有对错误进行处理?
    2) 参数传递有无错误,字符串的长度是自己的长度还是字符(可能是单/双字节)的长度,是以0开始计数开始以1开始计数?
    3) 边界条件是如何处理的?switch 语句的 default 分支是如何处理的?循环有没有可能出现死循环?
    4) 有没有使用断言(Assert)来保证我们任务不变的条件真的得到满足?
    5) 对资源的利用,是在哪里申请,在哪里释放的?有无可能存在资源泄漏(内存、文件、各种GUI资源、数据库访问的连接,等等)?有没有优化的空间?
    6) 数据结构中有没有用不到的元素?

  5. 效能
    1) 代码的效能(performance)如何?最坏的情况是怎样的?
    2) 代码中,特别爽循环中是否有明显可优化的部分(C++中反复创建类,C#中 string 的操作是否能用 StringBuilder 来优化?
    3) 对于系统和网络的调用是否会超时?如何处理?

  6. 可读性
    代码可读性如何?有没有足够的注释?

  7. 可测试性
    代码是否需要更新或创建新的单元测试?针对特定领域的开发(如数据库、网页、多线程等),可以整理专门的核查表。

结对编程两人合作的不同阶段和技巧1. 萌芽阶段 Forming,规格认识,拘谨而彬彬有礼。2. 磨合阶段 Storming3. 规范阶段 Norming4. 创造阶段 Performing。不是所有合作都能走到创造阶段,磨合太多,可能进入“解体阶段”。5. 解体阶段 Deforming。散伙。

如何正确的给予反馈

第5章 团队和流程

典型的软件团队模式和开发流程,优缺点;TSP,MVP,MBP,RUP;

团队特点

  1. 团队有一致的集体目标,团队要一起完成这目标。

  2. 团队成员有各自的分工,互相依赖合作,共同完成任务。

软件团队的模式

  1. 主治医师模式(Chief Programmer Team, Surgical Team):主治医师干活,其他人为主治医师服务。

  2. 明显模式(Super-star Team) :主治医师模式运用到极点。明星承担了很多工作。

  3. 社区模式(Community Model):如开发和维护Linux操作系统的社区。

  4. 业余剧团模式(Amateur Theater Team):不同点人挑不同的角色,在下一个剧目中,可能会换成其他角色。

  5. 秘密团队(Skunk Work Team):软件项目在秘密状态下进行。

  6. 特工团队(SWAT):由一些有特殊技能的专业人士负责解决棘手的有紧迫性的问题。如专门做网站安全性服务的团队。

  7. 交响乐团模式(Orchestra):门类齐全,各司其职,演奏都靠谱,同时看指挥的,演奏的都是练习多次的曲目,重在执行。软件处于问题成长时,会用这个模式,如 Office 软件。

  8. 爵士乐模式(Jazz Band):强调个性表达,强有力的互动,对变化的内容有创意的回应。有些类似“敏捷开发模式”。

  9. 功能团队模式(Feature Team):具备不同能力的同事们平等协作,共同完成一个功能。

  10. 官僚模式(Bureaucratic Model):脱胎于大型组织机构,架构组织是树形模式。

开发流程(开发模式)

团队开发时使用的开发方式和方法。

  1. 写了再改模式(Code-and-Fix):只用一次的程序,看过了就扔的原型,一些不实用的演示程序。

  2. 瀑布模型(Waterfall Model):分析 -> 设计 -> 实现(制造) -> 销售 -> 维护。
    瀑布模型的变形:a) 生鱼片模型,各相邻模块像生鱼片部分重叠; b) 大瀑布带小瀑布;

  3. 统一流程 (Rational Unified Process, RUP)
    业务建模 -> 需求 -> 分析和设计 -> 实现 -> 测试 -> 部署 -> 配置和变更管理 -> 项目管理 -> 环境(向软件开发组织提供软件开发环境,包括过程和工具)

  4. 老板驱动的流程 (Boss Driven Process)

  5. 渐进交付的流程 (Evolutionary Delivery),MVP 和 MBP
    开发 -> 发布 -> 听取反馈 -> 根据反馈改进。
    MVP:Minimum Viable Product 最小可行产品,又称为 Minimum Feature Set
    MBP:Maximal Beautiful Product 最强最美产品

TSP 的原则(Team Software Process)

  1. 使用妥善定义的流程,流程中的每一步都是可以重复、可以衡量结果的。

  2. 团队的各个成员对团队的目标,角色,产品都有统一的理解。

  3. 尽量使用成熟的技术和做法。

  4. 尽量多地收集数据(也包括对团队不利的数据),并用数据来帮助团队做出理性的决定。

  5. 制定切合实际的计划和承诺,团队计划要由负责具体执行的角色来制定(而不是从上级而来)。

  6. 增强团队的自我管理能力。

  7. 专注于提高质量,争取在软件生命周期的早期发现问题。最有效提高质量的办法是做全面而细致的设计工作(而不是在后期匆忙修复问题)。
    这些原则虽然抽象,但是每个团队对在做 Postmortem 的时候,可以对照检查,看看自己的团队在刚刚过去的软件生命周期到底提高了多少。
    书籍推荐:《梦断代码》(Dreaming in Code)

第6章 敏捷流程

敏捷开发原则和流程

敏捷开发的原则

  1. 尽早并持续地交付有兼职的软件以满足顾客需求。

  2. 敏捷流程欢迎需求的变化,并利用这种变化来提高用户的竞争优势。

  3. 警察发布可用的软件,发布间隔可以从几周到几个月,能短则短。

  4. 业务人员和开发人员在项目开发过程中应每天共同工作。

  5. 以有进取心的人为项目核心,充分支持信任他们。

  6. 无论团队内外,面对面的交流始终是最有效的沟通方式。

  7. 可用的软件是衡量项目进展的主要指标。

  8. 敏捷流程应能保持可持续的发展。领导、团队和用户应该能按照目前的步调持续合作下去。

  9. 只有不断关注技术和设计,才能越来越敏捷。

  10. 保持简明 —— 尽可能简化工作量的技艺 —— 极为重要。

  11. 只有能自我管理的团队才能创造优秀的架构、需求和设计。

  12. 时时总结如何提高团队效率,并付诸行动。

敏捷步骤:

  1. 第一步,找出完成产品需要做到事情 —— Product Backlog,产品待解决的问题。

  2. 第二部,决定当前的冲刺(Sprint)需要解决的事情 —— Sprint Backlog。团队成员能主导任务的估计和分配,他们的能动性得到较大的发挥。

  3. 第三部,冲刺 Sprint。外部人士不能打扰团队成员,一切交流只能通过 Scrum 大师来完成。有任何需求改动都留待冲刺结束后再讨论。

  4. 得到软件的一个增量版本,发布给用户。然后在此基础上又进一步计划增量的新功能和改进。

敏捷的团队

敏捷对团队的要求很简单:自主管理(Self-managing)、自我组织(Self-organizing)、多功能型(Cross-functional)。
1. 自我管理:以前领导布置了任务,我们实现就可以了,现在要自己挑选任务;每次Sprint结束之后,还要总结不足,提出改进,并且自己要实施这些改进。“自主管理”不等于“没有管理”。
2. 自我组织:以前做好自己的事情就好了,安心下班。现在每个人要联合起来对项目负责,有人工作落后了还要帮助他改进,项目缺少某类资源还要自己顶上去。
3. 多功能型:以前规格说明书由PM来写,测试由测试人员来做,现在每个人都全面负责,自己搞定规格说明书,和别人沟通,同时自己搞定测试。

敏捷 Agile 是一股思潮,或者说是一种价值观,它涵盖了好几种软件开发的方法论;这些方法论优势建立在许多行之有效的最佳实践方法之上。

第7章 MSF,微软解决方案框架 Microsoft Solution Framework

第8章 需求分析

理论和知识点:软件需求的类型,利益相关者;获取用户需求的常用方法和步骤;竞争性需求分析的框架 NABCD,四象限方法;项目计划和估计的技术;

软件需求(获取步骤)

  1. 获取和引导需求 Elicitation,需求捕捉
    了解和挖掘软件的利益相关者的软件需求,引导他们表达出真实的需求。根据技术发展趋势和产业的变化、社会发展的大趋势,推测用户需求。需求来自企业本身,如盈利要求。

  2. 分析和定义需求 Anlysis & Specification
    对从各个方面获取的需求进行规整,定义需求的内涵,从各个角度量化需求。

  3. 验证需求 Validation
    软件团队要跟利益相关者沟通,通过分析报告、技术原型、用户调查或演示等形式向他们验证软件团队对于这些需求的认知。

  4. 在软件产品的生命周期中管理需求 Management
    需求变化、技术发展、团队成员能力的提高。功能实现改变,外部合作伙伴变化。

软件需求的划分

  1. 对产品功能性的需求:产品必须首先的功能。

  2. 对产品开发过程的需求。

  3. 非功能性需求:服务质量需求,如访问耗时,同时在线用户数。

  4. 综合需求:不仅仅是单个软件模块能满足,涉及到软件系统、部门、监控等其他系统。

软件产品的利益相关者

用户:最终用户(User, End-User) ,直接使用软件的人。
顾客:客户(Customer, client) ,购买者或根据合同或规定接收软件的人。
市场分析者:代码“典型用户”的需求。
监管机构:在一些行业,软件要符合行业和政策规定。
系统/应用集成商:
软件团队:
软件工程师:

获取用户需求 —— 用户调研

常用方法:

  1. 焦点小组 Focus Group : 目标用户代表和利益相关者一起讨论需求,软件评价。
    弱点:a) 讨好他们,表达能力差异,被人左右; b)

第9章 项目经理

  • Product Manager : 产品经理 —— 正确地做产品。涉及:产品定位、市场发展、需求分析、运营、营销、市场推广、商务合作。
    核心要求:根据市场和用户需求,协调各部门资源,正确地把握产品定位和方向,解决用户痛点,持续优化产品。

  • Project Manager : 项目经理 —— 正确地做流程。对项目负责,从立项到上线按时完成。正确协调团队内外,调配各部门资源和时间,有效进行风险管理,保证项目顺利按计划结项。

  • Program Manager : 微软的职位名称。微软产品团队角色分配:PM、开发、测试。PM负责除产品开发和测试之外的所有事情。产出是产品的规格说明书(需求说明)。

开发和测试搞不定(不愿做)的事情

  1. 和客户交谈,组织用户调查,发现用户需求。

  2. 了解和比较竞争对手的产品。

  3. 怎么让软件变得可用(Usable) 、有用(Useful) 。

  4. 怎么改进团队流程。

PM 做开发和测试之外的所有事情
分类:1.功能设计PM;2.了解商业和客户端的PM;3.广泛经验与知识面、商业拓展能力;4.流程驱动;5.深入某一领域;6.与研究人员合作;

PM和风险管理

  • 人员风险:客户、最终用户、利益关系人、项目成员、合作伙伴。

  • 流程风险:项目的预算、成本、需求。

  • 技术风险:开发和测试工具,平台、安全性,发布产品的技术,与我们产品相关的技术。

  • 环境风险:法律、法规,市场竞争环境、经济情况,技术大趋势,商业模式、自然界。

第10章 典型用户和场景

第11章 软件设计与实现

第12章 用户体验

第13章 软件测试

第14章 质量保障

第15章 稳定和发布阶段

第16章 IT行业的创新

第17章 人、绩效和职业道德