# 再看Monorepo

## What is Monorepo

> 由于谷歌在 Monorepo 上的实践，Monorepo 受到了越来越多的关注。Monorepo 意味着把所有项目的所有代码统一维护在一个单一的代码版本库中，和多代码库方案相比，两者各有优劣，需要根据公司文化和产品特性进行取舍

记得两年前还在某W司的时候，当时我设计了一套基于prometheus的监控平台，这套平台是微服务架构，当时作为微服务门徒的我跟同事在服务划分上产生了分歧：是一个repo一个服务，还是将不相关的**两个服务**放在一个repo里面？当然，我支持前者，那位同事支持后者，彼时我并没有说服对方，还觉着很沮丧，不过服务是由那位同事编写，我也没有过多地坚持自己的意见。记得当天我将这件事当作牢骚发在微博上，有一个朋友就留言道：这没什么啊，谷歌也是这么干的啊……我惊了，谷歌会这样？不相信也不接受，直到最近因为工作原因，开始接触[bazel](https://bazel.build)，好像发现了一个新天地……

### 我们的现状

我现在的工作是做一个公司的内部服务平台，依然使用我“熟悉”的微服务架构，我对“微”这个字保持着洁癖，以至于一年之后的现在，我负责的微服务多达10几个，由于是内部平台，流量小，清一色部署在k8s上，0.5G+0.5C的配置，短小而精悍，这期间我还开发了一个极简的golang服务脚手架，又引入了`golib`共享库，加入了对dubbo、gRPC的支持(虽然现在看没有必要)等等，还有metric、log、trace，持续给系统加菜，效果上看，对功能的支撑是没问题的，开发速度也还不错，只有我又发现了几个不大不小的问题

- client sdk: 平台的微服务间使用http协议进行通信，我引入了swagger，新建了一个client repo, 专门存放通过[swag](https://github.com/swaggo/swag)自动生成的client sdk，问题在于，协同开发时，A服务依赖B服务, 所以B服务接口定义好后，生成sdk，更新到client repo，A服务才能继续进行开发，就有人A服务repo、B服务repo、client repo三个代码仓库，有时会出现不一致的情况，小团队人少问题也不是特别明显；
- `golib`共享库：会放一些常规代码：日志处理、中间件、共用的数据定义等等，这个库主要我在维护，主要目的是减少重复代码
- 代码风格：这是一个让我非常头疼的问题，由于一些历史原因，团队的代码格式化做的非常糟糕，更不用提静态检查了，因为没有强制措施的缘故，我甚至当了半年多的“格式化”工程师，我自问并没有代码洁癖，格式化已经是最低要求了
- 负载：这其实是一个额外的问题，在我之前，团队没人关注这一些，服务无脑申请资源，至少1C+2G，平均负载低的可怜，极大地浪费，在我的强烈敦促下，才减少到0.5G+0.5C，就这样，平均负载50%都不到（我一度怀疑是不是咱们团队写的代码太优秀了）

### Monorepo解决的问题

其实真有点讽刺，今年以前，我还是微服务的无脑拥护者，不过抛开那些耳熟能详的高谈阔论，单一代码仓库又确实能解决我的问题，而我们的服务又是微服务的，在我们这样子的小团队，优势还是很明显的

- 代码风格统一
- 抽象得当的话，代码复用率会非常高
- 小团队，代码合并问题不大

因为公司的CI选型是[droneci](https://www.drone.io/) ，且没有任何迹像要引入bazel，所以单一代码仓库对提供构建效率是毫无帮助的。

### 轮回

如果过往奉k8s、微服务、容器化为圭臬，现如今我也没有得出完全相反的结论，合适的场景，选择适合的技术体系，使团队收益最大化，才是紧要的事情，毕竟，唯一不变的，就是变化。

参考

- https://en.wikipedia.org/wiki/Monorepo
- https://xie.infoq.cn/article/4f870ba6a7c8e0fd825295c92
- https://earthly.dev/blog/golang-monorepo/
