如何在团队里写好代码

先来看两段文字,体会一下文字中所蕴含的味道!

即使我们的工作是在维护“屎山(Shit Mountain)”,也请不要忘记时时仰望星空!

—— 2013年“搞笑诺贝尔奖”中提到屎壳郎在迷路时能够利用银河导航

不管你们有多敬业,加多少班,在面对烂系统时,你仍然会寸步难行,因为你大部分的精力不是在应付开发需求,而是在应对代码的混乱带来的问题

—— Rober C.Martin

团队中为何会出现“烂”代码?

团队中出现“烂”代码通常会有多方面的原因:

  • 系统的复杂性:系统设计方案过于复杂(过度设计),在逐渐迭代的过程中,因为业务的复杂度增加带来系统的复杂度越来越高,代码越写越烂

软件的复杂性是一个基本特征,而不是偶然如此 —— 《人月神话》

  • 开发者能力不足
    • 写代码是一种技能,但是写好代码确实一件很不容易的事情,需要持续学习和实践,同时在团队内要建立良好的学习氛围,分享最佳实践和经验
  • 开发团队缺少技术文化的氛围
    • 团队缺乏对代码质量的要求,过于专注于完成开发任务,导致“面条”代码泛滥。同时也有可能是团队缺少统一的代码规范要求,代码“千人千面”
  • 各种无休止的妥协
    • 向各方妥协,包括自己、产品经理、工期、技术债等,导致设计的丧失,缺乏对代码质量的关注,过于注重功能的交付

更有甚者,对如何写好代码完全不管,功能怼上去就行,还会安慰自己:下次我优化下!于是一场没有结局的代码追爱之路就开始了

好代码的爽

在技术领域,好的代码不仅仅是一堆能够运行的指令,更是一项技能和艺术的结合。好的代码带来的爽不仅仅局限于程序的执行结果,更体现在开发者个人、团队协作以及项目整体成功的方方面面。

  • 开发者的技艺形象和影响力
    • 好代码是开发者的代言人,通过写出整洁、可读性强的代码,开发者展示了对编码技巧的熟练掌握,提升个人的技术影响力
    • 代码是最直接的技术交流方式,Talk is cheap,Please show me your code!好的代码反映了开发者的专业水平,为团队成员提供了学习的范本,提升了整体团队的技术影响力
  • 统一的代码规范,结构优雅的代码,维护成本低,同时研发效率得到提升
    • 统一的代码规范会让代码在团队内的更易于阅读和理解,降低维护的成本
    • 良好设计的代码结构可以使功能的扩展和修改变得更加方便,提升团队整体的研发效率
  • 合理的设计和优雅的代码可以让开发者和团队成员赏心悦目
    • 优雅的代码不仅是机器能够理解的指令,更是一种艺术品,可以让开发者和团队成员在阅读代码的过程中感受到愉悦,代码如诗如画
    • 通过共同遵守规范、欣赏彼此的代码,团队成员间的默契和协作得到加强,形成强大凝聚力的团队

好的代码不仅仅是实现功能的手段,更是一种技术、文化和团队合作的体现。通过写好代码,不仅提升个人的技术水平,也为团队和项目的成功奠定基础

程序员的美德

作为程序员,肩负着创造性和协作性的责任,在这个过程中,培养一些美德和良好的实践是至关重要的,特别是对于协作伙伴和未来接手代码的人

  • 可读性好的设计和代码
    • 注重编写易于理解、可读性好的代码,让协作者轻松理解设计意图,代码为协作者而写
    • 适度而清晰的注释为他人提供上下文信息,帮助他人更快地冗余代码中
  • 维护性强的设计和代码
    • 代码设计时要未雨绸缪,需要具有足够弹性以便应对未来的变化,避免硬编码和过度复杂的逻辑结构
    • 将代码分解成独立的模块,每个模块有清晰的职责,降低耦合提高整体可维护性
  • 持续学习和分享
    • 积极学习新技术,不断提升自己的技术水平,适应行业快速发展的行业变化
    • 分享经验和知识,将学到的新知识分享给团队,共同成长
  • 尊重他人的贡献
    • 认可协作者的工作,尊重并认可他人的设计和代码,通过积极建设性的反馈帮助对方提高
    • 为接盘侠考虑,在离开项目或团队时,留下详细的文档和整洁的代码,为接手者提供好的上下文,减少知识的流失

通过培养这些美德,不仅为自己创造了愉悦的开发环境,也可以为整个团队的协作和项目的持续健康贡献力量,责任心和协作意识共同工程程序员的美德

写好代码的指导原则

在编写高质量代码时,需要也应该要遵循的一些指导原则,确保代码可维护性、可读性和可扩展性

  • 代码的目的是维护而非实现功能
    • 认识代码的生命周期,平时大部分的研发时间会花在阅读、理解和维护代码上,因此编写易于维护的代码比仅仅实现功能更加重要
  • 优秀的代码应该自描述优于文档和注释
    • Scrum 中也倡导代码自描述,代码中避免过多的注释,清晰的代码本身就是最好的文档
  • 设计模式是手段而非目的
    • 设计模式的使用是为了解决特定的问题,而不是为了增加复杂度,设计模式是写好代码的手段,不应该成为编码的目的,清洗简洁的代码才是编码的最终目标

指导书籍

  1. 提供实际编程技巧和架构原则帮助开发者编写清晰可维护的代码:《代码整洁之道》、《架构整洁之道》、《匠艺整洁之道》
  2. 针对 Java 提供了一系列有效的编程实践让代码更加健壮高效:《Effective Java》
  3. 提供重构技术让代码更加易读:《重构:改善既有代码的设计》
  4. 阿里巴巴团队在 Java 开发方面的经验总结:《阿里巴巴 Java 开发手册》

官方要求

本部分对原则不做展开描述,后续架构设计基础理论部分有详细介绍

  1. 架构设计的三原则:合适、简单、演化
  1. 软件设计的七原则:SOLID + CARP + LoD

民间要求

  1. 运行速度快
  2. BUG 数量少且易于发现
  3. 可扩展且维护迭代成本小
  4. 易读性且团队协作效率高

代码“坏”味道

  1. 代码重复:大量重复代码增加维护成本 -> 提取重复代码,封装为可复用的方法或模块
  1. 方法太长:难以阅读和理解 -> 拆分为更小易于管理的方法
  2. 类太长:类过于庞大难以维护 -> 拆分更小功能单一的类提高代码灵活性
  3. 数据浆糊:相同代码重复出现导致不必要的冗余 -> 清理冗余代码确保相同逻辑不散落在不同的方法和类
  4. 参数列表太长:过长的参数列表方法对可读性和可维护性有极大伤害 -> 长参数列表封装成对象提高代码清晰度
  5. 命名不清晰:使用有意义的命名可以让代码更可读
  6. if/else 过多:大量嵌套的 if/else 语句,影响代码的可维护性 -> 考虑设计模式简化逻辑
  7. 循环里资源浪费:循环内部放置大对象 -> 大对象不在循环里提供资源利用率
  8. 全程一个 try:降低代码健壮性 -> 合理使用 try-catch 在合适的位置
  9. 资源未及时清理:未及时清理资源易导致内存泄漏问题 -> 使用过的资源及时释放
  10. 循环里字符拼接使用 +:效率低 -> 使用 SB 等高效手段拼接
  11. 散落各处的错误码:难维护统一错误码 -> 使用统一错误码定义
  12. 同质常量定义不用枚举:使用枚举管理同质常量提高清晰度
  13. 代码风格不统一:千人千面的代码风格可读性极低 -> 统一规范要求
  14. 缺乏统一的日志打印规范:无法对日志集中管理亦无法进行日志搜集分析 -> 统一规范、统一处理框架
  15. ......

如何写好代码

  • 码前阶段
    • 清晰理解需求场景,设计合理的架构方案,在合理的架构方案基础上,进行技术方案的设计,技术方案设计过程中要善用图的方式去设计,流程图、用例图、架构图、时序图、状态图、 ER 图、......
    • 团队内要有统一标准的规范要求、流程制度、常用高效工具等
    • 营造合理的技术文化氛围,比如技术方案脑暴、Review 等
  • 码中阶段
    • 遵守基本编码原则和要求(上述已讲)
    • 持续重构改进和性能优化
    • TDD 驱动的研发方式
  • 码后阶段
    • 定期代码 CR,确保代码质量,同时确保代码符合团队规范和标准,同时传递知识和分享,提高团队整体技术水平
    • 时机:正常 CR 的时机是第一波冒烟测试之后,千万别上线前夜做 CR
    • 事后:定期回顾和总结CR结果;CheckList 和代码规范的持续更新完善;发现好的代码和设计,同时 Diss 坏的代码和设计
    • 措施:CR 过程中如有严重风险问题的上线前必须完成修改,然后进行二次CR;其他问题尽量上线前完成修改,如果未修改要加TODO,指定人和时间来修改,同时项目管理空间,添加技术优化需求
    • 内容:对照技术方案是如何编码落地的;总体代码流程、关键设计、重点功能、基本规范
    • 前提:编译通过,插件检测通过;基于 CheckList 自检通过
    • 方式:300/500 行标准,区分线上/线下CR(团队具体情况自定)
    • 持续进行自动化测试,确保质量和稳定性。性能测试持续进行,确保高负载下的稳定性
    • 补充好详细的技术文档,通过知识库的方式记录和分享技术经验和问题解决方案
    • 个人/团队事后复盘,提升个人解决问题能力的同时,更新团队 CheckList 和规范要求

最佳实践

命名规范

命名很难,总体原则是见名知义

  • 变量名
    • 名词,正确描述业务场景,有表达含义
    • 理论上是不需要注释说明的,比如 id 和 userId,id 是需要注释用户 ID 的
    • 魔法值禁止
  • 函数名
    • 具体说明做什么,千万不要命名成怎么做
    • 不用泛称,比如 processData,所有的方法都是做数据处理的,这样一个方法名没有一定意义,尽量用完整的单词描述清楚做什么,不用怕字多,比如 checkUserLoginRequestParams
  • 类名
    • 实体类:承载核心业务数据和核心业务逻辑,命名要充分体现业务语义,并团队内达成共识
    • 辅助类:辅助实体类完成业务逻辑,命名要能通过后缀来体现功能,比如 UserController、UserService、UserRepository
    • 后缀清晰:XO 对象定义要明确,数据库DO,数据传输对象(服务之间传输)DTO,页面对象VO,还可以有其他,但是务必团队内统一

代码结构和组织

  1. 包的组织结构,优雅的分层结构
  2. 类和方法的组织结构
  3. 避免过度嵌套
  4. 合理的空行使用

代码注释

  1. 合理的注释位置和格式
  2. 注释的目的和内容
    1. 别给糟糕的代码加注释 —— 与其加注释,不如重新写代码
    2. 注释一般说明为什么这么做(真实的意图),做什么不强求,有更好

异常处理

  1. 系统自定义两类异常(BizException & SystemException),自有服务针对异常做统一处理,常用AOP方式处理,避免使用 catch 所有异常的方式
  2. 错误码:外部服务使用 Code,但需要统一指导文档输出;内部服务使用 Msg,但要见名知义,风格P_XXX_XXX(参数异常)、B_XXX_XXX(业务异常)、S_XXX_XXX(系统异常)

设计模式的使用

  1. 不详细讲解
  2. 一句话:设计模式不是目的只是手段,用的不好还不如不用

日志规范

正常关注ERROR、WARN、INFO、DEBUG

  • ERROR
    • 打印线程堆栈、一定的上下文,比如 traceId、关键性参数,便于排查问题
    • 一定要有监控和报警的配置 → 需要及时排查防止故障发生 → 不能滥用,狼来了
  • WARN
    • 可预知的业务问题:参数校验不通过、鉴权失败等
    • 配置时间段、一定阈值范围的监控和报警
  • INFO
    • 定位是用来初步定位问题的,记录简单状态变更、核心处理流程等变化信息,不能滥用,处处 INFO
  • DEBUG
    • 开发同学自己玩的日志,更进一步协助排查问题使用,线上不打印,但对于核心服务流程,可以设置开关来决定线上是否开启DEBUG日志的打印

Java 的高级特性实践

  • Lambda 表达式与函数式编程
    • 适当使用,不能滥用
    • Lambda 表达式可简化代码
  • Stream API
  • Optional 的合理使用
    • 避免空指针异常

代码性能和相关安全

  • 数据库性能以及优化
    • 合理的索引
    • 合理的缓存策略使用
  • 多线程优化
    • 规避线程安全问题
    • 合理使用锁机制
  • 安全考虑
    • SQL 注入防止
    • 脚本攻击防止
  • 资金安全
    • 资损评估
    • 资金流程强监控
    • 资金对账回补
  • 易恢复
    • 重大功能的灰度/AB 能力
    • CI/CD 流程支持快速回滚
  • 可监控/可发现/可报警
    • 性能监控、异常监控、关键指标监控
    • 报警发现和处理机制
  • 可限流/可降级
    • 识别强弱依赖,保证可降级
    • 识别可限流场景,做好监控和限流配置

思考

  1. 如何进行有效的 CR?
  2. 何时以及如何进行代码重构?
  3. 你和你现在的团队在实际项目中遇到的阻止你写好代码的问题?你是怎么解决的?

你可能感兴趣的:(职场技术人,程序人生)