LINEヤフー Tech Blog

LINEヤフー株式会社のサービスを支える、技術・開発文化を発信しています。

LINE iOSアプリにおけるローカルビルドメトリクス基盤の構築と活用

こんにちは!モバイルデベロッパーエクスペリエンスチームの@giginetです!普段はLINEアプリの基盤改善をしています。

LINE iOSは巨大なプロジェクトです。日々、開発者が多くの時間をビルドに費やしています。これまでビルド時間はCIの実行時間などで一部計測されていましたが、開発者が日常的にどれぐらいビルド待ちを体感しているのか推し量るのは容易ではありません。

この記事では、LINE iOSで開発者のビルド時間を集計するローカルビルドメトリクス基盤を構築し、開発者生産性の可視化、改善に活用している事例をいくつか紹介します。

実際に運用されているローカルビルドダッシュボード

なぜローカルビルドメトリクスが必要なのか

本稿では、開発者のローカルマシンでビルドした際の詳細なログをローカルビルドメトリクスと呼びます。CIで実行したログも含めて集計していますが、これらはあとから区別が付くようになっています。

これまでのメトリクスは、CIの実行時間を単に時系列データとして保存するだけでした。これでは、開発時のローカルでのビルド時間を正確に把握できなかったり、ビルドの各ステップ毎の詳細な時間を把握できないなどの課題がありました。

なぜわざわざこのような基盤を新たに作るのでしょうか。以下のような理由が挙げられます。

  • 開発者の体感に近い状況を捉えるため
  • ボトルネックの課題発見のため
  • 仕事の成果をアピールするため
  • 障害やデグレーションを発見するため

これらの項目について詳しく見ていきましょう。

開発者の体感に近い状況を捉えるため

開発者が日常的に体感しているビルド時間は、CIの実行時間だけでは正確に捉えることができません。例えば、開発者は多くの場合はインクリメンタルビルドを頻繁に行いますが、CIではクリーンビルドが前提になっています。

アプリ開発時は修正とインクリメンタルビルドを繰り返すため、インクリメンタルビルドの速度が開発者の体験に直結します。

ボトルネックの課題発見のため

詳細なビルドログは開発者生産性を上げるための課題発見に役立ちます。例えば、ビルドのうち特定のステップに時間がかかっていたり、失敗率が高い場合、その部分の最適化は効果が高いことが把握できます。

仕事の成果をアピールするため

ビルド環境の改善にはベンチマークが不可欠です。これまで、効果のあるような改善施策を行っても、その効果や成果を示すことが難しいことがありました。詳細なビルドメトリクスは、ビルド成功率やビルド時間などを通して、仕事の成果を定量的にアピールする手段となります。

障害やデグレーションを発見するため

これまで、ある変更を入れた際にローカルのビルド環境に速度低下や失敗率増加の問題が起きた場合、開発者からの問い合わせを受けるまで把握することができませんでした。ローカルビルドを継続的に監視することで、新しい変更に問題があった時に即座に発見できるようになります。

LINE iOSのビルドプロセス - Prebuilt TasksとXcode Build

まず最初に、現在のLINE iOSのビルドプロセスを見てみましょう。大きくPrebuilt TasksXcode Buildの2つのフェーズにわかれています。

現在のLINE iOSのビルドプロセス

Prebuilt TasksフェーズはLINE iOSのビルドの前に下準備をするスクリプトの総称です。複数のShell ScriptやSwift製のCLIツールで構成されています。

その後のXcode Buildフェーズは、通常のXcodeによるWorkspaceのビルドです。

それぞれのフェーズは、さらに細かなステップに分かれています。例えばPrebuilt Tasksは依存関係の取得や、コード生成、Xcodeプロジェクトの生成などに分割することができます。

詳しいビルドシステムについては、iOSDC Japan 2023の『Swift Packageを使った巨大な依存グラフのキャッシュ戦略』というセッションで紹介しています(当時から大分状況は変わりましたが……)。

新しいビルドメトリクスには、これらのフェーズ内の各ステップ毎の実行時間や成功率、失敗原因を細かく分析できることが求められました。これを実現するためには、ビルドログを細かく見ていく仕組みが必要そうです。

Prebuilt TasksはShell Scriptを中心に構成されているため、各ステップの実行を計測するラッパースクリプトを導入してビルド時間の計測を実現しています。一方で、Xcode Buildの詳細なログの解析はどのようにすればよいでしょうか。

Xcodeのビルドログを読む - XCActivityLogの解析

ここで分析するのはXcode Buildの最終成果物として生成される*.xcactivitylogというファイルです。これはXcodeのビルドログを構造化したもので、実体はバイナリをgzipで圧縮したものです。

ファイルフォーマットは公式には非公開ですが、XCActivityLogをパースする手段として、XCLogParserが知られていて、今回もこれを利用しています。

XCActivityLogをパースすると、例えば以下のようなログが得られます。

[
  {
    "errors" : [

    ],
    "signature" : "Build XCActivityLogPlayground",
    "errorCount" : 0,
    "startTimestamp" : 1760685017.641521,
    "endTimestamp" : 1760685017.816908,
    "compilationEndTimestamp" : 1760685017.641521,
    "domain" : "Xcode.IDEActivityLogDomainType.BuildLog",
    "warnings" : [

    ],
    "buildIdentifier" : "hostname_F1CDD235-F3AA-4208-9FDA-8A1F341CE523",
    "duration" : 0.17538702487945557,
    "parentIdentifier" : "",
    "subSteps" : [

    ],
    "warningCount" : 1,
    "fetchedFromCache" : false,
    "identifier" : "hostname_F1CDD235-F3AA-4208-9FDA-8A1F341CE523_1",
    "architecture" : "",
    "startDate" : "2025-10-17T07:10:17.642000Z",
    "schema" : "XCActivityLogPlayground",
    "documentURL" : "",
    "machineName" : "hostname",
    "title" : "Build XCActivityLogPlayground",
    "notes" : [

    ],
    "endDate" : "2025-10-17T07:10:17.817000Z",
    "detailStepType" : "none",
    "compilationDuration" : 0,
    "type" : "main",
    "buildStatus" : "succeeded"
  },
  ...
]

このログを解析・集計することで、細かなビルドの実行時間を見ることができそうです。

しかし、LINE iOSの場合、クリーンビルド成功時にはこのファイルは約20〜30MB程度の大きさになり、ローカルマシンでパースすると30秒から1分程度かかることもありました。ビルド時間を計測するために、ローカルビルドを長時間ブロックしてしまうことは許容できません。

ローカルビルドログ収集基盤のアーキテクチャ

そこで、この問題を解決するために、今回は以下のようなビルドメトリクス収集基盤を設計しました。

安定で高速な基盤のための設計指針

第一に、前述の通り、ビルドメトリクスの集計のためにビルドが不安定になったり、余計にビルド時間が延びてしまっては本末転倒です。時間のかかる処理は極力クライアントで行わない方針で設計しました。

また、ログの送信時の失敗も許容するべきです。ビルドプロセス全体を止めてしまわないようにしつつ、ログの欠損も極力避ける工夫が必要です。

ビルドメトリクス基盤の全体像 - 重い処理はサーバーサイドで行う

これらの点を考慮して、以下の図のようなアーキテクチャが産み出されました。

ビルドログ収集基盤の全体像

ポイントは、クライアントでの処理は計測と送信だけに留め、ほとんどをサーバーサイドで処理することです。

システム全体はRecorder、Collector、Batchの3つで構成されていて、これらはSwiftによるモノレポで開発されています。これにより、ログデータのモデルなどを共通化し、互換性を維持しやすくしています。

1. Recorder - ローカルビルドのデータを送信する

Recorderは各開発者に配られ、ビルド時に実行されます。Prebuilt Tasksの各フェーズの実行時間を計ったり、Xcodeビルドの成果物(*.xcactivitylog)をログエンドポイントに送信します。

Recorder自体のビルド時間がかかってしまわないように、RecorderのバイナリはCIで作成し、開発者にnestというツールを使って配布しています。

また、オフライン環境でビルドしているときに備え、送信に失敗したログをバッファリングし、次回再送信する仕組みも備えています。

2. Collector - ビルドログが送られてくるエントリーポイント

CollectorはRecorderから送信されたビルドログを受け取るエントリーポイントです。ポイントは、責務を受け取ったデータをそのままObject Storageに保存することだけに限定している点です。

ログの解析や集計を送信時に行わないことで失敗の可能性を減らし安定性を高めています。また、ログを一度保存することで、バッチの失敗時にリトライが可能になるという利点もあります。

特徴として、Hummingbirdを使いサーバーサイドSwiftとして実装されています。興味本位での技術選定だったのですが、受け取ったファイルをObject Storageに保存するだけという用途では、サーバーサイドSwiftの採用は重厚すぎた印象です。

3. Batch - ビルドログのパースとDBへの格納

Batchは、Object Storageに保存されたビルドログをXCLogParserで定期的にパースし、MySQLに格納するバッチ処理です。このバッチジョブはGitHub Actions上で実行されています。LINE iOSのXcodeのログパースは時間がかかるため、Swift Concurrencyを活用した並行処理で高速化を図っています。

ビルドメトリクスを図示・解釈する

これによって、様々なデータをクエリできる環境が整いました!集めたデータは内製のBIツールであるIU OASISを使い、ダッシュボードとして可視化しています。

ここからは、集めたデータをどのように分析、活用しているかを紹介します。

ビルドメトリクスの定義と可視化

ただ大量の計測データを集めても、解析が伴わなければ効果は薄いです。メトリクスの可視化の肝は、どのような状態を想定して、どのような指標を定義するかにあります。例えば、以下のような意図でそれぞれの指標を設計しています。

  • Prebuilt Tasksの実行回数は少ないべきなので、必要以上に実行されていないか把握したい
    • 開発者ごとのPrebuilt Tasksの実行回数
  • 各ステップにまだ高速化の余地があるため、将来的なマイグレーション効果がどれぐらいかトラックしたい
    • Prebuilt Tasksの各ステップ毎の平均実行時間
  • 何も変更を加えていないのにビルドが壊れたり、ビルドエラーの解消を行うのは生産性を下げるため、失敗率を低く保ちたい
    • ローカルビルドの失敗率
  • 開発者が1日にどれぐらいビルドに時間を取られているか把握したい
    • 開発者一人当たりの1日の合計ビルド時間
  • クリーンビルドはなるべく発生しない方がよいので、インクリメンタルビルドの割合を増やしていきたい
    • ローカルビルドのうち、インクリメンタルビルドの割合

このように、一つずつのメトリクスに対して、仮説やストーリーを明確にし、定義・観測していくことが肝要です。

ビルドメトリクスの読み取りと解釈

値の読み取りには注意が必要です。例えば、「1日の開発者ごとの合計ビルド時間」といった指標は、開発生産性を考える上でどのような意味を持つでしょうか?

  • 「1日の開発者ごとのビルド時間合計」が減れば、ビルドの待ち時間が短くなり、開発者の生産性が向上したと言えそう
  • しかし、単に他の要因で開発自体が停滞してしまい、開発に使える時間が減っているだけかもしれない
  • 逆に「1日の開発者ごとのビルド回数」が増えていたとしても、1回辺りのビルド時間が短くなっていた場合、イテレーションサイクルが上がり、結果的には生産性が高まったのかもしれない
  • そもそもXcode PreviewsのようなIDE機能の充実により、ビルド自体をしなくても開発できるようになったのでビルドされていない可能性もある
  • コーディングエージェントの普及により、ビルド待ちと修正が完全に自動化できたため、そもそもビルド待ち時間自体があまり生産性に直結しなくなっているかも

このように、1つのメトリクスだけを見て、生産性の善し悪しを判断するのは危険です。どのような状態でその値の増減が起きうるか仮説を立て、複合的な指標で生産性を判断することが重要です。

LINEアプリ開発では、ビルドメトリクスのみならず、開発者のPR数など複合的な生産性指標を組み合わせて、ビルド時間の生産性への影響を分析しています。

実際のビルドメトリクス活用事例

これらのメトリクスを読み解くことで、LINE iOSの開発者生産性を上げるための様々な施策に応用することができました。実際の事例をいくつか見ていきましょう。

開発時のモジュールビルド利用率の把握

毎回アプリ全体をビルドするのは時間がかかります。そのため、変更したモジュールだけをビルドするビルドスキームの利用率を高めることで、全体の生産性を底上げできると考えました。

Xcodeのビルドスキーム

実際に開発者が日常的に使用しているビルドスキームを分析すると、現在は全ビルドの約2割がモジュール単体でのビルドでした。

Xcode上のローカルビルドスキーム

この基盤ができるまでは、開発者がどのビルドスキームを使って開発イテレーションを回しているか把握することができませんでした。現在は、全体のローカルビルド実行のうち、モジュールビルドの割合を可視化することができているため、モジュール分割の効果を計りやすくなっています。

依存ライブラリビルド処理のデグレーション検知と改善

Prebuilt Tasksフェーズのうち、あるタイミング(8月末)でパッケージ依存の解決方法のメンテナンス性を高める変更をしたところ、目に見えて速度が低下してしまったことが確認できました(図の折れ線グラフが変更前後の中央値)。この変更の影響を検知することができたため、依存解決にキャッシュを導入するなど、改善施策を検討することができました。

依存解決方法の再実装によるビルドプロセスへの影響

このように、ローカルビルドメトリクスは、変更による問題発見、またその修正の効果測定の両面に活用できます。

Xcode 26からのExplicitly Built Modulesの影響測定

Explicitly Built Modulesは、Xcode 26からデフォルトで有効化された新しいビルドオプションです。

この機能自体は、依存ツリーの定義が厳密化される。デバッガが高速化するなどの恩恵も大きいのですが、一方で、LINE iOSのビルド環境においてはXcode 26への移行のタイミングでビルドパフォーマンスの低下が見受けられました。

そのため、PR Builderのみでこの機能をオフにすることで、開発環境での恩恵は受けつつもPRビルドの待ち時間を改善できました。

以下の青いグラフは、Xcode 26導入以前(10月上旬)、Xcode 26のデフォルト設定(11月中旬以前)、Explicitly Built Modulesを無効にした場合(11月中旬以降)の3つの状態でのビルド時間の傾向です。オプションを無効にした11月中旬以降は、ビルド時間が改善していることがわかります。

Explicitly Built ModulesをPR Builderのみで無効にした

マシンチップ毎のビルド時間を利用したマシンリプレースの検証

ビルドメトリクスには、開発者のホスト名やチップ、メモリの状態といったメタデータも含んでいます。これにより、ローカルビルドの値を集計させることで、マシン置き換えの効果測定を行うこともできました。

例えば、Apple Silicon M1 MaxのマシンをM4 Maxにリプレースしたことで、統計的に1.25倍(約4分)程度ビルドが高速化できました。

マシンチップごとのビルド時間の比較

おわりに - 開発者生産性についてもっと知りたい方は

今回はローカルビルドメトリクス基盤を構築し、ビルド時間を通した開発者生産性の可視化と改善に取り組んだ事例を紹介しました。

さて、筆者は2024年11月に『エンジニアチームの生産性の高め方』という書籍を出版しました。その第7章「開発基盤の改善と開発者生産性の向上」において、今回触れたような開発者生産指標の定義と、その改善の為のプロセスについて触れています。

エンジニアチームの生産性の高め方 | 技術評論社

開発者生産性の読み解き方について、ご興味のある方は手に取ってみてください!