DPE(Developer Productivity Engineering)ユニットに所属している、alpaca-tcです。
最近モジュラーモノリス化を進めるためにRuby動的解析ツールを作ったので、その話をします。
📝 私事ですが、新潟の佐渡島に移住しました。新潟や佐渡島のRubyistの方がいらっしゃいましたら、ぜひRubyKaigiでお友達になってください!
SmartHRではRailsのモジュラーモノリス化を検討をしているよ
Railsにおける「モジュラーモノリス」は、アプリケーションを拡張性のある構造にするために、単一プロセスでモノリスアプリケーションを区分されたサブセット(モジュール)に分割するアーキテクチャのことです。
SmartHRでは、コード量が多いプロダクトでモジュラーモノリス化を進めています。
すでに新規機能の開発では導入されていますが、既存コードのモジュラーモノリス化はまだ道半ばです。
この作業の難しさは、膨大なコードに対してどこでモジュールを分割するかの境界の探索が難解なためです。
DPEユニットでは、Ruby動的解析ツールを作成して、モジュールの境界を探索するための支援することにしました。
Rubyの動的解析ツールを作ったよ
diver_down というgemを用意しました。
このgemでは、実行されたRubyコードを解析してコードの依存関係を抽出・閲覧することができます。
解析
DiverDown::Trace::Tracer
を使うと、ブロック内のRubyコードを解析することができます。
# gemを除外したユーザーアプリケーションのパスのみ解析結果に出力する caller_paths = [ *Dir["app/**/*.rb"], *Dir["lib/**/*.rb"], ] tracer = DiverDown::Trace::Tracer.new( caller_paths:, module_set: { paths: caller_paths, } ) # resultには使用されたクラスやメソッドや依存関係などが格納される result = tracer.trace do # do something end
閲覧
解析結果は bundle exec diver_down_web
を使って、ブラウザ上で閲覧することができます。
表示される内容は以下の通りです。
- class/moduleの名前
- 依存関係(呼び出されているメソッド情報)
モジュールの探索
先ほどの解析結果のUIは、閲覧だけではなくてモジュール境界を指定して、任意のモジュールの境界を試行錯誤することができます。
モジュールを指定すると、依存関係の表示でも四角い図でモジュールが囲まれます。
このようにモジュールを指定すると、モジュール単位での依存関係が明らかになります。
また、いくつか試行錯誤のための機能を提供しています。
複数の解析結果をまとめて表示したり、粒度を変更してモジュール間の依存関係のみを表示したりできます。
なぜ動的解析を選んだのか
既存コードに対してモジュールの境界を探索することはとても難しい作業です。
この作業の不確実性を減らすためには、実際の動作を元にした情報の支援による効果が高いと考えて動的解析を選びました。
- class/moduleの命名を元に分類をすると、実際の振る舞いや依存関係を見落とす可能性がある
- Excelのようなツールで分類を進めるよりも、グラフのような可視化を伴う分類の方が洞察を得やすい
- モジュラーモノリス化でよく使われるShopify/packwerkは静的解析なので、依存関係の見落としが起こり得る
- 実際にコードを変更する前に、モジュール境界のパターンを試行することができる
- 依存関係とコード行数が解析によって特定されることで、実際にモジュールを分割する前に作業コストを把握できる
これからの取り組み
diver_downを用いたモジュール境界の探索は、社内でも始まったばかりです。
動的解析によってアプリケーション全体の依存関係が可視化されましたが、まだモジュールの境界は試行錯誤の途中です。
ここから1-2ヶ月ほどかけて、開発チームと共にモジュールの探索作業を進めていきますので、また進捗がありましたら投稿します。