# 1. 技术债务的含义
所谓技术债务,通俗地讲,其实就是那些技术上没做好的事情,会逐渐体现为长期的成本。
如果把视野再拔高一点,其实不单单是技术有债务的问题,业务发展带来各种各样的债务,例如团队管理、项目管理、知识管理等,其实都可能形成债务。
技术债务其实无法彻底消除的,只要业务在持续运转,就一定会产生债务,重点是控制债务的规模,使其受控即可。
# 2. 成因
从研发团队的内外视角出发,可以分成外因和内因两大类。
# 2.1. 外因
- 技术发展:由于技术的发展,原先采用的技术已经落后业界,体现为落后于时代的生产力,研发效能落后于竞争对手。
- 业务发展:由于业务的发展,原先采用的技术已经无法适应业务,体现为业务受限,或研发效率、质量产生下滑。
- 团队变动:由于组织结构调整,遗留下来的祖传老业务,若不能舍弃,在反复交接后导致维护越来越困难。
# 2.2. 内因
- 团队管理:急功近利的文化,习惯性地将长期利益让位于短期利益,过度追求短期交付效率。团队人员的选、育、用、留不当,人才培养跟不上,体现为团队成员的能力不足(没招到 / 没留住 / 没培养出合适的人才,导致团队能力实质上无法匹配所需解决的问题)。
- 基础薄弱:团队所采用的基础设施落后,没有建设好统一的技术栈底座(基础库、框架、中间件等)。
- 标准缺失:团队没有根据最佳实践,持续建设好标准规范并遵循。
# 3. 专业做事
- 目标导向:反复自省长短期目标是什么,并动态观察路径、工具、方法、资源(人 / 财 / 物 / 关系)、时机节奏等。
- 复杂问题分而治之:时间上分迭代,空间上分模块。
- 标准化:有章可循,沉淀形成团队的规范合集。
- 范式化:对于很多重复的事情范式化,形成最佳实践,最好进一步固化下来,沉淀为模板、框架、库、组件、工具等。
- 工具化:通过工具化、自动化来提升生产效率(例如 git、codecc 代码扫描、tapd、iwiki、流水线、代码生成等)。
- 算法化、系统化:通过技术角度来解决问题(例如人工配置变为基于画像的智能决策)。
- 迭代思维:任务分期拆分迭代,小步快跑,闭环复盘,持续自省。
- 灰度:渐进过渡,平滑演进。
- 调研:市场调研,用户调研。
- ROI:考虑成本收益与风险。
- 知行合一:实践 - 理论 - 实践,螺旋式结构。
# 4. 具体做法
- 要持续专业地做事,周期性地审视系统,涵盖研发管线的所有环节。
- 团队文化层面,需要形成自顶而下的统一认知,重视技术事务,在团队的正负向激励方面也予以配合,鼓励那些主动清理债务的员工。
- 团队管理层面,从能力和意愿角度识别员工,鼓励学习,为大家赋能,同时淘汰价值观不匹配的员工。从以人为本的角度讲,只要员工素质好了,技术债也就自然受控。
- 组织架构上可以设立技术中心、公共组之类的,主要是可以明确权责,方便长期持续性地维护好基础技术栈。如果仅仅是一些平行的业务研发小组,最后就会缺乏统一的质量把关控制,也没有稳定的人力投入。
- 项目管理层面,采用迭代运作,迭代中除了业务需求,也可以安排技术需求(但很难固定一个人力的百分比)。单个具体需求的评估中,需要包含有设计、文档、自测等工作的时间。
- 大的技术专项(如技术栈演进),需要自顶而下驱动,并注意做好各个角色之间的沟通。
- 当老的系统确实积重难返,改好的成本甚至高于重写,此时可以跳出现有系统去思考,考虑立新以破旧,另起炉灶采用更先进更合适的技术。但一定要考虑好如何平滑过渡要当心新已立而旧难破的情况,同时也要评估好风险,从性能、安全、稳定性、扩展性等各方面评估新技术。例如:欢乐游戏的云原生技术栈演进。
- 持续沉淀最佳实践,形成标准、规范,并最好沉淀为模板、框架、库、组件、工具等。
- 适当采用开源开放的技术栈,通常比自己造轮子相对来讲会更加与时俱进,被技术发展淘汰的风险可能小一些。
- 从经验来看,统一性(识别共性问题并采用统一的解决方案)、扩展性(功能和性能都方便扩展)、可维护性(不要搞黑科技,不要看复杂的手册才能维护)会是相对重要的几点系统非功能性需求。
- 充分利用好故障复盘的机会,推动相关技术债的偿还。
- 逻辑与执行分离,数据与逻辑分离。纯函数,方便单测。
# 5. 微服务
架构的本质是管理复杂性,微服务本身是架构演化的结果。
架构原理是相对抽象和稳定的,而具体实现可以千差万别。微服务原理和软件工程,面向对象设计中的基本原理相通,体现如下:
单一职责(Single Responsibility),一个服务应当承担尽可能单一的职责,服务应基于有界的上下文(bounded context,通常是边界清晰的业务领域)构建,服务理想应当只有一个变更的理由(类似Robert C. Martin讲的:A class should have only one reason to change),当一个服务承担过多职责,就会产生各种耦合性问题,需要进一步拆分使其尽可能职责单一化。
关注分离(Separation of Concerns),跨横切面逻辑,例如日志分析、监控、限流、安全等等,尽可能与具体的业务逻辑相互分离,让开发人员能专注于业务逻辑的开发,减轻他们的思考负担,这个也是有界上下文(bounded context)的一个体现。
模块化(Modularity)和分而治之(Divide & Conquer),这个是解决复杂性问题的一般性方法,将大问题(如单块架构)大而化小(模块化和微服务化),然后分而治之。
微服务架构同时还是一个组织原理的体现,这个原理就是康威定律(Conway’s Law),Melvin Conway在1968年指出:“Any organization that design a system(defined broadly) will produce a design whose structure is a copy of the organization’s communication structure”,翻译成中文就是:设计系统的组织,其产生的设计和架构等价于组织间的沟通结构。Dan North对此还补充说:“Those system then constrain the options for organization change”,简言之,这些系统在建成之后反过来还会约束和限制组织的改变。
# 5.1. 微服务架构不是免费的午餐
当业务不复杂,团队规模不大的时候,单块架构比微服务架构具有更高的生产率(productivity),原因在于建立微服务架构需要额外的开销来支持和管理微服务,从而降低生产率;但是随着业务复杂性的增加和团队规模的扩大,单块架构比微服务架构的生产率下降更趋明显,当复杂性达到一个点,微服务架构的生产率会优于单块架构,原因在于微服务的松散耦合自治特性减缓了生产率的下降趋势。
另外,团队的技能是比单块或者微服务架构的选择更重要的因素。说白了,如果团队能力不行,不管用单块还是微服务,还是难于管理复杂性。
反过来,如果团队能力强,不管用单块还是微服务,都能找到好的管理复杂性的手段,所以说团队的技能才是管理复杂性的关键。
# 6. 分层
分层优点:抽象稳定,功能复用,功能内聚,屏蔽复杂和变化,扩展规模。
分层缺点:系统复杂度增加,性能/存储消耗,依赖添加/依赖传递。
分层原则:为了系统一致性,可以强制保留某些分层;允许跨层调用,不允许反向调用;优先依赖下层,不依赖同层;保证越底层越稳定;允许一层有多层;还有一句废话,权衡有优缺点,利大于弊就行。
# 7. 熵增定律和破窗效应
# 8. 写不出文章
为什么写不出文章?大部分人的原因可以用一句话概括:心里没东西。
要写文章至少要有自己的思考和总结吧,平时学习和工作中没总结,没有自己的理解,没有记录和思考,没有可反思的经验教训,试问如何写出文章来呢?
古人云,读书破万卷,下笔如有神。但是光靠读,可以写出风花雪月,技术沉淀类的还是要有自己思考的,而这思考一定来源于实践。
# 9. 代码规范
代码规范是一个人代码水平的其中一个维度,只有维护过大量乱糟糟的代码后才知道简洁的重要性,只有亲手搭过lint工具后才知道规范如此简单,只需要一键格式化。
对代码规范越重视,才能写出可维护性强、复用性强的代码,毕竟日抛型代码基本不需要规范。
大处着眼,小处着手。架构设计可以当做一个大处,代码规范,代码细节可以当做小处。细节决定成败。
一个人不重视代码规范,可以说明这么几点:
- 他没维护过大型、特大型项目
- 不熟悉eslint,没搭过eslint工具
- 没维护过稳定的、复用性强的模块
# 10. 易维护性
易维护性,就是不用费多少时间多少精力,就可以改功能或者加功能,甚至过了好几年,想改的时候也很好改(重构)。
一个反例,是我的爬虫服务。
# 11. 复用
充分复用,能用一套不写第二套的好处:
- 减少工作量
- 方便后期维护