编码技巧 笔记

1 整洁性

  • 语义简单明确,优先考虑易于读者理解的写法
  • 简洁!=代码短
    • 复杂的问号表达式反而不如 if else 方便理解
      • 个人疑问:问号表达式可以一定程度上提供条件数据传送,而非条件转移
  • 提前返错
    • 提前返回错误判断可以减少主体逻辑的缩进数量,使主体代码逻辑更醒目
  • 利用析构函数做清理工作
    • 利用C++析构函数做清理工作,在复杂冗长代码中不会漏掉. 比如执行回调,关闭文件,释放内存
  • 用朴素直观的算法
    • 在非关键路径上,优先使用朴素直观,维护性好的代码.
  • 用轮询代替条件变量
    • 非关键路径上这么做,代码间接,不容易出bug
    • 轮询: 一直等待信号
  • 在关键对象增加magic字段
    • 增加magic字段和断言检查,可以及时发现内存错误

2 测试

  • 边界

  • 状态/分支测试

  • 重复/幂等性测试

  • 兼容性测试

  • 防御性测试

    • 关注系统在最差情况下的表现,明确能力边界
  • 避免写出不稳定case

      • 测试不聚焦,无脑复制粘贴,等价类测试爆炸
      • 异步等待,基于时间假设,sleep 并发,未能在预期的窗口期交互
      • 有顺序依赖的测试,共享某个状态
      • 资源溢出,数据库链接满、内存 OOM 析构随机 core
      • 析构未严格保序或者未构造
      • 多线程共享资源的错误用法导致概率 crash
      • 有未处理完的任务就退出

3 提交

  1. 一次提交不要超过400行代码,最好只解决一个问题
  2. 自我检查
    1. 速度<500行/小时
    2. 一次review时间不超过1小时
    3. 接口>测试>实现

4 高效工作方法

  • 抽象和分而治之

    • 抽象:明确模块之间的依赖关系,确定API接口
    • 分而治之:对子系统设计进行合理的注释,帮助理解
    • 代码提交尽量做到原子[不可分割的特性.修复.优化],测试代码一同提交
  • 不要重复

    • 寻找重复逻辑和代码,并进行封装
    • 寻找流程重复,使用脚本或者工具自动化
    • 沉淀踩坑经验
  • 快速迭代

    • 不要过度设计
    • 尽快让代码运行和快速验证,不断迭代来完善
    • 为了快速验证,本地测试成本低
    • 实现一个可运行的脚手架,再持续添加内容
  • 忌“太心急”,慢即是快

    • 需求澄清:类似 TCP 三次握手,用自己理解的方式再给对方讲一遍,确认双方理解一致,对焦,避免重复返工
    • 自我提问:为什么做这件事?业务价值是什么?关键技术是什么?已有的系统和它对比有什么不同?兄弟团队是否做过类似的工作?是否有经验可供参考?业务/技术的适用场景是什么?预计耗时和进度风险?
    • 新人往往脚踏实地,忘记了仰望星空,只顾着埋头苦干,不思考背后的业务价值,这一锄头,那一铁锹,遍地都是坑,就是不开花,费时费力,成就感低。
  • 忌低效沟通,用数据说话

    • 精确地描述问题,上下文和范围,提供有效信息
    • 文档是提高沟通效率的最佳方式之一,Google 有文档文化,推荐阅读《Design Docs at Google》[5]
    • Bad Case:「测试 CX6 网卡时,IOPS 大幅下降」
    • Good Case:「在 100g 网络标卡 CX6 验证性能时,8 jobs 32 depth iosize 4K 场景下,极限 IOPS 从 120 万下降至 110 万,与 FIC 卡相比性能存在 8% 差异」
  • 忌“蠢问题”,学会提问

    • 鼓励新人多提问,但提问的问题一定要有质量
    • 关于如何提出一个好问题推荐阅读《提问的智慧》[6]
    • Bad Case:「我在编译耗时很长,我怀疑是资源不够,这种情况怎么办?」
    • Good Case:「我的开发机编译耗时 2 小时,不符合预期,OS 是 centOS 7U、128GB 内存、64Core,编译并发度是 20 核,未限制内存,编译过程使用 Top 查看确实 20 核并发,Cpu 和 Mem没有达到瓶颈,iostat 看磁盘使用率每秒 60%」
  • 5 延伸阅读

  • 编写可读代码的艺术

    software Engineer at Google

    人月神话

    数据密集型应用系统设计