- 写出能解决问题的程序有100种方法,但是优秀的工程师能找到最好的一种。
- 代码自描述
- 代码是写给人看的,不是写给机器看的,只是恰好能运行而已
- 每条语句皆有作用,不做无谓的赋值
- 减少上下文依赖,编写显而易见是正确的代码
- 最小化参数要求
- 传ID就能解决问题的时候不需要传对象
- 减少函数内多余的中间变量
- 减少中间变量的命名的意义
- 直接return
- 在一个作用域内,变量的类型保持不变(仅对脚本语言)
- 不要滥用语法糖,少用奇技淫巧
- 如果你的整体代码结构变得极其复杂,往往是产品需求有问题
- 从整体到细节,自顶向下的工作顺序
- 基础库的编写顺序,优先写 guide / example
- 全新的业务工程,先不考虑架构
- 好的代码不是写出来的,是重构出来的
- 在项目发展的合适时机进行重构,项目规划过程中应该留出明确的重构时间
- 这份札记是如何编写出来的
- 重构的过程中不一定要保证每次修改均可运行
- 防御式编程
- 循环结构优化
- 不同的语言有不同的最佳实践
- 不要轻易使用while(true)
- 错误和异常是常态,错误处理像业务代码本身一样必不可少
- 原子化函数
- 举例,微博三步授权流程代码
- 业务逻辑分层
- 最好的代码拆分方式是,清晰定义每一层的程序各自解决什么问题,不关注什么问题
- 基于清晰的分层原则,即使在很多类里面出现一行代码的函数,你也会非常自信地认为这是合理的
- 如果无法定义分层,就只能根据代码复杂度来拆分。通常情况下,这是因为你的理解还不够深刻,或者需求的复杂度还没有充分展开。
- 类的层次
- 什么样的代码应该写成独立业务,什么样的代码应该写在公共流程中(并加启用条件)
- 如果使用率很高,就写在主流程里
- 如果只是业务的一个分支,则不应该给主程序增加复杂度
- 不要把业务相关代码和基础库代码混在一起
- 始终以要开源的标准去写基础库,绝不耦合业务逻辑
- 业务逻辑的不同层次的实现方式
- 大量需要变更和管理的业务,存在数据库
- 很少更新,并且无逻辑的,写在配置文件里
- 很少更新,但是有逻辑的,写在独立的程序文件里
- 尽量不要写在主程序里
- 一个功能全面的大方法还是多个功能简单的小方法
- 与其用复杂的配置参数,不如用多个简单的原子化方法
- 然后写一个通过多个原子化方法实现功能的shorthand方法
- 善用fluence interface
- 如果一个函数要接受5个以上参数,你可以先想想这些参数真的是用来解决同一个问题的吗?
- 什么情况下用配置文件的方式接受配置,什么情况下用函数来接受配置?
- 配置文件虽然看起来不需要编程,但实际上额外需要一份冗长的配置参数文档
- 配置文件方式限制了灵活性,但保证了安全性,适用于提供给对外部用户的功能
- 函数方式提供了更强的灵活性可塑性,比如TensorFlow之于Caffe
- 函数方式要求使用者具备编程能力,适用于中间件,适用于需要二次开发的场景
- 除非有特别需求,提供一种初始化/配置方法就够了,不要玩参数列表的花活
- 一个可以处理多种情况的多if-else函数,还是处理单一情况的简单函数
- 区分有状态对象和无状态对象
- 类内尽量少维护状态变量
- public的方法必须在自己函数体内完成对所有状态变量的维护
- 所有public方法,调用前和调用后,类的状态应该是自洽的
- shorthand方法
- 不要在局部函数里修改全局变量
- 如果你很难给某个函数起名字,往往是因为代码拆分有问题
- 如果你很难给某个类起名字,可能这个类就不应该存在
- 对于脏的输入,明确地在一层代码中做参数校验,而在后续的代码层中完全信任传入的参数,因为都已经被校验过
- 正确看待封装
- 不要为了封装而封装
- 封装的时候,最重要的不是选择隐藏什么细节,而是暴露什么细节
- 不要惧怕学习,或者阻止别人学习。不要为了避免别人的学习而封装
- fail-fast
- 服务端,如果请求hang住会导致服务器被打满,需要尽快把有问题的请求抛弃掉,保证普通请求的正常执行
- 引申,如果有错误,不要遮着掩掩,而应该让错误及时暴露出来,更早发现更快解决
- 如果这个问题可以在开发过程中被发现并解决,就不是什么问题
- 不要在代码里为不会使用API的程序员报错误
- 开发过程中,应该让错误出现得越明显越好,能crash能抛异常,而不要静默处理
- 用户层,不要掩盖错误细节,至少提供错误码或者保存日志
- 什么样的代码应该写成函数?
- 不是代码写了两遍就需要写成函数,而是这个函数有明确的功能和清晰的参数列表
- 不要因为一段代码只写过一遍,就不抽成函数
- 合理冗余代码
- DRY原则永远是对的吗?
- 少写代码并不是架构设计的主要目标,甚至都不是目标
- 明智而自豪地复制代码
- 面向未来编程,如果两段代码目前很相似,但未来会分化,就不要复用它
- 三行以内的代码不值得复用
- 不要因为自己查资料写出了一段自己原来不会写的代码,就一定要把这段代码抽成函数
- 恶心自己,方便大家
- 类的内部合理冗余代码,外部调用足够简单
- 复用的代价
- 实际处理过程被隐藏
- 灵活性损失
- 性能损失
- 通用和性能之间的平衡点
- Lazy Load
- 在用户主路径上的业务和开销极小的业务不需要 Lazy Load
- 变量名的原则
- 变量的命名要符合上下文场景
- 一个参数传递的链条中,不一定是同一个名字
- 学好英语,起好名字
- 用意义准确的特殊词,少用组合词,比如: SiteUser -> Member
- 英文动词不只有get和set,用好动词准确表达
- 正确使用单复数
- 梳理出明确的命名原则
- 建立一个产品概念到开发概念的映射词典
- 能用批量替换解决的问题,就不是问题
- Don't comment WHAT, comment WHY.
- 不要寄希望于用注释来解释清楚代码
- 和业务相关的脏代码必须注释
- 删除历史遗留代码,而不要注释它
- 善用 FIXME, TODO 等特殊注释关键词,以便日后检索整理方便
- 多层继承还是多个独立的类
- 尽量在一个类中呈现出完整的业务逻辑,同一个层次的业务逻辑,不要写在不同层的类中
- 多层继承的代码可读性和可维护性远比你想象得要糟
- 善用trait/mixin
- 每一个public函数的切面,整个对象状态都自洽
- 将对成员变量的变更和维护,集中到有限几个函数中,原子化的功能函数不维护状态变量
- 用唯一确定的属性作为定位的规则,避免歧义
- 按层放置文件
- 文件名和类名有一致的对应规则
- All Input is Evil
- 所有来自用户自由输入的参数,都必须检查
- spl库的运用
- yield的运用
- 命名空间的运用