Programming Principle & Code Test
项目闭环
流程
- 需求澄清明确:理解背景、功能/收益点、基本设计与实现思路。
- 人无我有,人有我优,人优我异。
- 竞争力 = 质量/成本/进度 三角;保证质量优先。
- 开发框架构建:
- 敏捷开发流程(快速编译涉及的小单元、快速测试小例子)
- 彻底理解工具(或许耗时,但有方法能彻底理解逻辑,e.g.,pytorch.profile,viztracer,GDB)
- 实时看护运行框架(低开销监控修改的影响:修改正确的触发,对前中后的时间影响,e.g.,cpprinter,CI)
- 批量实验设计:组内原始数据,组件CSV数据对比,整体可视化感知。
- 理解已有设计:只有充分理解设计,才不会有疏忽漏洞和BUG
- 项目拆分,确定看护代码规模
- 先使用彻底理解工具来Profile理解
- 向前兼容设计/重构:
- 最小化修改原则下,回答5W1H
实时输出《详细设计文档》
- 4+1视图
- 小步开发交付:
- 小步敏捷开发。不超过100行代码提交一次PR。
- DEBUG/DEV同时开发,DEV虽然支持打印debug信息,但是没有
-g
的堆栈信息,不能快速GDB。 - 及时保存各种情况的编译包。
- DEBUG(需要真实的证据,和严谨的推导)
- 在同一套添加了DEBUG信息和开启了DEBUG模式的代码里,用环境变量字串,来控制代码中的元修改,通过排列组合的快速测试,来定位到相对于原代码,哪几处的元修改导致了seg fault.
- (不敏捷)使用无限画布,来推导
- 从已知到目标,包括猜想、实验、测试、阶段结论的循环。
- 测试并可视化:
- 由于共用机器,OS等环境很容易发生变化,测试时一定要成组测试,baseline和修改后要一起测试。
- 如果出现BUG,一定是看护规模不够大,影响了外部组件,返回第三步针对debug涉及组件扩大看护规模,重新收集。
实时输出《自验文档》,基于哪个gitid的包,进行了哪些对比实验。
wiki(使用说明)
- 接口部署
- 回顾分析:提取重点和案例
技巧
- 用户需求分类和排序:KANO三层模型
- 根因分析:5WHY,通过连续的询问来明确根本原因。
- 5W+3H: Why,What,Who,When,Where + How/How long/How much
- Why是关注项目的背景:一般是性能瓶颈,不支持流行的功能
- What是具体做什么,实现什么
- How much是这里一般指人力开销(每人天)
- How long是功能的开销
实际情况
由于机器(显卡)可用时间极短,所以要保证测试的有效性,功能开发时要提前单元测试,保证子模块的正确性和测试效率。
需要功能拆分,拆分出独立的功能单元,在其余仓库进行开发、编译和单元测试。之后在快速合并。
需要wiki
方法论
- 从已知推进到未知:基于事实论据-> 假设实践(设计编码)-> 测试验证 -> 支持/修正假设。
- 从复杂划分出简单:将复杂项目和需求拆分,缓慢的编译和测试打散,小步开发递进
三层类设计
- DAO(数据持久化)层:最底层是聚类数据和基于数据的原子操作,包括保存外部的config或者选项的参数的类config_data。
- Server层:上面一层是根据底层状态数据和外部参数决定的操作组合(工厂类),利用掌握的所有数据类及其上面的元操作进行复杂功能实现。满足开闭原则,这一层添加新函数。也可以再分成两层:偏原子操作类,偏调度类
- Interface/Controller层:在最上一层是外部处理接口, 并且掌握所有的数据类的unique_ptr,和所有的工厂类的使用。作用是建立/初始化工厂类和各数据类直接的关联
目标
- 期望:延续软件的易拓展性
- 认知的四层境界:不知道自己不知道;知道自己不知道,知道自己知道,不知道自己知道(化繁为简,融为一体,浑然一体)
- 认知的多个阶段:愚昧山峰->自信低谷->持续进步。
- 多用cpp-reference
代码设计
四层思想
- 目标:易读、易维护的Clean Code。
- 从抽象到具体有如下四层:
其余原则
- 首位是正确性(通过所有测试)
- DRY (Don’t Repeat Yourself)
- KISS (Keep It Simple, Stupid)
- YAGNI (You Ain’t Gonna Need It):不必预期编码,而是预留接口
- SMaRT,安全,可维护,可移植,可测试
- 最小职责(别和陌生人说话
具体实现
- BFS写法+分治法
- 函数设计要求
- 多思考函数的必要性,通用性
- 任务、函数解耦; 降低圈复杂度是提升可读性的有效方法。
- 命名简洁有特色,自注释,见字知意,
- 利用参数的信息来简化函数名,比如split(string A, char B)比splitString好。
- 如没必要,尽量简化传参
- 直接返回值,避免在外面初始化重要变量,然后传地址的方式
- 重要的输入,会喜欢写在外面,如果只在函数内使用,这是没必要的
- 代码就近原则,相关功能的代码写一起
- 多用空格
- 高效读代码:BFS读代码方式,边读代码边输出UML类图,
闭环
计划、总结反思
- PDCA 戴明环:plan do check act
- ORID :Objective客观 Reflective反应 Interpretive诠释 Decisional决定
代码测试设计
- 设计的案例需要覆盖所有独立变量/状态/功能的可能情况,但是不要测量最小独立变量间的排列组合,这不满足正交性。
- 反向验证:如果测试case报错,但是不能一眼定位代码问题,说明测试case不满足正交性,需要拆开。
- 如果建立的完备正交的独立测试集,但是还有代码没有覆盖,说明代码有冗余。
- 好的测试相当于代码文档。
- C++ 可以使用 googletest,可参考相关学习文档。
测试的分类
- UT(Unit Test)
- 多使用
Test Double
,用测试替身来替代测试环境中依赖的外部组件
- 多使用
- IT(Integrated Test)集成测试
- ST(System Test)
测试设计原则
TDD思想
- 极限编程核心编程循环:TDD(test-driven development) + 重构 + 简单设计
OOP思想
- 面向对象编程:将数据与操作封装在一起,相对于面向过程,虽然略微复杂,但是代码能高内聚,低耦合,提高了程序后续的可拓展性
- 面向对象的软件设计原则: 开闭原则、单一职责、依赖倒置、里氏替换
- 通过封装(访问控制)、继承(复用父类函数)、多态(重载函数实现)来实习
代码重构
- 定义:保持外部接口的时候,简化代码
- 目的:提高开发效率,使得代码易于理解与易于拓展
- 何时重构:有时间时、对核心和经常WTF的代码来重构
- 如何重构:小步提交。
- 识别坏味道的多种方法
- 抽取、 替换、 组成、 改名、 移动。
Programming Principle & Code Test
http://icarus.shaojiemike.top/2024/09/01/Work/Programming/ProgrammingPrinciple/