Slide 1

Slide 1 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 1 ⾷べログのデッドコード解析基盤の裏側 株式会社カカクコム ⾷べログ開発本部 技術部 マイクロサービス化チーム 栗⼭ 友樹

Slide 2

Slide 2 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 2 本⽇のお品書き 1. ⾷べログがデッドコード解析基盤を必要とした背景 2. デッドコード解析基盤の裏側 3. 静的解析のちょっとだけツッコんだ詳細 4. まとめ ‒ 成果と課題

Slide 3

Slide 3 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 3 発表者⾃⼰紹介 株式会社カカクコム ⾷べログ開発本部 技術部 マイクロサービス化 チームリーダー兼テックリード ⾷べログではサービス開発、DevOps などを経て2019年からマイクロサー ビス化を担当しています マイクロサービス化チームのここ数年の公開活動抜粋 • ⾷べログのレストラン検索を⽀える Debezium と Apache Kafka ‒ Qiita • ⾷べログの内製Pub/Subメッセージング基盤をApache Kafkaにリプレイスした 話 ‒ Qiita • 緊急Ques 「⾷べログの品質ダッシュボード」-コードのメトリクスに基づくリ ファクタリング戦略 • Debezium Usecases in Tabelog - Tabelog Tech Blog • ⼤規模サービスにマッチした可変レート分散トレーシング - Tabelog Tech Blog

Slide 4

Slide 4 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 4 ⾷べログがデッドコード解析基盤を 必要とした背景

Slide 5

Slide 5 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 5 ⾷べログではアーキテクチャ刷新レベルのリファクタリングが進⾏している ⾷べログは2007年に ASP から Ruby on Rails にリプレイスされましたが、 それ以降はアーキテクチャ刷新レベルのリファクタリングなしに継続運⽤ されています。どれくらい⻑い期間かというと、この間に Windows が Vista から11まで4回代替わりしているくらいです。 Rails の標準的なモノリシックアーキテクチャは当時の⾷べログにはマッチ していて、⾷べログは短期間で⼤きく成⻑できました。しかしコードベー スが⼤きくなり、サービスも複雑になった現在の⾷べログにはマッチしな いため、⼤規模なリファクタリング計画が進⾏中です。

Slide 6

Slide 6 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 6 リファクタリング前にデッドコードを削減して認知負荷を下げたい 密結合で技術的負債が溜まったアプリケーションというのは散らかった部 屋のようなものです。散らかった部屋を⾒て「よし、部屋を分けよう!」 なんていきなり⾔いだす⼈はあまりいなくて、まずはいらないものを捨て たり、ホコリや汚れをキレイに掃除したり、収納グッズを買って⽤途ごと に置き場所を決めたりするなどして、部屋を整理しようと考えるのが⾃然 です。 カカクコムCTO京和による2020年のアドベントカレンダー ⾷べログの⼤規模なレガシーシステムを段階的に改善していく取り組み から引⽤

Slide 7

Slide 7 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 7 しかし Ruby, Ruby on Rails では、コードが本当に不要なのか判断するのが難しい Ruby は極めて動的な開発⾔語なので、デッドコードを静的解析だけ で判別するのは難しい • メタプログラミングがかんたんで「メソッド名でgrepしてヒットしない」=「未使⽤」 と断⾔できない • コンパイル・トランスパイルが不要で、実⾏前にデッドコードを検知して警告する機会 がない • Rails にはライブラリの明⽰的な require が不要な auto load の仕組みがあり「requireさ れてないコードは不要」という判断もできない • などなど…… そこで今回紹介するデッドコード解析基盤を構築しました。

Slide 8

Slide 8 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 8 デッドコード解析基盤の裏側

Slide 9

Slide 9 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 解析サーバ 9 デッドコード解析基盤の概要 Google Cloud BigQuery ⾷べログ稼働サーバ アプリケーション 動的 カバレッジ ログ転送 ⾷べログ リポジトリ 構⽂解析 処理 メソッド 解析情報 動的 カバレッジ ⽇次集計 カバレッジ メソッド単位 カバレッジ デッドコード解析基盤は⼤まかに3パートで構成されています。動的な実⾏カバレッジの計測(Part 1)、静的 なメソッド解析(Part 2)、最後に両者をマッチングして、⻑いスパンで追跡しやすいメソッド単位のカバ レッジ(Part 3)を取得します。 Coverage ライブラリ メソッド解析情報 x ⽇次集計カバレッジ マッチング処理 Part 1 Part 2 Part 3

Slide 10

Slide 10 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 10 デッドコード解析基盤説明のためのサンプルコード ここからはこの test.rb をサンプルとしてデッドコード解析基盤の動作を説明してゆきます。

Slide 11

Slide 11 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 解析サーバ 11 Part 1. 動的な実⾏カバレッジ Google Cloud BigQuery ⾷べログ稼働サーバ アプリケーション 動的 カバレッジ ログ転送 ⾷べログ リポジトリ 構⽂解析 処理 メソッド 解析情報 動的 カバレッジ ⽇次集計 カバレッジ メソッド単位 カバレッジ Coverage ライブラリ メソッド解析情報 x ⽇次集計カバレッジ マッチング処理 Part 1 まず稼働サーバで動的な実⾏カバレッジを計測して⽇次集計カバレッジを取得します。

Slide 12

Slide 12 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 12 アプリケーション稼働サーバでコード実⾏カバレッジを計測する Ruby の標準ライブラリ Coverage を使って実際の動作環境でどのコード断 ⽚が実⾏されたかを⾏単位で計測します。⾷べログでは実⾏性能への影響が 最も⼩さい oneshot_lines モードを採⽤しています。 https://docs.ruby-lang.org/ja/latest/library/coverage.html oneshot coverage の計測モードでは、各⾏について最初の 1 回だけカウントアップのフックを実⾏し ます。1 度実⾏されたら、その⾏のフックのフラグを消し去るので、あとはカバレッジ計測のない状 態と同じになります。 Ruby 2.6 新機能:本番環境での利⽤を⽬指したコードカバレッジ計測機能 - クックパッド開発者ブログ から引⽤

Slide 13

Slide 13 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 13 カバレッジを BigQuery に転送して⽇次でマージ集計 1回のリクエスト/レスポンスごとにファイル名と実⾏された⾏番号、リビジョンなどの付帯情報をカバレッ ジログに出⼒して fluent-bit で BigQuery に転送しています。ハイライトしてる executed_lines が実⾏され たコードの⾏番号です。このログは断⽚的で⼤量なので⽇次でマージ集計します。 カバレッジログ ⽇次集計カバレッジ この例で気が付いたと思いますが、メソッド定義の開始⾏は常に 実⾏される扱い、終了⾏は常に実⾏されない扱いになります

Slide 14

Slide 14 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 14 動的な実⾏カバレッジの産物サンプル こうして集計したデータは以下のようなイメージです。この段階では⾏番号ベースなのでファイルを変更する とずれてしまい、⻑いスパンで計測するのは難しいです。 例えば頻繁に改修がある重要なモデルの中に、めったに使わない社内管理機能から利⽤されるコードがあると ⾟いです。

Slide 15

Slide 15 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 解析サーバ 15 Part 2. 静的なコード解析 Google Cloud BigQuery ⾷べログ稼働サーバ アプリケーション 動的 カバレッジ ログ転送 ⾷べログ リポジトリ 構⽂解析 処理 メソッド 解析情報 動的 カバレッジ ⽇次集計 カバレッジ メソッド単位 カバレッジ Coverage ライブラリ メソッド解析情報 x ⽇次集計カバレッジ マッチング処理 Part 2 そこで⾏番号をメソッドに対応付けて、⾏番号より変化しづらいメソッド名で実⾏実績を追跡できるように します。このパートではリポジトリのコードを静的に解析してメソッド解析情報を取得します。

Slide 16

Slide 16 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 16 コードを AST に変換する まずカバレッジ計測されたリビジョンのコードをリポジトリからチェックアウトして、 RubyVM::AbstractSyntaxTree (標準ライブラリ。以下 RubyVM::AST と省略)を使い Abstract Syntax Tree(抽象構⽂⽊)に変換します。

Slide 17

Slide 17 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 17 AST から各要素の定義位置を解析する AST からモジュールやクラス、メソッドの定義開始・終了位置がわかります。

Slide 18

Slide 18 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 18 ⾏番号を名前空間、クラス、メソッドに対応付ける メソッドの開始・終了⾏のデータが取得できました。

Slide 19

Slide 19 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 解析サーバ 19 Part 3. 動的な実⾏カバレッジと静的なメソッド解析のマッチング Google Cloud BigQuery ⾷べログ稼働サーバ アプリケーション 動的 カバレッジ ログ転送 ⾷べログ リポジトリ 構⽂解析 処理 メソッド 解析情報 動的 カバレッジ ⽇次集計 カバレッジ メソッド単位 カバレッジ Coverage ライブラリ メソッド解析情報 x ⽇次集計カバレッジ マッチング処理 Part 3 最後に2種のデータをマッチングしてメソッド単位のカバレッジを取得します。

Slide 20

Slide 20 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 20 後はデータをマッチングするだけ! 動的な実⾏カバレッジ(Part 1)と静的なメソッド解析(Part 2)を組み合わせて メ ソッド単位の実⾏カバレッジが取得できました!! 動的な実⾏カバレッジ(Part 1) 静的なメソッド解析(Part 2) メソッド単位の実⾏カバレッジ

Slide 21

Slide 21 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 21 プロダクションの実⾏実績がメソッド名で⻑期間追跡できるようになった timestamp: 計測⽇ path: ファイル名 method: メソッド名 executed: 実⾏実績 • true:実⾏された • false:実⾏されてない 取得したメソッド単位の実⾏カバレッジは BigQuery に投⼊してあり、計測⽇、ファイル名、メソッド名など 任意の条件で検索できます。

Slide 22

Slide 22 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 22 不要なメソッド⼀覧を出⼒できる ファイル名と期間だけ指定して特定ファイルをローラー作戦で徹底的にお掃除するためのターゲットリスト を取得する使い⽅もできます。

Slide 23

Slide 23 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 23 静的解析のちょっとだけツッコんだ詳細

Slide 24

Slide 24 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 24 ポイント1. MRI の RubyVM と同⼀の解析結果が得られるパーザを採⽤ ⾷べログは MRI(Matz Ruby Implementation)で運⽤しているため、前述のように AST 変換には RubyVM::AST を採⽤しています。RubyVM::AST は API が安定していないという⽋点もありますが、RubyVM ⾃⾝と構⽂解析仕様が完全に同⼀なので、解析結果が100%信頼できます。Ruby のカバレッジ可視化ツールと して標準的な Coverband でも利⽤されています。 もっと扱いやすい Parser, Ripper, Prism などのパーザもありますが、エッジケースで実際の RubyVM と挙動 が異なります。例えば `def f = ..a ..` というコードは MRI では SyntaxError になり、RubyVM::AST でも SyntaxError と判断されますが…… 参考: Prism accepts some expression in range_node.left that Ruby rejects when the code includes `..a..` · Issue #2021 · ruby/prism

Slide 25

Slide 25 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 25 他のパーザは(まだ)MRI の RubyVM と100%の互換性がない 参考: Prism accepts some expression in range_node.left that Ruby rejects when the code includes `..a..` · Issue #2021 · ruby/prism Prism でパースするとワーニングは出るものの SyntaxError と判定されません。 これらのパーザには、例えば Prism はエラー耐性が⾼く、編集中の⼀時的に Syntax が正しくないコードを解 析することが多い LSP(Language Server Protocol) に適していると⾔った特性・特⻑があり、適材適所と⾔え ます。パーザは今年の RubyKaigi のセッションで最も多かったテーマなので、興味のある⽅は RubyKaigi の 動画を観るとおもしろいでしょう!

Slide 26

Slide 26 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 26 ポイント 2. 名前空間などが解析できるメソッドスキャナーを⾃作 Coverband 付属メソッドスキャナの解析結果 ⭐1: 名前空間がわからない ⭐2: 同じクラスに同じ名前のクラスメソッドと インスタンスメソッドがあると区別がつかない ⭐3: 特異メソッドのショートハンド `def self.method_name` が解析できない 前出の Coverband にもコード解析機能(メソッドスキャナ)がありますが、⾷べログにちょくちょくある性質の コードが解析できない問題がありました。

Slide 27

Slide 27 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 27 ⾷べログにちょくちょくある性質のコードも解析できるようになった ⾷べログ版メソッドスキャナの解析結果 ⭐1: 名前空間がわかる ⭐2: クラスメソッドとインスタンスメソッドが判別 できる ⭐3: 特異メソッドのショートハンドが解析できる AST にはモジュール、クラスなどの情報も含まれているのでメソッドスキャナを⾃作して解決できました。

Slide 28

Slide 28 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 28 まとめ

Slide 29

Slide 29 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 29 デッドコード削減基盤の成果と課題 成果 • ⾷べログのアプリ向けAPIのお掃除では、調査/削除に専任1名の体制で1ヶ⽉に約300のデッドメ ソッドを削除できました。障害もレビュー指摘の不備による軽微な1件だけでした。(プロダク ションのデータや利⽤実態によって通っていないコードもあり得るので、削除前にコードリー ディングが必要です) • リファクタリングに備えたお掃除の他に不要機能のお掃除などにも活⽤されています 課題 • ⻑いメソッドの途中にあるコードを消そうとすると、ファイルの編集で変わりやすい⾏番号単位 の実⾏実績しか⼿がかりにできないため、判断が難しくなります。不要と⾒当をつけたコードを 別のメソッドに切り出して、メソッド名で追跡できるようにしてから判断するのも⼿だと思いま す。 (書籍リファクタリング第2版における「関数の抽出」パターンです。) • メソッド定義開始⾏は常に実⾏扱いになるため `def foo; nil; end` のようなメソッド記法は正し く判定できません。また後置 if/unless も似た理由で正しく判定ができません

Slide 30

Slide 30 text

CONFIDENTIAL © Kakaku.com Inc. All Rights Reserved. 30 ⾷べログではエンジニア絶賛募集中です! ⾷べログでは今回ご紹介したデッドコード解析基盤などを開発するマイクロサービス基盤エンジニア の他、多くのポジションでエンジニアを募集中です。 ご興味のある⽅は、ぜひ採⽤サイトからご連絡をください。エンジニア組織の⽂化⾵⼟などを⾯接前 に知りたい⽅は、本採⽤プロセス前にカジュアルな⾯談も可能です。フリーコメント欄に「カジュア ル⾯談希望」とご記載ください。ご応募お待ちしております! ϚΠΫϩαʔϏεج൫ΤϯδχΞ ϚΠΫϩαʔϏεԽνʔϜ ΤϯδχΞϦϯάϚωʔδϟʔީิ ϞδϡϥϞϊϦεΞϓϦέʔγϣϯԽΤϯδχΞ