《构建之法:现代软件工程》笔记
第一章 概论
程序 = 数据结构 + 算法,软件 = 程序 + 软件工程
软件企业= 软件 + 商业模式
软件工程是什么?
软件工程是把系统的、有序的、可量化的方法应用到软件的开发运营和维护的过程。
软件工程包括下列领域:软件需求分析、软件设计、软件构建、软件测试和维护。
软件工程和下列学科相关:计算机科学、计算机工程、管理学、数学、项目管理学、质量管理、软件人体工学、系统工程、工业设计 和用户界面设计。
人们在开发、运营、维护软件的过程中有很多技术、做法、习惯和思想体系。软件工程把这些相关的技术和过程统一到一个体系中,叫“软件开发流程”。软件开发流程的目的是为了提高软件开发、运营、维护的效率,并提高软件的质量、用户满意度、可靠性和软件的可维护性。
软件开发难题(5点)
复杂性。
软件的各个模块之间有各种显性或者隐性的依赖关系,随着系统的成长和规模的增多,这些关系的数量往往以几何级数的增长。不可见性。
商业软件出现错误,工程师可以看到出错的痕迹(错误代号、大致的目标代码位置、错误信息等),但是几乎无法完整重现程序到底出了什么问题。易变性。
a) 让软件做新的事情;
b) 让软件适应新的硬件。
与此同时,正确地修改软件是一件很困难的事情。服从性。
软件不能独立存在,总是要运行在硬件上,它要服从系统中其它组件的要求,服从用户要求,服从行业系统的要求(例如银行利率的变化)。非连续性。
人们比较容易理解连续的系统:增加输入,就能看到相应的输出增加。但是很多软件系统却没有这样的特性,有时输入上很小的变化,会引起输出上极大的变化。
软件工程的目标 —— 创造足够好的软件 (3点)
研发出符合用户需求的软件。
通过一定的软件流程,在预计的时间内发布“足够好”的软件。
能证明所开发的软件是可维护和继续发展的。
能做到这三点,就是初步学会了软件工程。
第2章 个人技术和流程
知识点:单元测试、回归测试、效能分析、个人团结开发流程(PSP, Personal Software Process)
单元测试标准:
单元测试应该在最基本的功能/参数上验证程序的正确性。
单元测试必须由最熟悉代码的人(程序作者)来写。
单元测试过后,机器状态保持不变。(删除或者恢复记录、状态)
单元测试要快(一个测试圆形时间是几秒钟,而不是几分钟)
单元测试应该产生可重复、一致的结果。
独立性 —— 单元测试的运行/通过/失败不依赖于别的测试,可以认为构造数据,以保持单元测试的独立性。
单元测试可以覆盖所有代码路径。(包括正处处理代码路径、错误处理代码路径、必须测试所有公开的和私有的函数和方法)
单元测试应该集成到自动测试框架中。
单元测试必须和产品代码一起保存和维护。
回归测试
模块或功能之前是正常的,在新的模块构建中除了问题,该模块“退步”(Regression),从稳定变成不稳定。要重新测试。
BugFix做回归测试的目的
验证新的代码的确改正了缺陷。
同时要验证新的代码有没有颇高模块的现有功能,有没有Regression(倒退,退化)。
效能分析工具
抽样 Sampling;
代码注入 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 对个人的要求。
初级软件工程师成长
积累软件开发相关的只是,提升技术能力(对具体技术的掌握,动手能力)。
积累问题领域的知识和经验(例如:对游戏、医疗或金融行业的了解)。
对通用的软件设计思想和软件工程思想的理解。
提升职业技能(区别于技术技能)。
包括:自我管理,表达和交流的能力,与人合作的能力,按质按量完成任务的执行力。实际成果。(产品用户评价,市场占有率,用户价值,个人的作用)
软件开发工作量和质量衡量
a. 项目/任务有多大?
b. 花了多少时间?
c. 质量如何?
d. 是否按时交付?
团队对个人的期望
交流;
说到做到;
接受团队赋予的角色并按角色完成工作;
全力投入团队活动;
按照团队流程的要求工作;
准备;在开会讨论前,新功能,新项目前,要做好准备工作;
理性的工作:抛开个人和感情因素,从事实和数据出发,按照流程,理性的工作;
第4章 两人合作
代码规范、极限编程、结对编程、两人合作的不同阶段,影响他人的技巧
代码规范
代码风格规范:简明,易读,无二义性。
缩进,行宽,括号,断行与空白的 {} 行,分行,命名,下划线,大小写,注释。代码设计规范。程序书写格式,程序设计、模块之间的关系,设计模式等。
代码复审 Code Review
代码复审的正确定义:看代码是否在“代码规范”的框架内正确地解决了问题。(见表4-1)
表 4-1 代码复审的形式:
名称 | 形式 | 目的 |
---|---|---|
自我复审 | 自己 vs. 自己 | 用同伴复审的标准来要求自己。 不一定最有效,因为开发者对自己总是过于自信。 如果能持之以恒,则对个人有很大的好处 |
同伴复审 | 复审者 vs. 开发者 | 简便易行 |
团队复审 | 团队 vs. 开发者 | 有比较严格的规范和流程,适用于关键的代码,以及复审后不再更新的代码。 |
软件工程中最基本的复审手段,就是同伴复审。
代码复审的目的
找出代码的错误,比如:
1) 编码错误,比如一些碰巧骗过了编译器的错误;
2) 不符合团队代码规范的地方;发现逻辑错误,程序可以编译通过,但是代码逻辑是错误的。
发现算法错误,比如使用的算法不够优化,边界条件没有处理好等。
发现潜在的错误和回归性错误——当前的修改导致以前修复的缺陷又重新出现。
发现可能需要改进的地方。
教育(互相教育)开发人员,传授经验,让更多的成员熟悉项目各部分的代码,同时熟悉和应用领域相关的实际知识。
代码复审有“教育”和“传播知识”的作用。
代码复审的步骤
在复审前——
代码必须成功地编译,在所有要求的平台上,同时要编译 Debug | Retail 版本。编译要用团队规定的最严格的编译警告等级(例如C/C++中的W4)。
程序员必须测试过代码。什么叫测试过?最好的方法是在调试器中单步执行。
程序员必须提供新的代码,以及文件差异分析工具。
在面对面复审中,一般是开发者控制流程,讲述修改的前因后果。但是复审者有权在任何时候打断叙述,提出自己的意见。
复审者必须之一提供反馈意见。注意,复审者有权提出很多看似吹毛求疵的问题,复审者不必亲自调查每一件事,开发者有义务给出详尽的回答。
开发者必须负责让所有的问题都得到满意的解释或回答,或者在TFS中创建类似的工作以确保这些问题会得到处理。
对于复审的结果,双方必须达成一致的意见。
1) 打回去——复审发现致命问题,这些问题在解决之前不能签入代码。
2) 有条件地同意——发现了一些小问题,在这些问题得到解决或者记录之后,代码可以签入,不需要再次复审。
3) 放行——代码可以不加新的改动,签入源码控制服务器。
要注意避免不必要的繁文缛节,我们做代码复查的目的是为了减少错误的发生,而不是找一个人来对着你的代码点头。一些简单的修改不是非得要一个复审者来走一遍形式。
代码复审核查表
概要部分
1) 代码符合需求和规格说明么?
2) 代码设计是否考虑周全?
3) 代码可读性如何?
4) 代码容易维护么?
5) 代码的每一行都执行并检查过了吗?设计规范部分
1) 设计是否遵从已知的设计模式或者项目中常用的模式?
2) 有没有硬编码或字符串/数字等存在?
3) 代码有没有依赖于某一平台,是否会影响将来的移植(如Win32到Win64)?
4) 开发者新写的代码能否用已有的 Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以调用而不用全部重新实现?
5) 有没有无用的代码可以清除?(很多人想保留尽可能多的代码,因为以后可能会用上,这样导致程序文件中有很多注释掉的代码,这些代码都可以删掉,因为源代码控制已经保存了原来的老代码。)代码规范部分
修改的部分符合代码标准和风格么?具体代码部分
1) 有没有对错误进行处理?
2) 参数传递有无错误,字符串的长度是自己的长度还是字符(可能是单/双字节)的长度,是以0开始计数开始以1开始计数?
3) 边界条件是如何处理的?switch 语句的 default 分支是如何处理的?循环有没有可能出现死循环?
4) 有没有使用断言(Assert)来保证我们任务不变的条件真的得到满足?
5) 对资源的利用,是在哪里申请,在哪里释放的?有无可能存在资源泄漏(内存、文件、各种GUI资源、数据库访问的连接,等等)?有没有优化的空间?
6) 数据结构中有没有用不到的元素?效能
1) 代码的效能(performance)如何?最坏的情况是怎样的?
2) 代码中,特别爽循环中是否有明显可优化的部分(C++中反复创建类,C#中 string 的操作是否能用 StringBuilder 来优化?
3) 对于系统和网络的调用是否会超时?如何处理?可读性
代码可读性如何?有没有足够的注释?可测试性
代码是否需要更新或创建新的单元测试?针对特定领域的开发(如数据库、网页、多线程等),可以整理专门的核查表。
结对编程两人合作的不同阶段和技巧1. 萌芽阶段 Forming,规格认识,拘谨而彬彬有礼。2. 磨合阶段 Storming3. 规范阶段 Norming4. 创造阶段 Performing。不是所有合作都能走到创造阶段,磨合太多,可能进入“解体阶段”。5. 解体阶段 Deforming。散伙。
如何正确的给予反馈
第5章 团队和流程
典型的软件团队模式和开发流程,优缺点;TSP,MVP,MBP,RUP;
团队特点
团队有一致的集体目标,团队要一起完成这目标。
团队成员有各自的分工,互相依赖合作,共同完成任务。
软件团队的模式
主治医师模式(Chief Programmer Team, Surgical Team):主治医师干活,其他人为主治医师服务。
明显模式(Super-star Team) :主治医师模式运用到极点。明星承担了很多工作。
社区模式(Community Model):如开发和维护Linux操作系统的社区。
业余剧团模式(Amateur Theater Team):不同点人挑不同的角色,在下一个剧目中,可能会换成其他角色。
秘密团队(Skunk Work Team):软件项目在秘密状态下进行。
特工团队(SWAT):由一些有特殊技能的专业人士负责解决棘手的有紧迫性的问题。如专门做网站安全性服务的团队。
交响乐团模式(Orchestra):门类齐全,各司其职,演奏都靠谱,同时看指挥的,演奏的都是练习多次的曲目,重在执行。软件处于问题成长时,会用这个模式,如 Office 软件。
爵士乐模式(Jazz Band):强调个性表达,强有力的互动,对变化的内容有创意的回应。有些类似“敏捷开发模式”。
功能团队模式(Feature Team):具备不同能力的同事们平等协作,共同完成一个功能。
官僚模式(Bureaucratic Model):脱胎于大型组织机构,架构组织是树形模式。
开发流程(开发模式)
团队开发时使用的开发方式和方法。
写了再改模式(Code-and-Fix):只用一次的程序,看过了就扔的原型,一些不实用的演示程序。
瀑布模型(Waterfall Model):分析 -> 设计 -> 实现(制造) -> 销售 -> 维护。
瀑布模型的变形:a) 生鱼片模型,各相邻模块像生鱼片部分重叠; b) 大瀑布带小瀑布;统一流程 (Rational Unified Process, RUP)
业务建模 -> 需求 -> 分析和设计 -> 实现 -> 测试 -> 部署 -> 配置和变更管理 -> 项目管理 -> 环境(向软件开发组织提供软件开发环境,包括过程和工具)老板驱动的流程 (Boss Driven Process)
渐进交付的流程 (Evolutionary Delivery),MVP 和 MBP
开发 -> 发布 -> 听取反馈 -> 根据反馈改进。
MVP:Minimum Viable Product 最小可行产品,又称为 Minimum Feature Set
MBP:Maximal Beautiful Product 最强最美产品
TSP 的原则(Team Software Process)
使用妥善定义的流程,流程中的每一步都是可以重复、可以衡量结果的。
团队的各个成员对团队的目标,角色,产品都有统一的理解。
尽量使用成熟的技术和做法。
尽量多地收集数据(也包括对团队不利的数据),并用数据来帮助团队做出理性的决定。
制定切合实际的计划和承诺,团队计划要由负责具体执行的角色来制定(而不是从上级而来)。
增强团队的自我管理能力。
专注于提高质量,争取在软件生命周期的早期发现问题。最有效提高质量的办法是做全面而细致的设计工作(而不是在后期匆忙修复问题)。
这些原则虽然抽象,但是每个团队对在做 Postmortem 的时候,可以对照检查,看看自己的团队在刚刚过去的软件生命周期到底提高了多少。
书籍推荐:《梦断代码》(Dreaming in Code)
第6章 敏捷流程
敏捷开发原则和流程
敏捷开发的原则
尽早并持续地交付有兼职的软件以满足顾客需求。
敏捷流程欢迎需求的变化,并利用这种变化来提高用户的竞争优势。
警察发布可用的软件,发布间隔可以从几周到几个月,能短则短。
业务人员和开发人员在项目开发过程中应每天共同工作。
以有进取心的人为项目核心,充分支持信任他们。
无论团队内外,面对面的交流始终是最有效的沟通方式。
可用的软件是衡量项目进展的主要指标。
敏捷流程应能保持可持续的发展。领导、团队和用户应该能按照目前的步调持续合作下去。
只有不断关注技术和设计,才能越来越敏捷。
保持简明 —— 尽可能简化工作量的技艺 —— 极为重要。
只有能自我管理的团队才能创造优秀的架构、需求和设计。
时时总结如何提高团队效率,并付诸行动。
敏捷步骤:
第一步,找出完成产品需要做到事情 —— Product Backlog,产品待解决的问题。
第二部,决定当前的冲刺(Sprint)需要解决的事情 —— Sprint Backlog。团队成员能主导任务的估计和分配,他们的能动性得到较大的发挥。
第三部,冲刺 Sprint。外部人士不能打扰团队成员,一切交流只能通过 Scrum 大师来完成。有任何需求改动都留待冲刺结束后再讨论。
得到软件的一个增量版本,发布给用户。然后在此基础上又进一步计划增量的新功能和改进。
敏捷的团队
敏捷对团队的要求很简单:自主管理(Self-managing)、自我组织(Self-organizing)、多功能型(Cross-functional)。
1. 自我管理:以前领导布置了任务,我们实现就可以了,现在要自己挑选任务;每次Sprint结束之后,还要总结不足,提出改进,并且自己要实施这些改进。“自主管理”不等于“没有管理”。
2. 自我组织:以前做好自己的事情就好了,安心下班。现在每个人要联合起来对项目负责,有人工作落后了还要帮助他改进,项目缺少某类资源还要自己顶上去。
3. 多功能型:以前规格说明书由PM来写,测试由测试人员来做,现在每个人都全面负责,自己搞定规格说明书,和别人沟通,同时自己搞定测试。
敏捷 Agile 是一股思潮,或者说是一种价值观,它涵盖了好几种软件开发的方法论;这些方法论优势建立在许多行之有效的最佳实践方法之上。
第7章 MSF,微软解决方案框架 Microsoft Solution Framework
第8章 需求分析
理论和知识点:软件需求的类型,利益相关者;获取用户需求的常用方法和步骤;竞争性需求分析的框架 NABCD,四象限方法;项目计划和估计的技术;
软件需求(获取步骤)
获取和引导需求 Elicitation,需求捕捉
了解和挖掘软件的利益相关者的软件需求,引导他们表达出真实的需求。根据技术发展趋势和产业的变化、社会发展的大趋势,推测用户需求。需求来自企业本身,如盈利要求。分析和定义需求 Anlysis & Specification
对从各个方面获取的需求进行规整,定义需求的内涵,从各个角度量化需求。验证需求 Validation
软件团队要跟利益相关者沟通,通过分析报告、技术原型、用户调查或演示等形式向他们验证软件团队对于这些需求的认知。在软件产品的生命周期中管理需求 Management
需求变化、技术发展、团队成员能力的提高。功能实现改变,外部合作伙伴变化。
软件需求的划分
对产品功能性的需求:产品必须首先的功能。
对产品开发过程的需求。
非功能性需求:服务质量需求,如访问耗时,同时在线用户数。
综合需求:不仅仅是单个软件模块能满足,涉及到软件系统、部门、监控等其他系统。
软件产品的利益相关者
用户:最终用户(User, End-User) ,直接使用软件的人。
顾客:客户(Customer, client) ,购买者或根据合同或规定接收软件的人。
市场分析者:代码“典型用户”的需求。
监管机构:在一些行业,软件要符合行业和政策规定。
系统/应用集成商:
软件团队:
软件工程师:
获取用户需求 —— 用户调研
常用方法:
焦点小组 Focus Group : 目标用户代表和利益相关者一起讨论需求,软件评价。
弱点:a) 讨好他们,表达能力差异,被人左右; b)
第9章 项目经理
Product Manager : 产品经理 —— 正确地做产品。涉及:产品定位、市场发展、需求分析、运营、营销、市场推广、商务合作。
核心要求:根据市场和用户需求,协调各部门资源,正确地把握产品定位和方向,解决用户痛点,持续优化产品。Project Manager : 项目经理 —— 正确地做流程。对项目负责,从立项到上线按时完成。正确协调团队内外,调配各部门资源和时间,有效进行风险管理,保证项目顺利按计划结项。
Program Manager : 微软的职位名称。微软产品团队角色分配:PM、开发、测试。PM负责除产品开发和测试之外的所有事情。产出是产品的规格说明书(需求说明)。
开发和测试搞不定(不愿做)的事情:
和客户交谈,组织用户调查,发现用户需求。
了解和比较竞争对手的产品。
怎么让软件变得可用(Usable) 、有用(Useful) 。
怎么改进团队流程。
PM 做开发和测试之外的所有事情
分类:1.功能设计PM;2.了解商业和客户端的PM;3.广泛经验与知识面、商业拓展能力;4.流程驱动;5.深入某一领域;6.与研究人员合作;
PM和风险管理:
人员风险:客户、最终用户、利益关系人、项目成员、合作伙伴。
流程风险:项目的预算、成本、需求。
技术风险:开发和测试工具,平台、安全性,发布产品的技术,与我们产品相关的技术。
环境风险:法律、法规,市场竞争环境、经济情况,技术大趋势,商业模式、自然界。