リポジトリの巨大化によるビルド時間増加への対策

# はじめに
あるプロジェクトにて、システムが大きくなり過ぎてしまい全てのテストが終わりデプロイされるまでに2時間ほどかかってしまっていた。これでは、どんな小さな変更でも少なくとも２時間かかってしまうことになり不都合が起きる。この問題に対してどのような方針を立てたのか頭の整理のためにもまとめておこうと思いました。

# プロジェクトで使用している技術
```
バックエンド： java, springboot
インフラ： paas
管理ツール： github
ciツール
```

# リポジトリ巨大化による課題
今回の課題を整理すると、
- ビルド時間増加
    - テスト実行時間の増加
    - ciツールにおけるリポジトリのclone時間増加

# リポジトリの初期状態
課題を抱えるリポジトリはリポジトリの内部に複数のモジュール(1~n)を保持していて、モジュール単位でPaas上へデプロイされる。モジュールnはベースモジュールを使用している。

# 課題解決の方向性
1. パイプラインを変更を加えことで影響が出るモジュールのテストのみ実行させる
    a. リポジトリ分割
    b. CIツールでパイプラインを条件分岐
→ 今回CIツールのスペックの影響でCIツール上で条件分岐をすることが叶わなかったのでリポジトリ分割を選択することとなった。
2. テスト高速化(言語によるところが大きいので、ここの説明は割愛します。)

## リポジトリ分割
### 要件
- テストを高速化したいため、パイプラインは細かく切った方がいい
- 複数モジュールを触るような案件があるので、いちいちエディタを開き直したくない
- ワンクリックで分割したリポジトリ全てをデプロイしたい
- 変更を加えたら影響する全てのCIテストが回ってほしい
- clone時間も減らしたい

### リポジトリ分割方法
- pom.xmlで関係するリポジトリをjarファイルとして取得する
    - パイプラインは分けることができる
    - jarファイルとして取得するとjarファイルの中身は編集できないため複数モジュールを触る案件の時には不便
    - ワンクリックで分割したリポジトリ全てをデプロイすることはCIツールで可能
    - 変更を加えたら影響する全てのCIテストが回る要件はCIツールで可能
    - jarは圧縮形式なのでclone時間はそのまま持ってくるより短い
- `git submodule`を使って依存するリポジトリを取得する
    - パイプラインは分けることができる
    - `git submodule`を使うとjarファイルと違い編集できるため、複数モジュールを触る案件でも分割前と同様にいじれる
    - ワンクリックで分割したリポジトリ全てをデプロイすることはCIツールで可能
    - 変更を加えたら影響する全てのCIテストが回る要件はCIツールで可能
    - jarと比べるとcloneに時間がかかる

→ clone時間の削減はCIにて特定ブランチのcloneもしくはdepthをつかったclone([詳しくはこちら](https://swet.dena.com/entry/2020/12/10/100000))をすれば多少clone時間を短縮することができるので`git submoduleを使って依存するリポジトリを取得する`を採用することとした。

### git submoduleとは？
こちらの記事がわかりやすかったです。

https://qiita.com/sotarok/items/0d525e568a6088f6f6bb

### 流れ

1. 分割対象の順番を決める(私は最近全然変更がされていなく今後も変更される予定がないモジュール→変更はされるがコア機能でないモジュール→コア機能の順番にしました。)
1. 最初に切り出すモジュールiのgit repository(リポジトリi)の作成
1. リポジトリiに必要な設定(pom.xmlやdangerやCIの設定)
1. リポジトリAから該当コードを移植(モジュールiのフォルダーごとではなく、中身のみ(取り込む際にpathを修正しなくても良くなるため))
1. ローカルでリポジトリiのテストが全て動くことを確認
1. 試験環境でデプロイし正常に動いていることを確認
1. ベースモジュールをリポジトリ分割
1. リポジトリAとリポジトリiに切り出したベースモジュールをサブモジュールとして連携
    1. `git submodule add <リポジトリURL>`でサブモジュール追加
    1. 間違えた場合は
    ```
      git submodule deinit -f submodule # 登録解除
      git rm -f submodule # ファイルを削除
      git commit -a -m "サブモジュールの削除"# 変更を保存
    ```
1. サブモジュール化によって不要になったリポジトリAとリポジトリi内の元のフォルダーを削除
1. 再度ローカルでリポジトリiのテストが全て動くこと、リポジトリAが動くことを確認
1. ベースモジュールが変更されたことをトリガーにしてリポジトリiとリポジトリAのテストが回るようにCIツールのパイプライン作成
1. 試験環境へデプロイし正常に動くことを確認
1. 本番環境へデプロイし正常に動くことを確認
1. 別のリポジトリーも同様に分割をする(ただし、ベースモジュールはすでに分割済みなのでスキップする)

### 注意点
- 今回私が触っているリポジトリのベースモジュールですが、頻繁に変更がされるものです。なので、上記のStep4~Step12までに元リポジトリで行われた変更は切り出したベースモジュールのリポジトリには反映されない。
→ 案1： `git remote add`してベースモジュールのリポジトリにリポジトリAから引っ張ってこれるように一時的に設定、次に一部のディレクトリだけ`git fetch`して最新のベースモジュールを取得して、`--allow-unrelated-histories`をつけてmergeする。
[参考１](https://maku77.github.io/git/merge/merge-repo.html)
[参考2](https://qiita.com/ponsuke0531/items/1e0ab0d6845ec93a0dc0)

- これにより課題は解決される見込みですが、開発の際に使用するgitコマンドが変わってくるのでチーム内に浸透させる必要があります
    - `git submodule foreach "git pull master --recursive"`：全てのサブモジュールのmasterの最新状態取得
    - `cd ./<サブモジュール>;git add .;git commit -m "",git push origin master`：単体でpushしたい時該当サブモジュールの階層へいく
    - git clone <リポジトリURL> --recursive：オプションつけないとサブモジュールのフォルダの中身がからになる
### 今回の内容をstepごとに表した図
![スクリーンショット 2022-03-12 15.18.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340562/e52f58e4-e860-edc9-39b6-224083a5c79f.png)