最近我们团队在做季度技术规划的时候,遇到了一个选择:是先做微服务拆分,还是先引入TypeScript?

这两个方向,一个是后端架构的升级,一个是前端技术栈的升级,都很有价值,团队里也都有人支持。但是团队精力有限,不能同时做,必须选一个先做。

这个问题,看起来是两个不相关的技术方向的选择,但其实本质上是一个技术优先级和资源分配的问题。在做技术规划的时候,我们经常会遇到这样的选择:有很多有价值的事情想做,但是人力有限,时间有限,必须排优先级,选最重要的先做。

今天,我想记录一下我们团队这次技术选型的思考过程,包括两个方向的价值、成本、风险、收益分析,以及我们最终的选择和理由。希望能给正在做技术选型的团队一些参考和启发。

一、背景:为什么会有这个选择

先说说我们团队的背景,以及为什么会有这个选择。

我们团队是一个做To B SaaS产品的团队,大概20个人,其中后端10人,前端6人,测试2人,产品2人。产品已经做了三年了,有一定的用户量,也在持续迭代。

目前的技术栈是:

  • 后端:单体应用,Java + Spring Boot + MySQL,部署在几台云服务器上,用Nginx做负载均衡
  • 前端:React + JavaScript + Webpack 3,组件库用的是Ant Design

这两年,随着业务的发展,我们的技术栈也暴露出了一些问题:

后端的问题

  1. 单体应用越来越大,代码量超过了50万行,编译部署慢,开发效率低
  2. 模块之间耦合严重,改一个地方可能影响很多地方,风险大
  3. 团队协作困难,20个人在同一个代码库里开发,经常冲突
  4. 扩展性差,只能整体扩容,不能按需扩容
  5. 技术债越积越多,很多老代码没人敢改

前端的问题

  1. 用的是JavaScript,没有类型系统,大型项目维护困难,重构风险大
  2. 接口数据没有类型约束,前后端联调经常因为字段问题出bug
  3. 代码提示和自动补全不好,开发效率低
  4. 重构困难,改一个组件的props,不知道会影响哪些地方
  5. 新人上手慢,没有类型约束,很难理解代码结构

因为这些问题,团队里一直有人提议做技术升级:后端的同学提议做微服务拆分,前端的同学提议引入TypeScript。

这两个提议都很有道理,也都有很多人支持。但是,我们团队的精力有限,每个季度能做的技术升级是有限的,不能同时做两个大的技术升级。所以,我们必须选一个,这个季度先做,另一个下个季度再做。

于是,就有了这次"微服务拆分 vs TypeScript入门"的选择。

二、微服务拆分的价值、成本和风险

先分析一下微服务拆分这个方向。

价值

微服务拆分的价值,主要有以下几点:

  1. 提升开发效率:拆成微服务之后,每个服务独立开发、独立部署,团队可以并行开发,不会互相影响。编译部署也快了,每个服务的代码量小,编译部署时间短。
  2. 降低耦合:每个服务职责单一,服务之间通过API通信,耦合度降低。改一个服务,不会影响其他服务,风险小。
  3. 提升扩展性:可以按需扩容,哪个服务压力大,就扩容哪个服务,不需要整体扩容,节省资源。
  4. 技术栈灵活:每个服务可以用不同的技术栈,比如有的服务用Java,有的用Go,有的用Python,可以根据服务的特点选择最合适的技术栈。
  5. 提升系统稳定性:一个服务出问题,不会影响整个系统,故障隔离更好。而且,可以对核心服务做更多的冗余和优化,提升系统整体的稳定性。

成本

微服务拆分的成本,也很高:

  1. 开发成本高:微服务拆分不是简单地把代码拆开,还要处理分布式事务、服务间调用、服务注册发现、配置中心、API网关等一系列问题,开发量很大。
  2. 运维成本高:从一个应用变成十几个甚至几十个服务,运维复杂度大幅提升。需要容器化、CI/CD、监控告警、日志收集、链路追踪等基础设施的支撑。
  3. 学习成本高:微服务是一套新的架构理念和技术栈,团队需要学习很多新东西,比如Docker、Kubernetes、服务注册发现、配置中心、分布式事务等。
  4. 迁移成本高:从单体迁移到微服务,是一个大工程,需要很长时间,而且迁移过程中还要保证业务正常运行,不能影响用户。
  5. 人力成本高:微服务拆分需要投入大量的人力,而且需要有经验的架构师来主导,不是随便就能做好的。

风险

微服务拆分的风险,也不容忽视:

  1. 拆分不当的风险:如果服务边界划分不好,拆出来的微服务可能还是"分布式单体",耦合依然严重,而且增加了分布式的复杂度,得不偿失。
  2. 分布式事务的风险:微服务下,分布式事务是一个大难题。如果处理不好,可能会出现数据不一致的问题,影响业务。
  3. 运维复杂度的风险:微服务的运维复杂度很高,如果团队的运维能力跟不上,可能会出现更多的故障,系统稳定性反而下降。
  4. 性能下降的风险:微服务之间通过网络通信,比单体的进程内调用慢很多。如果服务拆分太细,调用链太长,可能会导致性能下降。
  5. 项目延期的风险:微服务拆分是一个大工程,很容易延期。如果拆分过程中遇到问题,可能会影响正常的业务迭代,得不偿失。

三、TypeScript入门的价值、成本和风险

再分析一下TypeScript入门这个方向。

价值

引入TypeScript的价值,主要有以下几点:

  1. 类型系统,减少bug:TypeScript有静态类型系统,可以在编译时发现类型错误,减少运行时的bug。特别是对于大型项目,类型系统能有效减少因为字段不匹配、参数错误等导致的bug。
  2. 更好的开发体验:TypeScript有了类型信息之后,IDE的代码提示、自动补全、跳转定义、重构等功能都更好用了,开发效率提升明显。
  3. 更容易维护和重构:有了类型系统,代码的结构更清晰,更容易理解。重构的时候,编译器会帮你检查类型错误,不用担心改了一个地方影响了其他地方不知道。
  4. 前后端类型共享:如果后端也用TypeScript(比如Node.js),或者用TypeScript定义接口类型,前后端可以共享类型定义,减少联调时的字段问题。即使后端不用TypeScript,也可以用TypeScript定义接口的类型,前端开发的时候有类型提示。
  5. 渐进式引入,风险低:TypeScript是JavaScript的超集,可以渐进式引入。不需要一次性把所有代码都改成TypeScript,可以先从新代码开始,或者先从核心模块开始,慢慢迁移。
  6. 社区生态成熟:TypeScript已经很成熟了,React、Vue、Angular等主流框架都支持TypeScript,大部分第三方库也有类型定义(@types),生态很完善。

成本

引入TypeScript的成本,相对较低:

  1. 学习成本:TypeScript的语法和JavaScript很像,只是多了类型注解。有JavaScript基础的开发者,学习TypeScript很快,大概1-2周就能上手。当然,要精通TypeScript的高级特性(比如泛型、条件类型、映射类型等),需要更多时间,但是入门很简单。
  2. 开发成本:引入TypeScript之后,写代码的时候需要多写类型注解,开发速度会稍微慢一点。但是,因为有了类型提示和编译检查,调试和修bug的时间减少了,整体开发效率其实是提升的。
  3. 迁移成本:如果是新项目,直接用TypeScript,没有迁移成本。如果是老项目,需要把JavaScript代码迁移到TypeScript,这需要一定的时间。但是,TypeScript支持渐进式迁移,可以先从新代码开始,老代码慢慢迁移,不需要一次性迁移完。
  4. 构建成本:TypeScript需要编译成JavaScript才能运行,构建时间会稍微增加一点。但是,现在的构建工具(比如Webpack、Babel)都支持TypeScript,编译速度很快,影响不大。
  5. 类型定义成本:对于一些没有类型定义的第三方库,需要自己写类型定义(.d.ts文件),这需要一定的时间。但是,大部分常用的第三方库都有官方或者社区提供的类型定义,需要自己写的情况不多。

风险

引入TypeScript的风险,相对较低:

  1. 学习曲线的风险:如果团队成员对TypeScript不熟悉,可能会有一个适应期,开发效率暂时下降。但是,TypeScript入门简单,这个适应期不会太长。
  2. 类型定义不完善的风险:有些第三方库的类型定义不完善,或者有错误,可能会导致类型检查不通过,或者类型提示不准确。但是,这种情况不多,而且可以通过修改类型定义或者用any来绕过。
  3. 过度设计的风险:有些开发者用了TypeScript之后,会过度使用高级类型,把代码写得很复杂,反而降低了可读性。但是,这是开发者的问题,不是TypeScript的问题,可以通过代码规范和review来避免。
  4. 迁移过程中的风险:如果是老项目迁移,迁移过程中可能会出现一些类型错误,需要花时间修复。但是,因为可以渐进式迁移,这个风险是可控的。

四、对比分析:到底该选哪个

分析完两个方向的价值、成本和风险,我们来做一个对比。

维度微服务拆分TypeScript入门
价值高,但是长期才能体现中高,短期就能体现
成本很高,需要大量人力和时间较低,学习和迁移成本都不高
风险高,拆分不当、运维复杂、分布式事务等低,渐进式引入,风险可控
见效时间慢,可能需要3-6个月才能看到效果快,1-2个月就能看到效果
团队接受度后端支持,前端无感前端支持,后端无感
对业务的影响大,迁移过程中可能影响业务迭代小,渐进式引入,不影响业务迭代
可逆性低,拆了之后很难再合回去高,不想用了可以再改回JavaScript

从这个对比可以看出,微服务拆分的价值更高,但是成本和风险也更高,见效慢;TypeScript入门的价值稍低,但是成本和风险也低,见效快。

这其实是一个典型的"长期高价值高风险" vs "短期中价值低风险"的选择。

那么,到底该选哪个呢?

我觉得,这个问题没有标准答案,要看团队的具体情况。下面是我的一些判断标准:

什么时候选微服务拆分

如果你的团队符合以下情况,可以优先选微服务拆分:

  1. 单体应用已经非常大,严重影响了开发效率和系统稳定性,到了不得不拆的地步
  2. 团队有足够的人力(至少3-5个后端),可以投入到微服务拆分中
  3. 团队有微服务的经验,或者有经验丰富的架构师来主导
  4. 基础设施已经比较完善(或者有能力建设),比如容器化、CI/CD、监控告警等
  5. 业务相对稳定,有时间做技术升级,不需要频繁迭代新功能

什么时候选TypeScript入门

如果你的团队符合以下情况,可以优先选TypeScript入门:

  1. 前端项目已经比较大,JavaScript的维护成本越来越高,bug越来越多
  2. 前端团队对TypeScript有兴趣,愿意学习和使用
  3. 团队的人力有限,不能投入大量人力做大型的技术升级
  4. 业务迭代频繁,没有时间做大型的技术升级,需要低风险、见效快的改进
  5. 后端的单体应用还没到不得不拆的地步,还能再撑一段时间

五、我们的选择和理由

那么,我们团队最终选了哪个呢?

我们选了TypeScript入门

理由如下:

理由一:当前阶段,前端的痛点更迫切

我们团队目前的痛点,前端比后端更迫切。

后端的单体应用,虽然有一些问题,但是还能撑得住。编译部署虽然慢,但是还能接受;模块耦合虽然严重,但是大家都熟悉了,改代码的时候小心一点,也还好;团队协作虽然有冲突,但是通过代码规范和review,也能控制。

而前端的问题,已经比较严重了。我们的前端项目,代码量也不小了,用JavaScript维护起来越来越困难。经常出现因为字段不匹配、参数错误导致的bug,而且这些bug都是在运行时才发现,有的甚至到了线上才发现。重构的时候也很担心,改一个组件,不知道会影响哪些地方,只能小心翼翼地改,然后全面测试,效率很低。

前端团队的同学,对引入TypeScript的呼声很高,已经主动学习了TypeScript,并且在一些小项目里试用过,反馈很好。

所以,从痛点的迫切程度来看,前端引入TypeScript更迫切。

理由二:TypeScript成本低、风险小、见效快

TypeScript的成本低、风险小、见效快,这是我们选择它的重要原因。

我们团队这个季度的业务迭代任务很重,能投入到技术升级的人力有限。微服务拆分需要投入大量的人力,而且周期长,风险高,我们这个季度确实没有足够的精力来做。

而TypeScript入门,成本低,风险小,见效快。前端团队的同学已经有一定的基础,只需要1-2周的学习和适应,就能开始用TypeScript开发新代码。而且,可以渐进式引入,先从新代码开始,老代码慢慢迁移,不会影响正常的业务迭代。

我们估算了一下,这个季度投入2个前端同学,用1个月的时间,就能完成TypeScript的引入和核心模块的迁移,剩下的时间可以继续迁移其他模块。投入产出比很高。

理由三:微服务拆分需要更多准备,下个季度做更合适

微服务拆分,我们不是不做,而是觉得现在还没准备好,下个季度做更合适。

目前,我们的基础设施还不够完善,容器化和CI/CD刚搭好,监控告警和链路追踪还在建设中。如果现在就做微服务拆分,运维能力跟不上,可能会出更多的问题。

而且,我们团队的微服务经验还不足,虽然有几个同学有微服务的经验,但是整个团队还没有形成统一的微服务开发规范和最佳实践。如果现在就做微服务拆分,可能会拆分不当,或者踩很多坑。

所以,我们决定,这个季度先做TypeScript入门,同时利用这个季度的时间,做好微服务拆分的准备工作:

  1. 完善基础设施,特别是监控告警、链路追踪、日志收集等
  2. 组织微服务的培训和学习,让团队成员都了解微服务的理念和技术
  3. 制定微服务的开发规范和最佳实践
  4. 做微服务拆分的方案设计,划分服务边界,制定迁移计划
  5. 先从一个边缘的、简单的服务开始试点,积累经验

等这些准备工作做好了,下个季度再正式开始微服务拆分,风险会小很多,成功率也会高很多。

理由四:技术升级要循序渐进,不要贪多

最后一个理由,也是我觉得最重要的理由:技术升级要循序渐进,不要贪多。

很多团队做技术升级的时候,容易犯一个错误:贪多。想一下子做很多技术升级,微服务、容器化、TypeScript、新框架、新语言,都想一起上。结果呢,每个都做不好,每个都半途而废,反而影响了正常的业务迭代。

我觉得,技术升级要循序渐进,一个一个来。每个季度选1-2个最重要的技术升级,集中精力做好,做扎实,看到效果,然后再做下一个。这样,虽然看起来慢,但是实际上是最快的,因为每个都做成了,没有浪费精力。

我们团队这个季度,就集中精力做好TypeScript入门这一件事,同时做好微服务拆分的准备工作。下个季度,再集中精力做微服务拆分。这样,两个方向都能做好,不会顾此失彼。

六、我们的TypeScript引入计划

既然选了TypeScript入门,我们也制定了详细的引入计划。

第一阶段:学习和准备(第1-2周)

  1. 组织TypeScript的培训,前端团队集体学习TypeScript的语法和最佳实践
  2. 搭建TypeScript的开发环境,配置Webpack、Babel、ESLint、IDE等
  3. 制定TypeScript的开发规范,包括命名规范、类型定义规范、any使用规范等
  4. 调研第三方库的类型定义情况,确认我们用的库都有类型定义

第二阶段:新代码用TypeScript(第3-4周)

  1. 从这个阶段开始,所有新写的代码,都用TypeScript
  2. 新的组件、新的工具函数、新的模块,都用TypeScript写
  3. 老代码暂时不动,但是如果修改老代码,顺手把修改的部分改成TypeScript
  4. 每周做一次TypeScript的代码review,确保代码质量,分享最佳实践

第三阶段:核心模块迁移(第5-8周)

  1. 选择几个核心的、常用的模块,优先迁移到TypeScript
  2. 比如,通用组件库、工具函数库、API请求层、状态管理等
  3. 迁移的时候,顺便做代码重构,优化代码结构
  4. 迁移一个模块,测试一个模块,确保迁移不影响功能

第四阶段:全面迁移和优化(第9-12周)

  1. 继续迁移其他模块,争取这个季度结束的时候,80%以上的代码都迁移到TypeScript
  2. 优化类型定义,减少any的使用,提升类型覆盖率
  3. 引入TypeScript的高级特性,比如泛型、条件类型、映射类型等,提升代码的类型安全性
  4. 总结TypeScript的使用经验,形成团队的最佳实践文档

通过这个计划,我们希望这个季度结束的时候,前端团队能全面用上TypeScript,代码质量和开发效率都有明显提升。

七、经验总结

最后,总结一下这次技术选型的经验。

经验一:技术选型没有标准答案,要根据团队情况来

技术选型,没有绝对的对和错,也没有标准答案。微服务拆分和TypeScript入门,都是有价值的技术方向,选哪个都没错,关键是看哪个更适合当前阶段的团队。

不要盲目跟风,看别人做微服务你也做微服务,看别人用TypeScript你也用TypeScript。要根据自己团队的实际情况,包括团队规模、技术栈、痛点、人力、时间等,做出最适合自己的选择。

经验二:价值、成本、风险,三个维度都要考虑

做技术选型的时候,不能只看价值,还要看成本和风险。一个技术方向,价值再高,如果成本太高、风险太大,团队承受不了,那也不是好的选择。

要综合考虑价值、成本、风险三个维度,选择性价比最高、风险最可控的方向。有时候,一个价值稍低但是成本低、风险小的方向,比一个价值高但是成本高、风险大的方向,更适合当前阶段。

经验三:技术升级要循序渐进,不要贪多

技术升级要循序渐进,一个一个来,不要贪多。每个季度选1-2个最重要的技术升级,集中精力做好,做扎实,看到效果,然后再做下一个。

贪多嚼不烂,同时做很多技术升级,结果往往是每个都做不好,每个都半途而废,反而浪费了精力,影响了业务。

经验四:准备工作很重要,不要打无准备之仗

对于大型的技术升级(比如微服务拆分),准备工作很重要。不要上来就做,要先做好准备:学习培训、方案设计、基础设施建设、规范制定、试点验证等。

准备工作做好了,正式做的时候才会顺利,风险才会小。打无准备之仗,很容易失败。

经验五:团队共识很重要

技术选型,不仅仅是技术问题,也是人的问题。如果团队没有共识,有人支持,有人反对,那执行起来肯定会有问题。

做技术选型的时候,要让团队成员充分讨论,每个人都发表意见,最后达成共识。即使有人不同意最终的选择,也要让他理解为什么这么选,并且愿意执行。团队共识,是技术升级成功的重要保障。

结语

"微服务拆分 vs TypeScript入门,到底该选哪个?"

这个问题,我们团队最终选了TypeScript入门,同时做好微服务拆分的准备工作,下个季度再做微服务拆分。

这个选择,不一定适合所有团队,但是适合我们团队当前的情况。技术选型,没有标准答案,适合自己的,就是最好的。

希望我们的思考过程,能给正在做技术选型的团队一些参考和启发。也欢迎大家交流讨论,你们团队在做技术选型的时候,是怎么思考和决策的?

最后,用一句话总结:"技术选型的本质,不是选最好的技术,而是选最适合当前阶段团队的技术。价值、成本、风险,综合考量,循序渐进,才能把技术升级做好。"

愿每一个技术团队,都能做出适合自己的技术选型,都能通过技术升级,提升团队的效率和产品的质量。