こんにちは、ライクル事業部 エンジニアの菊池@kichionです
現在、ライクルでは大きく2チームで分かれて開発を進めており、私は技術負債解消を行うチームでシステム改善を行っています
ライクルでは早すぎたマイクロサービス化によりコードベースが30近いGithub repositoryに分散しており、システムツールのrepositoryを含めると50のrepositoryが存在していました。 アジリティ向上のため、コンテキストをまとめrepositoryを17個削ったのでその方法論を紹介したいと思います
背景
ライクルではマイクロサービス化を見据えてrepositoryを細かく切るように開発が進められていました
しかし、1機能1repositoryといった形でコンテキストを気にせずマルチリポジトリにした結果、以下のような問題を抱えていました
- ドメイン知識の分散
- コードの重複
- ライブラリアップデートなどの手間増大
問題が顕在化してから対策を検討している間にもrepository数が増えていき気づけば50のrepositoryに分離していました…
結果的にコンテキスト境界が定義づけられず、機能間でデータベース結合も存在しておりいくつかの分散モノリスとしてシステムが組まれたため、チーム分割をする際にもお互いに依存を気にしながら開発を行う必要が出てきてしまい根本的な解決を狙って技術負債解消を行うチームでこの問題に立ち向かうことになりました
分散モノリスとの闘い
一辺倒に「コンテキストを境界付けて、適切なrepositoryに統合しよう!」とすると付属の問題を抱えてしまうので要素を分解しながら取り組みました
1. 重複コード・知識の分散を"なるべく"解消する
まずは重複するコード・知識を洗い出し、ライブラリとして切り出していきます
幸いにもコードベースでオニオンアーキテクチャを採用する前提は揃っていたため、まずは外部へのアクセス部分を共通化するようにしました。共通化にあたっては社内ライブラリ化を推進することで大きな問題なく進めることができました
社内ライブラリ化については別の記事でまとめていますので合わせて御覧ください
entity / modelについては最新の知識が盛り込まれているrepositoryと古い・メンテナンスが行われていないrepositoryで差異が大きかったりと、このタイミングで変更を行うことがリスクになり得たのでカジュアルに行える場合にのみ実施しました
2. 使っていない・今後の仕様に耐えられない機能を消す
完全に削除することを前提に「この機能は使われていないのでは?」「機能が意味のある形になっているか?」と言った視点で整理していきました
「サービスとして削れるか?」も含めてPdM / EMなどに相談していました。結果的に削れた機能は多くないですが、使っているかもわからない機能を残さないという共通認識が持てたことは大きかったです
このタイミングで各repositoryの機能を洗い出すのでコンテキストの整理を行いながら進行していました
3. メンテナンスが辛くなったrepositoryの整理
秘伝のタレ化したrepositoryがいくつか存在しており「コードを呼んでも意図がわからない」「自動テストもないので変更が怖い」という意見の出たrepositoryについては根本的にリファクタリング・リアーキテクティングを行いました
ここは未知との闘い、痛みを伴う部分なので障害発生をゼロにするのではなくMTTRを最小にするような立ち回りで細かくリリースしました
幸いにも対象のrepositoryの多くはバッチ処理だったので問題発生からリカバリーと言ったフローも組みやすく進められました
4. 問題の複雑化を招いていたデータソースの整理
ライクルでは事業検証フェーズでインフラコストを抑えるなどの歴史的経緯から主要なデータソースにRDBではなくS3(CSV)を選択していました
現在では事業も成長しておりS3(CSV)を使い続ける必要がないため、それらのデータをRDBへ移行し処理系も大きく見直しました(移行と一言に言っても様々な問題が有りましたが、凄まじい情報量になるので割愛します…)
移行時は並行稼動でS3(CSV)とRDBに同様のデータを出力し、AthenaとRDSの値をRedashのQuery Resultsにより突合することでデータチェックを自動化していました
5. コンテキスト毎にrepositoryをまとめる
いよいよ、repositoryの統合がスムーズにできそうな状態まで下地が整ったのでコンテキスト毎にモノリポ化を進めます
整理した機能・コンテキストがあるので淡々とコード移行していきます
一部バッチ処理で「そもそも処理系・処理単位を変えたほうが良いのでは?」という意見も出てきたので新しい機能として作り直しも行っていきました
6. 統合し終わったrepositoryを消す
機能としても統合が完了したのでrepositoryを消します
ただし、単純にrepositoryを消すと「コミットの情報が消失してしまっていいのか?」という意見もあったので、埋葬用のrepositoryを用意し情報を残しました
# target={{対象のリポジトリ名}} # default_branch={{デフォルトブランチ}} $ git remote add $target git@github.com:${target}.git $ git fetch $target $ git read-tree --prefix=$target/ $target/$default_branch $ git checkout -- . $ git add . $ git commit -m "added $target"
終わりに
統合の検討から含めると半年かかった改善作業でしたが、終わってみるとあっという間でした。repository統合の流れで以下のように実施したのは有効だったと感じました
- コードの整理
- 機能の整理(辞めることも含めて)
- コンテキストの整理
- 統合
- 後片付け
repositoryがコンテキスト毎まとまり全体の見通しが良くなったことで機能依存を強く意識することなく実装が進められるようになりました
依然としてデータベース結合は残った状態なので次の一手としてデータベースを分ける必要はあります
しかし、全体としてコンテキストが明確になってきたことで真のマイクロサービス化を推し進める準備が整ったので良い一歩を踏み出せたと思っています