开发个人检查清单
下面的内容我实际上是抄的代码大全2检查清单的内容,因为没看到哪里有所有的检查清单的内容。所以利用OCR+AI来拿出来所有的文字,除此之外,补上了部分我自己的检查内容,斜体是我自己的内容。
1 需求
-
具体的功能需求
-
是否指定了系统的所有输入,包括其来源、精度、值的范围和出现频率?
-
是否指定了系统的所有输出,包括其目标、精度、值的范围、出现频率和格式?
-
是否为Web 页面和报表等指定了所有输出格式?
-
是否指定了所有外部硬件和软件接口?
-
是否指定了所有外部通信接口,包括握手、错误检查和通信切议?
-
是否指定了用户想要执行的所有任务?
-
是否指定了每个任务中使用的数据和每个任务产生的数据。要给出产生的数据量一个预估值。
-
- 特定的非功能性(质量)需求
- 从用户的角度来看,是否为所有必要的操作指定了预期的响应时间? 尽量避免任何跨公网,以及本地的文件拷贝
- 是否指定了其他时间考虑因素,比如处理时间、数据传输速率和系统吞吐量
- 是否指定了安全级别?
- 是否指定了可靠性,包括软件故障的后果、需要在故障中得到保护的重要信息以及错误测和恢复策略?
- 是否指定了最小机器内存和空闲磁盘空间?
- 是否指定了系统的可维护性,包括适应特定功能的更改、操作环境的更改以及与其他软件接口的更改的能力?
- 是否包括了成功和失败的定义?
- 需求质量
- 需求是用用户的语言编写的么?用户这么认为么?
- 每个需求都避免了与其他需求的冲突么
- 是否详细说明了竞争属性之间的可接受的这种,例如健壮性和正确性的折中
- 是否避免了在需求中规定设计
- 需求在详细程度上是一致的么,是否有需求需要更详细的说明?是否有需求不需要那么详细的说明
- 需求是否足够清晰,以至于可以移交给一个独立的团队进行构建,且不会产生误解?开发人员这么认为么?
- 每个条款都与待解决的问题及其解决方案相关么?能从每个条款上溯到在问题域中对应的根源么
- 每个需求都是可测试的么?是否有可能通过独立测试来确定每个需求都被满足了
- 是否说明了需求的所有可能变更以及每种变更的可能性
- 需求合理性考察,很多需求实际上是非常不清楚或者不合理地,简单补充一个点。如果要检查一个文件是否正常合理,那么文件应该在它生成的过程当中就测试。而不是滞后
- 需要考虑需求的优先级,需要区分出来哪些是必须完成的,哪些是可能完成的。很多情况下不重要的就后推,只考虑重要的需求。简单来说也是需求的完整性
- 需求的完整性
- 对于在开发中无法获得的信息,是否详细描述了信息不完整的区域
- 需求的完备程度是否可以达到这种高度:如果产品满足了所有需求,就说明它是可以接受的
- 对全部需求都感到满意么?是否已经去掉了那些不可能实现的需求,那些只是为了安抚客户和老板的东西
2 架构
- 针对各架构主题
- 顶目的整体组织是否清晰,包括良好的架构概述及其理由
- 主要构件是否定义良好,包括它们的职贡范围以及它们与其他构件的接口
- 在需求中列出的所有功能都被合理覆盖了吗?
- 最关键的类是否有描述和论证?
- 数据设计是否有描述和论证?
- 是否说明了数据库的组织和内容?
- 是否确定了所有关键业务规则及其对系统的影响?
- 是否描述了用户界面设计的策略?
- 用户界面是模块化的,因此它的更改不会影响程序的其余部分吗?
- 是否描述井论证了处理IO的策略?
- 是否估算了稀缺资源(如线程、数据库连接、句糖和网络带宽等)的使用量,是否描述并论证了资源管理的策略?
- 是否描述了架构的安全需求”
- 架构是否为每个类、每个子系统或每个功能域提出时间和空间预算?
- 架构是否描述了如何实现可伸缩性?
- 架构是否关注了互操作性?
- 是否描述了国际化和本地化的策略?
- 是否提供了一套一致性的错误处理策略
- 是否提供了容错的方法(如果需要的话)
- 是否证实了系统各部分的技术可行性,要求系统各个部分能认识到技术难点是比较困难的
- 是否详细描述了过度工程的方法?
- 是否包舍了必要的购买还是构律的决策?
- 架构是否播述了如何加工被复用的代码,使之待合其他架构目际;
- 架构的设计是否能够适应极有可能出现的变更?
- 架构的总体质量
- 架构是否解决了全部需求?
- 有没有哪个部分是过度架构或欠架构?是否明确提出了这方面的具体目标
- 整个架构是否在概念上协调一致?这个问题是可以大大拓展的,架构不是只针对代码,对所有的都可以
- 顶层设计是否独立于用于实现它的机器和语言
- 是否提供了所有主要决策的动机?
- 作为一个将要实现系统的程序员,你是否对架构感到满意”
3 前期准备
- 你是否已经确定了你正在从事的软件项目的类型,并适当调整了你的方法
- 需求是否有充分明确的定义,并且足够稳定,可以开始构建(详见需求检查)
- 架构是否有充分明确的定义,以便开始构建(详见架构检查清单)
- 你的特定顶目所特有的其他风随是否得到了解决,从而使构建不会被暴露在不必要的风险中?
4 重要的构建实践
- 编码
- 是否定义了有多少设计工作要预先完成,以及多少工作再编码的同时完成
- 是否为名称、注释和代码格式等制定了编码约定?
- 是否定义了架构所隐含的特定编码实践?例如,如何处理错误条件: 如何处理安全性?类接口使用什么约定?对重用代码应用什么标准?在编码时对性能要考虑到什么程度?
- 是否已经确定了自己在科技浪潮中所处的阶段,井调整了自己的方法来?如果有此要,是否已经确定了如何深入语言来编程,而不是受限于只会用语言来绵程
- 团队工作
- 是否定义了集成工序,即是否定义了程序员在将代码登入主干之前必须经过的特定步骤? 简单来说就是对应的test和集成测试需要提前完成
- 程序员是结对编程还是独立编程,还是采用两者的组合?
5 软件构建中的设计
- 设计实践
- 是否已做过迭代,从多个结果中选择了最佳的一种,而不是简单地选择首次尝试的结果? 分布式的时代需要考虑性能,可靠性,数据量,是否可管理等多方面
- 尝试过以多种方式分解系统以确定哪种最好吗?
- 同时采用了自上而下和自下而上的方法来解决设计回题吗?
- 针对系统中有风险或者不熟悉的部分进行过原型设计,写数量最小的抛弃型代码来回答特定回题吗
- 自己的设计方案被其他人评审过吗?无论正式与否
- 一直在推动设计,直至实现细节昭然若揭吗
- 使用某种适当的技术(例如Wiki、电子邮件、挂图、数码照片、UML、CRC卡片或者代码中内嵌的注释)来记录设计了吗?
- 设计目标
- 设计是否充分解决了在整体架构层面确定并决定推迟实现的问题?
- 设计是否分层的?
- 对于将程序分解为子系统、包和类的方式是否感到满意
- 对于将类分解为子程序的方式是否感到满意?
- 类的设计是否使它们之间的交互最小化?
- 类和子系统的设计是否方便你在其他系统中重用
- 程序是否容易维护?程序的维护不是单纯地开发,针对出了问题快速定位原因及时暴露错误码也是是否容易维护的判断标准之一。程序内部采用快速失败是一个比较好的策略,如果出错了还继续执行可能造成无法理解的后果
- 设计是否精简?它的所有部分都是绝对必要的吗?
- 设计是否使用了标准技术来避免奇特的、难以理解的元素?
- 总的来说,这个设计是否有助于将偶然和本质的复杂性降至最低?
6 类的质量
- 抽象数据类型
- 是否将程序中的类视为抽象数据类型,是否从这个角度评估了他们的接口?
- 抽象
- 类是否有一个中心目的
- 类的命名是否恰当?其名字是否表达了其中心目的?
- 类的接口是否呈现了一致的抽象?
- 类的接口是否让人一眼就知道应该如何使用这个类?
- 类的接口是否足够抽象,使开发者无需考虑它的服务具体是如何实现的能将类看作是一个黑盒吗?
- 类的服务是否足够完整,使其他类无需摆弄其内部数据?
- 是否已经从类中移除了无关信息?
- 是否考虑过将类进一步分解为组件类?是否已经尽可能地进行了分解?
- 修改类时是否保持了其接口的完整性?
- 封装
- 是否最小化了类成员的可访问性?
- 类是否避免了公开其成员数据?
- 在编程语言允许的范围内,类是否尽可能地隐藏了具体实现细节?
- 类的设计是否避免了对其用户(包括其派生类)做出假设?这是一个很有趣的点,如果企鹅能够继承鸟,那么用户一般会假设鸟会飞,但是很明显企鹅不会飞。所以实际上如果真正实现的话,建议是拆解部分非通用的属性不在基类
- 类是否不依赖于其他类?是否松耦合?
- 继承
- 继承是否只用来建立”is-a”关系?即派生类是否遵循了里氏替换原则(lsp)而非仅仅为了重用代码?
- 类的文档是否描述了其继承策略?
- 派生类是否免除了”覆盖不可覆盖的函数”的限制?
- 是否足够清晰地指定了派生类如何重载、扩展和覆盖基类的行为,并将待继承的的通用接口数据放在了继承树上层?
- 继承树是否很浅?
- 是否将基类中的所有成员都定义为私有或受保护的?
- 与实现相关的其它问题
- 类的数据成员是否只有七个或更少?
- 是否将类中直接调用其他类的子程序的数量减到最少了?
- 类是否只在绝对必要时才与其他类协作?
- 是否所有数据成员都在构造函数中进行了初始化?
- 是否经过论证,类被设计为深拷贝而不是浅拷贝来使用?
- 语言特定问题,针对您所使用的编程语言,是否研究过语言特定的和与类相关的问题?
7 高质量的子程序
- 主要问题
- 是否有充分的理由来创建该子程序? 创建子程序的理由应该是不同功能的拆分,和代码复用
- 在该子程序中,哪些部分更适合抽取出来作为独立的子过程或函数?
- 关于该子程序的命名,是否使用了清晰的、强有力的动词加上对其操作对象的描述作为过程的名称,或者使用了对返回值的描述作为函数的名称?
- 该子程序的名称是否准确地描述了它所做的一切事情?
- 是否为一些常用操作建立了命名规范?
- 该子程序是否具有高内聚性,即只做一件事,并且做得很好?
- 该子程序是否具有松散的耦合性?该子程序与其他子程序的关联是否简单、专用、可见和灵活?
- 该子程序的长度是否是由它的功能和逻辑自然决定的,而不是由人为制定的编码标准决定的?这个可能有点反直觉,很多情况下会有规定程序不能太长
- 参数传递问题
- 该子程序的参数列表作为一个整体是否呈现了一致的接口抽象?
- 该子程序的参数是否按合理顺序进行排列,与其他类似子程序的参数顺序一致?
- 是否对接口假设进行了文档记录?
- 该子程序的参数数量是否少于或等于7个?
- 在该子程序中是否使用了每个输入参数?
- 在该子程序中是否使用了每个输出参数?
- 是否避免将输入参数作为工作变量使用?这里面有个问题,有的时候我们就是要把这个做工作变量,不过理论上应该是最小量的更新,获取之类的
- 如果该子程序是一个函数,在所有可能的情况下是否都返回了一个有意义的值?
8 防御式编程
- 一般事宜
- 子程序否可以保护自己免受输入参数的破坏?
- 是否使用断言来声明假设,包括前置条件和后置条件?
- 断言是否只用于声明永远不应该发生的情况?
- 是否在架构或高层级设计中指定了一组特定的错误处理技术?尽管有点反直觉,目前部分C++代码规范是反对使用异常的,很多函数有对应的不抛异常版本
- 是否在架构或高层级设计中规定了错误处理应该偏向于健壮性还是正确性?健壮性会带来时间和试错的成本,正确性的最简单选择就是快速失败
- 是否建立了防护屏障来防止可能造成破坏的错误,并减少与错误处理有关的代码数量?比方说一个json解析失败不应该导致整个服务器崩溃。这也是为什么微服务会兴起的原因
- 代码中是否使用了辅助调试代码?
- 如果要启用和停用辅助调试代码,是否不需要大动干戈?
- 防御式编程引入的代码的数量是否合适,既不太多也不太少?
- 在开发阶段是否使用攻击式编程技术使错误很难被忽视?这个是指混沌工程主动显示错误是吧?
- 异常
- 在项目中是否定义了一种标准化的异常处理方法?
- 是否考虑过异常之外的其他替代方案?
- 错误是否已尽可能在局部处理,而不是作为异常向外抛出?
- 代码中是否避免在构造函数和析构函数中抛出异常?
- 是否所有的异常都与抛出它们的子程序处于同一抽象层次上?
- 每个异常是否包含了关于异常发生的所有背景信息?
- 代码中是否没有使用空的catch块?或者,如果使用空的catch块,是否很合适?
- 安全事宜
- 检查错误输入数据的代码是否也检查了缓冲区溢出、SQL注入、HTML注入、整数溢出和其他恶意输入?
- 是否检查了所有错误返回码?
- 是否捕获了所有的异常?
- 出错消息中是否避免了出现有助于攻击者攻入系统的信息?心脏滴血漏洞实际上还不能说是出错信息,而是泄漏了过多信息
9 伪代码编程过程
- 确认已满足所有先决条件了吗?
- 定义好要解决的问题了吗?
- 概要设计足够清晰,能为类及其每个子程序起一个好的名字吗?
- 考虑过应该如何测试类及其每个子程序吗?
- 主要是从可靠的接口和可读性好的实现,还是从满足资源和速度预算的效率考虑?一般是初期考虑可读性,后期专人负责优化和设计
- 在标准库或其他代码库中找过可用的子程序或组件了吗?
- 在参考书中查找过有用的算法了吗?
- 采用详细的伪代码去设计了每一个子程序吗?
- 已经在心头检查过伪代码吗?这些伪代码容易理解吗?
- 关注过那些可能会让自己重返设计的警告信息了吗?例如,关于全局变量的使用以及一些似乎更适合放在另一个类或子程序中的操作等?
- 将伪代码准确转换成实际代码了吗?
- 以递归方式运用编程过程,并根据需要将一些子程序拆分成更小的子程序了吗?
- 在做出假设时对它们进行说明了吗?
- 删除多余注释了吗?
- 是否从几次迭代中选择了效果最好的,而不是在第一次迭代之后就停止尝试?
- 是否完全理解了自己写的代码?它们是否容易理解?
10 数据使用中的常规注意事项
- 初始化变量
- 是否一个子程序都检查输入参数的合法性吗?
- 每个子程序是否检查输入参数的有效性?
- 代码是否在首次使用变量的地方声明变量?
- 如果可能的话,代码是否在声明变量时对其进行初始化?
- 如果不能同时声明和初始化变量,代码是否在靠近首次使用的地方初始化变量?
- 是否正确初始化了计数器和累加器?如有必要,是否在每次使用时都重新初始化?
- 在重复执行的代码中,变量的重新初始化是否正确?
- 代码编译时,编译器是否发出警告?启用了所有可用的警告吗?
- 如果所用的语言允许隐式声明,是否为由此引发的问题做好了补偿措施?
- 使用数据的其他事项
- 所有变量都有最小的作用域吗?
- 对变量的引用都尽可能集中在一起吗?对同一变量的两次相邻引用以及变量的整个生命周期,是否都这样做了?
- 控制结构是否与数据类型相对应?
- 是否使用了所有已声明的变量?这种可以用编译器的设定来解决
- 变量都是在适当时同绑定的吗?也就是说,是否有意在后期绑定所带来的灵活性与增加的复杂度之间做出了平衡?
- 每个变量是否都有且只有一个用途?
- 每个变量的名称都很明确且没有隐含含义吗?
11 变量命名
- 命名的常规注意事项
- 名称是否完整且准确地描述了变量所代表的内容?举一个简单的例子,配置kubernetes comfigmap的对象,不应该叫做kubeconfig
- 名称是指现实世界的问题,而不是编程语言解决方案吗?
- 名称是否长到没有必要去猜测它的含义?
- 如果有计算值限定符,是否把它放在了命名的末尾?
- 名称是否使用了Count或Index而不是Num?
- 特定类型数据的命名
- 循环索引的命名是否有意义(如循环超过一两行或是嵌套的,则不应是i、j或k)?
- 所有“临时”变量是否被重新命名为更有意义的名字?
- 当布尔变量的值为真时,变量名能准确表达其含义吗?
- 枚举类型的名称是否包含一个表示其类别的前缀或后缀,比如用于Color Red、Color Green、Color Blue等?
- 具名常量是以其所代表的抽象实体而不是所指代的数字来命名的吗?
- 命名规范
- 规范是否对局部数据、类数据和全局数据进行了区分?
- 规范是否对类型名、只名常量、枚举类型和变量进行了区分?
- 在不强制命名只读输入参数的情况下,是否明确标识了子程序中的只读参数?
- 规范是否能够兼容于语言的标准规范?
- 名称的格式是否便于阅读?
- 短名称
- 代码是否使用了长的名称(除非有必要用短的名称)?
- 代码是否避免了使用只节省一个字符的缩写?
- 所有单词的缩写是否一致?
- 名称是否可读?
- 是否避免了可能误读或发生错误的名称?
- 短的名称是否记录在了缩写对照表中?
- 常见的命名问题: 是否避免了使用…
- …是否避免了使用误导性的名称?
- …是否避免了使用含义相似的名称?
- …是否只有一两个字符不同的名称?
- …是否避免了使用发音相似的名称?
- …是否避免了使用包含数字的名称?
- …是否避免了故意拼错名称为了更短?
- …是否避免了英语中经常拼错的名称?
- …是否避免了与标准库子程序名或预定义变量名冲突的名称?
- …是否避免了过于随意的名称?
- …是否避免了包含容易混淆字符的名称?
12 基本数据类型
- 一般的数字
- 代码是否避免了魔法数字?
- 代码是否考虑了除零错误?
- 类型转换是否明显?
- 是否按照期望进行计算?
- 代码是否避免了混合类型的比较?
- 程序编译时是否没有警告?
- 整型
- 使用整数除法的表达式是否按预期工作?
- 整数表达式是否避免了整数溢出问题?
- 浮点型
- 代码是否避免对大小差别很大的数字进行加减运算?
- 代码是否系统地防止了舍入错误?
- 代码是否避免了对浮点数做等价比较?
- 字符和字符串
- 代码是否避免了魔法字符和魔法字符串?
- 使用字符串时是否避免了差一错误?
- C语言的代码是否区别对待了字符串指针和字符数组?
- C语言的代码是否在适当的时候使用字符数组而不是指针?
- 枚举类型
- 程序是否使用枚举类型而非具名敞亮来提高可读性,可靠性和可修改性
- 使用枚举类型的判断是否能检测出无效值?
- 枚举类型的第一个元素是否保留为“无效值”?
- 具名常量
- 程序是否将具名常量而非魔法数字用于数据声明和循环控制?
- 具名常量的使用是否一致,是否避免在某些地方使用具名常量而在其他地方使用字面量?
- 数组
- 所有的数组索引是否都在数组的边界范围内?
- 数组引用是否避免了差一错误?
- 多维数组的所有索引顺序是否正确?
- 在嵌套循环中,是否使用正确的变量作为数组索引,以避免循环索引混淆?
- 创建类型
- 程序是否为每一种可能变化的数据使用不同的类型?
- 类型名称是否以类型所代表的现实世界实体为导向,而不是以编程语言类型为导向?
- 类型名是否具有足够的描述性,可以帮助解释数据声明?
- 是否避免了重新定义预定义类型?
- 是否考虑过创建一个新的类而不是简单地重新定义一个类型?
13 不常见数据类型的注意事项
- 结构体
- 是否适当使用了结构体而不是单纯的变量来组织和保持相关的数据?
- 是否考虑过使用类来替代结构体?
- 全局数据
- 除非绝对有必要,否则所有变量都应该具有局部作用域或类作用域。
- 变量命名规范是否区分了局部数据、类数据和全局数据?
- 是否为所有全局变量提供了文档说明?
- 代码中是否没有伪全局数据(pseudoglobal data),即不要包含将杂乱的数据传递给每个子程序的数据的巨大对象
- 是否使用了访问器子程序替代全局数据?
- 访问器子程序和数据是否组织在类中?
- 访问器子程序是否提供了一个在底层数据类型实现之上的抽象层?
- 所有相关的访问器子程序是否处于同一抽象层?
- 指针
- 指针操作是否隔离在子程序中?
- 指针引用是否有效?或者指针是否有可能成为野指针?
- 在使用指针之前是否检查了其有效性?
- 在使用指针引用的变量之前是否检查了其有效性?
- 指针释放后是否被设置为空?
- 为了提高可读性,代码是否使用了所有必需的指针变量?
- 链表中的指针是否按照正确的顺序进行释放?
- 程序是否分配了一块紧急备用内存,以便在内存耗尽时可以优雅地退出?
- 是否在没有其他方法可用的情况下才使用指针?
14 组织直线型代码
- 代码能明确语句之间的依赖关系吗?
- 子程序的名称能明确表达依赖关系吗?
- 子程序的参数能明确表达依赖关系吗?
- 是否使用注释描述了不够明确的依赖关系?
- 是否使用内部处理变量来验证关键代码中的顺序依赖关系?
- 代码能够自上而下流畅地阅读吗?
- 相关语句是否进行了分组?
- 是否将相对独立的语句组转化为独立的子程序?
15 使用条件语句
- if-then语句
- 代码中的正常路径清晰吗?
- if-then基于相等性测试的正确分支了吗?
- else子句是否使用并添加了注释?
- else子句用得正确吗?
- 是否正确使用了if和else子句?它们有没有被用反了?
- 正常情况是跟在if后面而非else后面吗?
- if-then-else-if链
- 复杂测试封装到布尔函数调用中了吗?
- 最先测试的是最常见的情况吗?
- 是否覆盖了所有情况?
- if-then-else-if链是最佳实现方式吗?是否比case语句更好?
- case语句
- 所有case都按有意义的方式排列吗?
- 每个case的动作都简单吗?必要时是否调用了其他子程序?
- case语句判断的是一个真实变量,而非只为滥用case语句而虚构的变量吗?
- default子句用得正当吗?
- default子句是用来检测和报告出乎预料的情况吗?
- 在C语言、C++语言或Java语言中,每个case的结尾处都有break吗?
16 循环
- 循环的选择和创建
- 是否在合适的时候采用while循环取代其他循环了吗?
- 循环是由内向外创建的吗?
- 进入循环
- 是否从顶部进入循环?
- 初始化代码是否直接放在循环前面了吗?
- 如果是无限循环或事件循环,其结构是否清晰,而不是采用类似于”while True”这样简单的代码?
- 如果循环属于C/C++或者Java的for循环,循环控制代码是否都放在循环头部了吗?
- 循环内部
- 是否使用括号或其等价形式来封闭循环体以免修改不当而出错?
- 循环体里面有内容吗?它是非空的吗?
- 内务处理代码是否集中存放在循环开始或者循环结束的位置了吗?
- 循环是否就像定义良好的子程序那样只执行一种功能?
- 循环是否短到足以让人一目了然?
- 循环的嵌套层数控制在三层以内吗?
- 长循环的内容是否转移到相应的子程序中了吗?
- 如果循环很长,是不是特别清晰?
- 循环索引
- For循环体内的代码有没有随意改动循环索引值?
- 是否专门用变量保存重要的循环索引值,而不是在循环体外部使用循环索引?
- 循环索引是否是整数类型或者枚举类型,而不是浮点类型?
- 循环索引的名称有意义吗?
- 循环是否避免了索引串扰问题?
- 退出循环
- 循环是否在所有情况下可以终止
- 循环内是否使用安全计数器
- 循环的终止条件是否显而易见
17 不常见控制结构
- return
- 每个子程序是否都只在必要的时候才使用return?
- return 是否增强了可读性?
- 递归
- 递归子程序是否包含用于终止递归的代码?
- 子程序是否使用安全计数器保证自己终止?
- 递归是否只限于一个子程序(没有循环递归)?
- 子程序递归深度是否在栈的大小限制范围内?
- 递归是实现子程序最好的方式吗?比简单的迭代好?
- goto
- goto 是否只是用作最后的紧急手段,还是为了增强代码的可读性和可维护性?
- 如果为了效率而使用goto,是否对效率的提升进行了度量和注释?
- 每个子程序的goto 是否只限于使用一个标签?
- 所有的goto 是否都是向前的而不是向后的?
- 所有的goto 标签都用到了吗?
18 表驱动法
-
是否考虑过将表驱动法作为复杂逻辑的替代方案?
- 是否考虑过将表驱动法作为复杂继承结构的替代方案?
- 是否考虑过将表数据存储在程序外部,并在运行期间读取,以实现在不修改代码的前提下进行修改?
- 如果无法采用直接的数组索引来访问表(例如像age例子那样),是否考虑提取键的计算功能为一个单独的子程序,而不是在代码中复制这些计算?
19 控制结构
大部分都是语言层面的错误
20 质量保证计划
- 是否已经确定了对项目至关重要的具体质量特性?
- 是否让其他人员了解项目的质量目标?
- 是否区分了外部和内部质量特征?
- 是否考虑了某些特性之间相互制约或促进的具体方式?最简单的考虑就是CAP了
- 是否要求针对不同错误类型使用不同的错误检测技术?
- 质量保证计划中是否有步骤来保证软件在开发各阶段的质量?
- 是否以某种方式度量质量,以便能够判断质量是在改进还是降低?
- 管理层是否理解质量保证在前期会消耗额外成本,但目的是在项目后期降低成本?
21 有效的结对编程
- 是否有编码规范,以便结对编程人员能够专注于编程,而不是编程风格的纠缠
- 结对双方是否都积极参与?
- 是否避免了对所有内容进行结对编程,而足选择真正能够受益于结对编程的任务?
- 是否定期对人员和工作任务进行轮换?
- 两人在速度和个性方面是否匹配良好?
- 是否有组长担任联络人来负责与项目外部管理层和其他人员沟通?
22 有效的审查
- 是否有清单可以使审查人的注意力集中于过去有问题的领域?
- 是否将审查侧重于找出缺陷,而不是去修正它们?
- 是否考虑过为审查人分配特定的视角或场景,以帮助他们在做准备工作时加强专注?
- 审查人在审查会议前是否有充足的时间进行准备,并且每个人都做好了准备?
- 每个参与者是否都扮演了明确的角色:主持人、审查人和记录员等?
- 会议是否以高效的速度进行?
- 会议时间是否限制在两小时以内?
- 所有审查参与者是否都接受过有针对性的审查培训,以及主持人是否接受过专门的主持技能培训?
- 每次审查时是否收集有关错误类型的数据,以便为组织量身定制未来的改进清单?
- 是否收集了有关准备速度和审查速度的数据,以便优化未来的准备和审查工作?
- 每次审查是否分配了行动项,由主持人亲自跟进或者安排一次重新审查进行跟进?
- 管理层是否理解自己不应该参加审查会议?
- 是否有跟进计划以确保问题已经得到正确修复?
23 测试用例
- 是否用手头所有的测试用例保证了每个部分有足够的测试覆盖吗?包括函数覆盖率,runtime覆盖率和需求满足情况
- 类和子函数的设计中每个元素都有各自所对应的测试用例吗?
- 每行代码至少用一个测试用例进行了测试吗? 是否考虑了计算测试每行代码所需的最小测试用例数量来进行验证?
- 是否用至少一个测试用例对每条“已定义-已使用”的数据流路径进行了测试?
- 是否检查了代码中不太可能正确的数据流模式,例如”已定义-已定义”和”已定义-未撤销”?
- 在编写测试用例时是否参考了一个常见错误的列表来检测过去经常出现的错误?
- 是否测试了所有的简单边界条件:最大值、最小值和差一边界?
- 是否测试了所有的复合边界条件,即多个输入数据的组合可能导致计算得出的变量过小或过大?
- 测试用例是否检查了错误的数据类型,例如,薪资管理程序中将员工数量设为负数?
- 是否测试了有代表性的、普遍性的取值?
- 是否测试了最小正常配置?
- 是否测试了最大正常配置?
- 是否对旧数据进行了兼容性测试?是否针对旧的硬件、旧版本的操作系统以及与旧版本的其他软件的接口进行了测试?
- 该测试用例是否易于进行手工查看?
24 调试要点
- 发现缺陷的技术
- 使用所有可用的数据来创建假设
- 精炼产生错误的测试用例
- 在单元测试套件中运行代码
- 使用可用的工具
- 使用不同的方法重现错误
- 生成更多的数据以产生更多的假设
- 利用负面测试的结果
- 头脑风暴找出可能的假设
- 在桌子旁边放一个记事本,把要尝试的事情都列出来
- 缩小可疑的代码区域
- 以前有缺陷的类和子程序值得怀疑
- 检查最近修改的代码
- 扩大可疑的代码区域
- 逐步集成
- 检查常见的缺陷
- 向某人讲述发现的问题
- 把问题放一放,先休息一下
- 为快而糙的调试设置最大时间
- 整理一个暴力技术的清单,并且使用它们
- 语法错误的技术
- 不要相信编译器消息中的行号
- 不要相信编译器消息
- 不要相信编译器的第二条消息
- 分而治之
- 使用语法制导编辑器发现错误的注释和引号
- 修复缺陷的技术
- 在复现前先理解问题
- 理解程序,而不仅仅是问题
- 确认缺陷诊断结论
- 放松
- 保存原始代码
- 修复问题,而不是症状
- 仅在有充分理由的情况下修改代码
- 一次只做一处修改
- 检查修复的程序
- 添加一个暴露缺陷的单元测试
- 寻找类似的缺陷
- 常用的调试方法
- 你是否将调试作为了解程序、错误、代码质量和解决问题方法的机会?
- 你是否避免了试错、迷信的调试方法?
- 你是否假设错误是你犯错导致的?
- 你是否使用科学的方法来复现间歇性错误?
- 你是否使用科学的方法来发现缺陷?
- 你不是每都使用相同的方法,而是使用几种不同的技术来发现缺陷吗?
- 你是否验证了修复的正确性?
- 你是否使用了编译器告警消息
25 重构的理由
- 代码发生重复
- 子程序太长
- 循环太长或嵌套太深
- 类的内聚力很差
- 类的接口不能提供一致的抽象层级
- 参数表有太多参数
- 在类中进行的修改各自独立
- 需要平行修改多个类
- 需要平行修改继承层级结构
- 需要平行修改 case 语句
- 一起使用的相关数据项没有被组织成类
- 一个子程序使用了另一个类(而非它自己的类)更多的特性
- 无脑使用基本数据类型
- 类的作用不大
- 子程序间传递流浪数据
- 一个中间对象没有做任何事情
- 一个类与另一个类过于亲密
- 某个子程序的名字太差劲
- 公共数据成员
- 一个子类只使用了其父类的一小部分功能
- 用注释解释难以理解的代码
- 全局变量的使用
- 程序包舍的代码似乎有一天会被需要
26 重构总结
- 数据级重构
- 用具名常量替换神秘数字。
- 用更清晰或更有信息量的名字重命名变量
- 使表达式内联。
- 用子程序替代表达式
- 引入中间变量。
- 将一个多用途的变量转换为多个单用途的变量,
- 局部用途的就用局部变量,而不要用参数
- 将数据基元转换为类
- 将一组类型代码转换为类或枚举,
- 将一组类型代码转换为带有子类的类,
- 将数组改为对象、
- 封装集合
- 用数据类替代传统记录
- 语句级重构
- 分解布尔表达式.
- 将复杂布尔表达式移入一个命名良好的布尔函数。
- 合并条件语句不同部分的重复片段
- 使用 break 或 return 替代循环控制变量
- 知道答案后立即返回,而不是在嵌套 if-then-else 语句中赋一个返回值。
- 用多态替代条件语句(尤其是重复的 case 语句)。
- 创建和使用空对象,而不是测试空值
- 子程序级重构
- 提取子程序
- 内联子程序的代码、将长的子程序转快为短动词短语
- 用简单算法代替复杂算法。
- 增加参数、
- 删除参数
- 将查询操作与修改操作分开
- 通过参数化合并类似的子程序
- 分解行为依赖于传入参数的子程序
- 传递整个对象而不是特定的字段。
- 传递特定的字段而不是整个对象。
- 封装向下转型 (downcasting)。
- 类实现重构
- 将值对象修改为引用对象
- 将引用对象修改为值对象
- 用数据初始化替代虚函数、
- 改变成员函数或数据的位置.
- 将特化代码提取到一个子类中
- 将相似代码合并到超类中,
- 类接口重构
- 移动方法到一个提供者。
- 将一个类转换为两个。
- 抽取类。
- 委托
- 去掉中间人。
- 用委托代替继承。
- 用继承代替委托。
- 引入外来的子程序
- 引入扩展类。
- 封装公开变量
- 系统级重构
- 为无法控制的数据创建一个明确的引用源
- 将单向累关联为双向类关联
- 提供工厂方法而不是简单构造函数
- 用异常代替错误代码或者相反
27 安全重构
- 每次改动都是一个语言改动策略的一部分吗?
- 重构前是否保存了最初的代码?
- 是否保持每次重构的幅度都很小?
- 是否一次只进行一个重构?
- 是否列出了在重构过程中打算采取的步骤?
- 是否做了一个停车场,以便记住重构中途产生的想法?
- 每次重构后都重新测试了吗?
- 若改动很复杂,或者会影响关键任务,是否进行了代码审查?
- 是否考虑过特定重构的风险等级并相应地调整了你的方法?
- 这个修改是否改善而非降低了程序的内部质量?
- 是否避免了用重构作为编码和修复的幌子,或者作为不重写坏代码的借口?
28 代码调优策略
- 程序整体性能
- 考虑通过变更程序需求来提升性能了吗?
- 考虑通过修改程序设计来提升性能了吗?
- 考虑通过修改类的设计来提升性能了吗?
- 考虑通过避免程序与操作系统的交互来提升性能了吗?
- 考虑通过避免IO操作创建来提升性能了吗?
- 考虑用编译型语言替代解释型语言来提升性能了吗?
- 考虑启用编译器优化选项来提升性能了吗?
- 考虑通过切换到不同的硬件设备来提升性能了吗?
- 代码调优是不是万不得已的最后选择?
- 代码调优方法
- 在开始代码调优之前,程序是完全正确的吗?
- 在代码调优之前,度量过性能瓶颈了吗?
- 度量过每一次代码调优的效果了吗?
- 如果代码调优并没有带来预期的性能提升,是否已撤销所有的改动?
- 是否尝试过针对每一个性能瓶颈进行多次修改以提升性能?
- 是否对性能瓶颈进行过优化?性能优化通常需要多次迭代才能通过代码调优达到预期的性能提升。
- 性能只是整体软件质量的一个方面,而且通常并不是最重要的。精心调优的代码只是整体性能的一个方面,而且往往不是最关键的。相比于代码的效率,程序的架构设计、组件之间的协作关系以及算法的选择和优化等都有更大的影响。构建性能优秀的代码只是整体性能优化的一小部分:除非系统自身的瓶颈得到解决,否则优化的效果会很有限。
- 需要关注的是整体系统的细节设计以及各个模块之间的协调与规划,这些对于整体性能的提升具有重大的影响。
- 确定瓶颈的定位是实现性能优化的关键。百足城出能真正提供高性能的指引方向的重要手段。
- 在开始编码时,为性能工作做好准备是编写易于理解和修改的清晰的代码的关键。
- 除非系统自身的瓶颈,否则知道是哪些代码在占据时间或者系统的复杂度吗?
29 代码调优技术
- 同时改进速度和规模
- 用查询表替代复杂逻辑。
- 合并循环
- 用整数而不是浮点变量
- 编译时初始化数据。
- 使用正确类型的常量。
- 预计算出结果。
- 消除公共子表达式
- 将关键子程序转化为低级语言
- 只改进速度
- 知道结果后,便停止测试:
- 按频率对 case 语句和 if-then-else 链中的测试进行排序,
- 比较相似逻辑结构的性能
- 使用惰性求值。
- 将循环中的条件判断提到外面
- 展开循环。
- 最小化循环内部的工作
- 在搜索循环中使用哨兵值
- 最忙的循环放到嵌套循环的内层
- 降低内层循环的运算强度
- 多维数组改为一维。
- 最小化数组引用。
- 为数据类型增加辅助索引。
- 缓存频繁使用的值,
- 利用代数恒等式。
- 降低逻辑和数学表达式的运算强度
- 小心系统子程序。
- 重写子程序以内联
30 配置管理
- 概要
- 软件配置管理计划是设计用来帮助程序员将开销尽量减小的吗?
- 软件配置管理 (SCM) 避免了对项目的过度控制吗?
- 变更请求是进行了分阶段,当作一个整体处理的吗?无论采用非正式的方法(例如创建一份传统的变更清单)还是更系统化的方法(例如设立变更控制委员会)。
- 系统化地评估了提交的每一项变更请求对于成本、进度和质量的影响了吗?
- 将重大的变更视为需求分析不够完善的一个警示了吗?
- 工具
- 采用版本控制软件来帮助配置管理了吗?
- 采用版本控制软件减少团队工作中的协作问题了吗?
- 备份
- 定期备份所有项目资料了吗?
- 定期将项目备份数据转移到异地存储了吗?
- 所有资料(包括源代码、文档、图片和重要的笔记)都备份了吗?
- 测试过备份与还原过程吗?
31 集成
- 集成策略
- 是否为集成子系统、类和程序确定了最优顺序?
- 继承顺序是否和构建顺序相互协作,保证在正确的时间每个类都将会为集成就绪?
- 准备就绪?
- 这种集成策略是否能让缺陷的诊断变得容易?
- 这种集成策略是否能让脚手架代码数量降到最低?
- 这种集成策略是否比其他方法更好?
- 组件之间的接口是否已经被详细定义了吗?虽然定义接口并不属于集成的任务,但是验证它们是否已经被良好定义是集成的任务)
- 每日构建和冒烟测试
- 顶目是否频繁地构建(理想的情况是每天构建)以支持增量式集成?
- 是否随着每个构建都执行了冒烟测试以确定每个构建是运行正常的?
- 构建和冒烟测试实现了自动化吗?
- 开发人员频繁提交代码是否频繁,即两次代码提交之间不会超过一天或两天?
- 冒烟测试是否随时与最新的代码保持一致,随着代码的更新而演变?
- 构建失败是偶发事件吗?
- 即使在承受压力的情况下是否坚持构建和执行冒烟测试验证软件?
32 编程工具
- 是否有一个高效的 IDE?
- IDE 是否集成了源代码控制工具(构建、测试和调试工具)及其他有用的功能?
- 是否有自动化常用重构操作的工具?
- 是否使用版本控制工具来管理源代码、文档、需求、设计、项目计划和其他项目工件?
- 如果在做一个超大型项目,是否有使用数据字典或者包含每个类的权威说明的中央知识库?
- 是否考虑过使用代码库而不是编写定制代码?哪里有可用的代码库?
- 是否在使用交互式调试器?
- 是否使用 make 或其他依赖关系控制软件来高效可靠地构建程序?
- 测试环境是否包含自动化测试框架、自动化测试生成器、覆盖率监控、系统扰动器、差异对比工具和缺陷跟踪软件?
- 是否有构建任何定制的工具以支持特定的项目需求,特别是自动化重复执行的任务的工具?
- 总的来说,目前的环境是否有足够的支持工具?
33 布局
- 通用
- 是否正确地凸现了代码逻辑结构的首要任务?
- 这种代码格式是否可以在使用中保持前后一致?
- 这种代码格式是否能产生易于维护的代码?
- 这种代码格式是否能提高代码的可读性?
- 控制结构
- 在代码中是否使用开始-结束或者 {} 避免了双重缩进?
- 连续的多个程序块是否用空行进行了分隔?
- 是否调整了复杂表达式的格式以保证可读性?
- 所有的单条语句块是否格式一致?
- case 语句的布局方式是否与其他控制结构保持一致?
- goto 语句的格式是否使其在视觉上更明显?
- 单条语句
- 是否使用空白来改善逻辑表达式、数组引用和子程序参数的可读性?
- 是否确保不完整的语句在行断开的地方有明显的语法错误?
- 换行时是否采用了标准的缩进量?
- 一行最多只包含一条语句?
- 编写每条语句时是否确保不包含副作用?
- 一行最多只包含一条数据声明?
- 注释
- 注释是否与其对应的代码缩进量相同?
- 注释的布局风格是否易于维护?
- 子程序
- 是否使用空行来分隔子程序中的不同部分,如类、文件和程序?
- 对于大多数类和文件,是否存在一一对应的关系?
- 类,文件和程序
- 如果一个文件包含多个类,是否已经清楚地将每个类中的所有子程序分隔开?
- 文件中的子程序是否以空行明显地进行了分隔?
- 作为更强大的组织原则的妥协代替方法,是否按字母顺序排列了所有子程序?
34 自文档代码
- 类
- 类的接口是否体现了某种一致的抽象?
- 类是否取好了名称,名称能否准确描述其核心意图?
- 类的接口是否让你一目了然地知道如何使用这个类?
- 类的接口抽象是否足以让你无需考虑其实现方式?可以当成黑盒对待吗?
- 子程序
- 是否每个子程序的名称都能准确描述它们的功能?
- 是否每个子程序都只执行一个明确定义的任务?
- 相比这些子程序被放入各自的子程序之前和之后,它们有受益吗?
- 每个子程序的接口是否清晰明确?
- 数据名称
- 类型名称是否足以协助记录数据声明?
- 变量是否命名良好?
- 变量是否只用于其命名所示的用途?
- 循环计数器是否使用了比 i、j、k 更具信息量的名称命名?
- 是否使用了精心命名的枚举类型,而非临时标识布尔变量?
- 是否使用了具名常量而非魔法数字或魔法字符串?
- 是否能区分类型名称、枚举名称、物理尺寸、具名敞亮、局部变量、类变量和全局部变量?
- 数据组织
- 额外变量是否根据需要用于澄清?
- 变量的引用是否彼此接近?
- 数据类型是否简单达到最低复杂度?
- 复杂数据是否是通过抽象访问子程序(抽象数据类型)来访问
- 控制
- 代码中的常规路径是否清晰?
- 相关语句是否组合在一起?
- 相对独立的语句组是否打包成各自的子程序?
- 常见路径是放在 if 语句的后面还是 else 语句的后面?
- 控制结构是否足够简单,达到最低复杂度?
- 是否每个循环和定义良好的子程序一样,执行一个且只执行一个功能?
- 嵌套有否做到最少?
- 是否使用新增的布尔变量、布尔函数和决策表来简化布尔表达式?
- 布局
- 程序的布局是否能表现其逻辑结构?
- 设计
- 代码是否明确直接且没有自作聪明?
- 是否尽可能隐藏了实现细节?
- 写程序时,是否尽可能多地使用问题域的术语,而非计算机科学或程序语言结构的术语?
35 好的注释
- 通用
- 别人拿到你的代码马上就能看懂吗?
- 注释是在解释代码意图或者总结代码做了什么,还是在复述代码?
- 是否采用伪代码编程法来减少注释的耗时?
- 对于重要的代码,是进行重写还是注释?
- 注释是最新的吗?
- 注释是否清晰、准确?
- 所用注释风格是否有利于修改注释?
- 语句和段落
- 是否避免在代码中使用行尾注释?
- 注释着重解释原因还是解释具体操作步骤?
- 注释是否有利于代码阅读者做好准备?
- 是否每条注释都有其用处?是否已删除或改进了多余的、无关紧要或过于随意的注释?
- 是否注释了代码的非常规之处?
- 是否避免使用缩略语?
- 主次注释的区别是否明显?
- 是否注释了用于处理某个缺陷或未公开特性的代码?
- 数据声明
- 是否已注释数据声明的数值单位?
- 是否已注释数值数据的取值范围?
- 是否已注释编码用途?
- 是否已注释输入数据的限制?
- 是否已注释位标志?
- 是否已在声明处注释各全局变量?
- 魔数是否已经替换为具名变量或者常量,而不是加注释
- 控制结构
- 是否对所有控制语句都进行了注释?
- 冗长或者复杂的控制结构,是否在代码结尾处进行了注释?或者竭力进行简化,而不再需要注释?
- 子程序
- 是否已注释各子程序的意图?
- 是否通过注释介绍了子程序的其他相关情况(如输入输出数据、接口假设、限制、纠错、全局效果和算法出处等)?
- 文件、类和程序
- 程序是否有一段简短的文档,就像书籍范本中介绍的那样,可以提供对程序组织方式的概览?
- 是否已说明各文件的意图?
结尾
唉,尴尬