エンジニア面接で「開発力」を見るために聞いていること

こんにちは!AnotherBall Avvy事業部でテックリード 兼 技術責任者をやっている @fortkle です。普段はプロダクト開発をしつつ、サーバーサイドエンジニアの一次面接も担当しています。

最近、その面接で使っている質問の聞き方を少し変えました。大きく面接プロセスを変えたわけではなく、これまで一つの設計に関する質問として聞いていた内容を、いくつかの段階に分けた程度です。

ただ、なぜ質問を分けたのかを振り返ってみると、私たちがエンジニアの「開発力」として何を見ようとしているのかが、よく現れていることに気づきました。この記事では、実際に面接で使っている質問を一例として紹介しながら、その背景にある考え方について書いてみます。

面接の中で、プロダクト開発を少しやってみる

サーバーサイドエンジニアの一次面接では、Avvyで実際に起こりそうなプロダクト開発を題材に、候補者の方と会話する時間を設けています。私がPdM役となって、まだ詳しい仕様が決まっていない相談を持ちかけ、候補者の方にはAnotherBallのサーバーサイドエンジニアとして考えてもらいます。

面接の途中で、次のように伝えます。

ここからは、私がAvvyのPdM役として相談します。
AnotherBallのエンジニアとして、必要なことを自由に質問してください。

質問には私がPdM役として回答します。候補者の方には、その回答を受けてさらに質問を深めたり、要求や前提を整理したりしながら、何を作るのかを具体化し、技術的な設計へ進んでもらいます。面接の中で短時間だけ、AnotherBallでのプロダクト開発を一緒に進めてみるような形式です。

例えば、以下のような題材です。

  • イベント期間中にギフトランキングと現在の順位をリアルタイムで表示したい
    • Avvyでは、ユーザーは配信者のライブ配信を視聴しながら、配信中にギフトを送れます
    • アプリ内では定期的にイベントが開催されており、期間中の応援(ギフトなど)により獲得したポイントをもとに、他のユーザーとランキングを競います
  • 一定の条件を満たしたユーザーだけが参加できるイベントを作りたい
    • 例えば「一定期間内に初配信をした」「コインの獲得数が一定以上」といった条件のイメージです
  • 配信できない日でもリーグ降格を防げる仕組みを作りたい
    • Avvyには配信者が参加する「リーグ」というランクに似た仕組みがあり、日々の配信の成果に応じてリーグが上がったり下がったりします

Avvyの現在の実装や、私たちが採用しているアーキテクチャを当ててもらいたいわけではありません。曖昧な相談を受けたときに何を質問するのか、PdMから返ってきた回答をどう解釈するのか、最初に考えていた案を会話の中でどう更新していくのか。そういったことを知りたいと考えています。

今回は、この中から「イベント期間中にギフトランキングと現在の順位をリアルタイムで表示したい」という題材を例に説明します。

「ギフトが送られたら、ランキングへリアルタイムに反映したい」

今回の例では、イベント期間中に送ったギフトをもとに、ユーザー同士でランキングを競うものとします。ここで、PdMとして次のような相談をします。

ギフトが送られたら、ランキングへリアルタイムに反映したいです。

まだ詳しい仕様は決まっていません。この相談を受けて、あなたがAnotherBallのサーバーサイドエンジニアだったら、どのように進めるでしょうか。

一つの質問で見ようとしていたものが多すぎた

以前は、この相談を伝えたあと、そのまま「どのように進めますか?」「どのように設計しますか?」と聞いていました。実際のプロダクト開発でも、雑談ベースで整理されていない相談から始まることもあります。

一方で、以前の聞き方では、面接官から最初に共有する情報と、候補者に質問を通じて引き出してほしい情報が整理できていませんでした。さらに、その一連の回答から、要求を整理・理解する力、技術的な設計力、異常系への対応力といった複数の観点をまとめて評価しようとしていました。

そのため、候補者の回答に考慮が足りない部分があったときも、候補者が必要な前提を確認できなかったのか、面接官の説明が不足していたのか、あるいは設計力そのものに課題があったのかを切り分けにくい状態でした。

これらを整理しないまま質問すると、「情報が足りない問題を自由に解いてください」とお願いすることになります。

例えば、相談を聞いてすぐにRedisやWebSocketを使った設計の話を始めた人がいたとします。それは要求を整理する力が不足しているのでしょうか。それとも、面接官から設計問題を出されたため、すでに仕様は決まっているものとして回答しただけなのでしょうか。

反対に、細かな仕様をたくさん質問できたとしても、確認事項の数が多ければよいわけではありません。PdMから返ってきた回答を受けても次の質問や設計が変わらないのであれば、実際のプロダクト開発で行う会話とは少し異なります。

考えてみると、この質問では大きく三つのことを見ようとしていました。

  1. 曖昧な相談から、背景にある要求や必要な条件を整理できるか
  2. 整理された条件をもとに、技術的な設計を考えられるか
  3. 欠落や重複など異常が起きても、あとから回復できるように考えられるか

それならば、一つの大きな質問ですべてを見ようとせず、段階を分けた方がよいのではないかと考えました。

そこで、質問を3段階に分けた

現在は、同じ題材を使いながら、質問を大きく三つの段階に分けています。

1. まず、PdMに何を確認しますか?

最初は、技術的な設計をすぐには求めません。

PdMから「ギフトが送られたら、ランキングへリアルタイムに反映したい」と相談されました。
まず、PdMに何を確認しますか?PdM役の私に自由に質問してください。

ここでは、まだ曖昧な「リアルタイムランキング」という言葉から、設計に必要な条件を整理しようとするかを見ています。例えば、次のようなことが会話の中で論点になります。

  • なぜランキングをリアルタイムに更新したいのか
  • 誰に、どの画面で見せたいのか
  • どの程度の遅延までを「リアルタイム」と考えるのか
  • 画面上の一時的なズレは許容できるのか
  • 最終順位にも同じ即時性が必要なのか

ただし、これらをすべて質問できたかどうかを、チェックリスト形式で採点しているわけではありません。PdM役から返ってきた回答を受けてさらに質問を深められるか、最初に持っていた仮説を更新できるかといったことも含めて対話します。

2. 共通の条件を渡して、設計してもらう

要求や前提についてある程度会話したあと、面接官から共通の条件を伝えます。例えば、以下のような条件です。

  • ランキングへの反映は10〜30秒程度遅れてもよい
  • 配信中の画面では自分の順位を表示する
  • イベントページのランキング画面では上位100人の順位を表示する
  • イベント終了直前にはギフト送信が大きく増える可能性がある
  • 画面上の順位には一時的なズレを許容する
  • 最終順位は報酬に使うため、正確でなければならない

そのうえで、次のように聞きます。

この条件であれば、サーバー側をどのように設計しますか?

ここで候補者ごとに共通の条件を渡しているのは、要求を整理する力と、技術的な設計力を少し分けて見たいからです。自分で聞き出した情報だけをもとに設計してもらうと、質問によって得られた情報量の違いが、そのまま設計問題の難易度の違いになってしまいます。

そのため、第1段階ではPdMとの会話を見たうえで、第2段階では同じ条件を渡し、その条件に対してどのような設計を考えるのかを会話します。ここでは、Redisや特定のクラウドサービスの名前を答えられること自体を評価したいわけではありません。

事実として確定したギフトの記録はどこに、どのように残すのか。表示用のランキングをどのように作るのか。画面表示と最終順位の確定を分けるのか。条件に対して、なぜその設計を選んだのかを聞いていきます。

3. 最後に、一つだけ失敗を起こす

正常系の設計について話したあと、最後に一つだけ失敗する条件を加えます。

ギフトの送信処理には成功しましたが、ランキングへの反映処理に失敗しました。
このような状況が発生する場合に備えてどのような点を考慮すべきですか?

ここでは、ギフトの記録と、画面に表示するランキングを分けて考えられるか等を見ます。他にも失敗した処理を再実行できるか、同じ処理が複数回動いても二重に反映されないか、正式な記録からランキングを再計算できるか、といったことを会話します。

正式な記録と派生データを分ける考え方や、再実行と冪等性の扱いなど、書きたいことは別にありますが、長くなってしまうためここでは概要にとどめます。

実際のシステムでは、すべての処理が毎回予定どおりに完了するとは限りません。途中で失敗してもデータを失わず、あとから回復できるように考えることも、サーバーサイドエンジニアの開発力の一つだと思っています。

なぜ最初に「まず何を確認しますか?」と聞くのか

ここまでのうち、今回特に書きたいのは、最初に「まず、PdMに何を確認しますか?」と聞いていることです。完成した要件をもとに技術設計ができることはもちろん重要ですが、実際のプロダクト開発では、最初から「表示は10秒以内」「一時的な不整合は許容する」「最終順位は正確にする」といった要件がきれいに整理されているとは限りません。

最初に出てくるのは、

リアルタイムにランキングを更新したい

という、まだ背景や目的が十分に言語化されていない相談であることも多いです。そこからPdMと会話し、何を実現したいのかを理解し、技術的な条件へ具体化していく。私たちは、そのプロセスも含めてエンジニアの「開発力」だと考えています。

要件は、会話の中で具体化される

以前、個人ブログで「要件ではなく要求を正しく理解するために」という記事を書きました。その記事でも触れましたが、私は「要求」と「要件」を次のように分けて考えています。

  • 要求:ユーザーや事業に、どのような状態や価値を実現したいのか
  • 要件:その要求を実現するために、システムが満たす具体的な条件

今回の「ギフトが送られたら、ランキングへリアルタイムに反映したい」という相談は、一見すると具体的な要件のように見えます。特に「リアルタイム」という言葉が入ることで、「リアルタイムなランキングをどう実装するか」という技術的な問題に見えやすくなります。

しかし、この時点では「リアルタイム」が何秒程度を指すのかも、なぜリアルタイムにしたいのかも分かっていません。もしかすると、PdMが実現したいことは次のようなことかもしれません。

ギフトを送った結果がすぐに順位へ反映されることで、イベントの盛り上がりを感じてほしい

この要求が分かって初めて、1秒以内に反映されなければ体験が成立しないのか、10秒や30秒程度の遅れなら許容できるのか、一時的な順位のズレを許容できるのか、最終順位にはどの程度の正確性が必要なのか、といった会話ができます。

つまり、「リアルタイムランキング」という言葉には、要求を実現するための解決策がすでに少し混ざっているのだと思います。

その要件は、本当に必要条件なのか

アジャイル開発の入門書としてよく読まれている『アジャイルサムライ』を読んだときに印象に残ったのが、「要件」という言葉によって、それが最初から必要不可欠で、交渉できないもののように扱われやすくなる、という話でした。

例えば「リアルタイムであること」をそのまま固定された要件として受け取ると、そこから先はリアルタイムな仕組みをどう作るか、という話になります。一方で、「なぜリアルタイムにしたいのか」まで戻ることができれば、要求を満たすために本当に守るべき条件を考えられます。

会話した結果として、1秒以内の反映が重要だと分かることもあると思います。その場合は当然、それを要件として設計します。しかし、10〜30秒程度の遅延でもユーザー体験を満たせるのであれば、集計やクライアントへの反映方法には、より多くの選択肢が生まれます。負荷やコストを抑えながら、十分な体験を提供できるかもしれません。

要求を理解することは、単にユーザーに寄り添うためだけのものではありません。何を守り、何を緩められるのかが分かることで、技術的な選択肢が増え、より適切なトレードオフを選べるようになります。

要求と要件の間には、会話がある

今回の例を整理すると、次のような流れになります。

ここで大切なのは、要求を聞いたあと、エンジニアが一人で要件を考えることではありません。PdMは、どのようなユーザー体験や事業上の成果を実現したいのかを知っています。一方でエンジニアは、条件によって技術的な難易度や負荷、コストがどう変わるのかを知っています。それぞれが持っている情報を会話に持ち寄ることで、要求を満たしながら実現可能な要件を一緒に考えられます。

こうした共通理解を作る方法の一つとして、Avvyの開発では、新しい機能や体験を考えるときにユーザーストーリーマッピングを使うことがあります。ユーザーがどのような流れでAvvyを使うのかを並べながら、PdM、デザイナー、エンジニアで話し、どの体験が重要なのか、今回はどこまで作るのかを整理します。

ユーザーストーリーマッピングを提唱したJeff Pattonの書籍『ユーザーストーリーマッピング』から学んだことの一つは、完成された仕様をきれいに書いて受け渡すこと以上に、ストーリーをきっかけに会話し、チームで共通理解を作ることが大切だということでした。完成したマップそのものにも価値はありますが、個人的には、マップを作る過程で会話が生まれ、チームの共通理解が作られることに大きな価値を感じています。

面接で行っているやり取りも、実際のプロダクト開発をそのまま再現できるものではありません。それでも、PdMから完成した要件を一方的に渡すのではなく、曖昧な相談から会話を始めることで、普段の開発で大切にしているプロセスを少しだけ一緒に体験できます。

振り返ってみると、面接の最初に「まず、PdMに何を確認しますか?」と聞くことで見たかったのも、まさにこの会話だったのだと思います。

おわりに

エンジニアの開発力というと、プログラミング能力や、データベース、インフラ、アーキテクチャなどの知識を思い浮かべることが多いと思います。もちろん、これらはすべて重要ですし、今回紹介した質問でも、要求や前提を整理したあとは技術的な設計や失敗時の挙動について詳しく会話します。

一方で、実際のプロダクト開発は、完成した要件を受け取るところから始まるとは限りません。まだ曖昧な相談からPdMやデザイナーと会話し、ユーザーに届けたい価値を理解したうえで、技術的な制約や選択肢を伝えながら何を作るのかを具体化していく。そうしたプロセスも含めて、私たちが面接で見たい「開発力」なのだと思います。

これは、エンジニアがPdMの代わりにすべてを決めるという話ではありません。PdMが持っているユーザーや事業への理解と、エンジニアが持っている技術的な知識を、会話の中に持ち寄るということです。

この記事を読んだうえで面接に来ていただいて構いません。題材は面接ごとに変わりますし、見たいのは正解を知っているかではなく、PdM役の回答を受けて質問や設計をどう変えるかです。事前に考えてきていただけるなら、その分だけ会話は深くなると思っています。

もちろん、この形式にも課題はあります。その場で考えを言葉にするのが得意な人に有利で、時間をかけてじっくり考えるタイプの人にはつらい面があるかもしれません。口頭だけに頼らず、図やチャートを示しながら考えを共有できるようにするなど、進め方は改善していくつもりです。

面接の中で短時間だけ、AnotherBallでのプロダクト開発を一緒にやってみる。今後も実際の面接を通じてお互いのことを知る時間にしていきたいです。

We’re Hiring!

AnotherBallでは、プロダクトの要求を理解し、職種を越えて会話しながら、一緒に形にしていくエンジニアを募集しています。Avvyのプロダクト開発に興味を持っていただけた方は、ぜひ採用情報をご覧ください!

AnotherBall Careers

参考書籍

  • Jonathan Rasmusson『アジャイルサムライ――達人開発者への道』オーム社
  • Jeff Patton『ユーザーストーリーマッピング』オライリー・ジャパン

AnotherBallはDroidKaigi 2026にゴールドスポンサーとして協賛します!

こんにちは!AnotherBall のモバイルエンジニアチームです。

AnotherBall は DroidKaigi 2026 にゴールドスポンサーとして協賛します!

ブースも出します!Avvyでアバターをつくって、その場でチェキにして持ち帰れる体験を用意しました。そして9月3日の12:20から、モバイルチームのRIO(@rioX432)がMeerkatで登壇します。

開催概要

  • 公式サイト: DroidKaigi 2026
  • 日程: 2026年9月1日(火)〜9月3日(木)
  • 会場: ベルサール渋谷ガーデン(〒150-0036 東京都渋谷区南平台町16-17 住友不動産渋谷ガーデンタワー 1F・B1)
  • AnotherBallブースの出展日: 9月2日(水)・9月3日(木)

Avvyというライブ配信アプリをつくっています

私たちが開発している Avvy は、誰でもスマホ一つで2Dアバターを作成でき、顔出しなし・機材なしでライブ配信を始められる、VTuberアバター作成・配信アプリです。2Dイラストパーツを組み合わせてオリジナルのアバターをつくり、スマートフォンのカメラで顔の動きや表情をトラッキングして動かせます。視聴者は配信中にギフトを送って配信者を応援できます。

Avvyのアバター作成画面と配信画面(Avvy公式サイトより)

Android側の構成はこうです。ビジネスロジックはKotlin Multiplatform(KMP)でiOSと共有し、UIはJetpack Compose。アバターの描画はUnity as a Library(UaaL)が担当し、フェイストラッキングにはMediaPipeを使っています。ゲームエンジンとネイティブUIが1つのアプリに同居しているので、普通のアプリなら踏まない場所でよく詰まります。

カンファレンスへの協賛は、去年の Kotlin Fest 2025 でシルバースポンサーを務めて以来です。

9月3日12:20、Meerkatで登壇します

Androidのフェイストラッキングで実際に躓いたことについて、弊社のRIOが説明します。

フェイストラッキングの精度は、アバターの表情のクオリティにそのまま出ます。配信者が笑ったときに、画面の中のアバターも同じ顔で笑うか。ユーザー体験がここで決まるので、精度が足りないと分かった時点で引き返す道はありませんでした。

こんな方に向けた話になります。

  • MediaPipeをAndroidアプリで使っている、または導入を検討しているエンジニア
  • ML推論やセンサーデータの品質が端末ごとに変わる問題に直面している方
  • フェイストラッキングやモーションキャプチャに興味がある方
  • KMPでハードウェア依存の機能を抽象化するパターンを知りたい方

ブースでは、あなたのアバターをつくれます

ブースの中心は、Avvyでアバターをつくる体験です。

髪型、目、服装の順に選んでいくと、自分のアバターができあがります。カメラモードにして好きな表情で1枚撮ると、アバターも同じ表情になります。撮った写真を、DroidKaigi 2026オリジナルのデザインでその場でチェキにプリント。ケースに入れてネームカードに付けられます。

ブースで作れるチェキとホルダー、配布するAnotherBallのステッカー(イメージ)

ひととおりで5〜6分ほどかかります。時間のない方は、ステッカーだけ受け取っていただいても構いません。

スタッフは全員、自分のアバターのチェキをネームプレートに下げています。エンジニアもブースに立ちます。

技術の話をしに来てください

モバイルチームがこれまでに書いてきた記事です。

どれか一つでも引っかかるものがあれば、ブースで声をかけてください。ネイティブのアプリにUnityをどう組み込んでいるか、ライブ配信のリアルタイムな処理をどう作っているか。興味を持ってくれた方とそういう話ができるのが、カンファレンスに出る一番の楽しみです。

アバターをつくりに、あるいは技術の話をしに。9月2日と3日、ベルサール渋谷ガーデンでモバイルエンジニア一同お待ちしています!

We’re Hiring

AnotherBallでは、Kotlin・Unity・AIを活用したアプリ開発に興味のあるモバイルエンジニアを募集しています。KMPやCompose Multiplatformといった新しい技術を取り入れながら、プロダクトを一緒に成長させていける方だと嬉しいです。

気になる方は、ブースでスタッフに声をかけていただくか、下記のリンクからご応募ください!

隙のないチームは、越境でつくる ── AIで空いた時間の使いみち

隙のないチームは、越境でつくる

ハロー!AnotherBallでサーバーサイドエンジニアをやってるTsujiです。

普段はサーバーサイドの開発をやったり、インフラのアラートの深堀りからヤクの毛刈りを嗜んだりしています。

AI活用で生まれた余白をどうするの問題

エンジニアの皆さん、AI活用はどんな感じですか?

AnotherBallも例に漏れず、開発はもちろんそれ以外の場面でも全社的にAIを活用してます!

AI活用が進む“仕掛け” ── 会社をBAKUSOKU化した4つの取り組み

AI活用が進んだ先で思うことは、巷でよく言われる「人間は何をするべきなのか」ではないでしょうか。そしてさらによく聞くのが「利益に貢献する」とか、「本質的な価値を作る」などというものでしょうか。

これまでエンジニアチームの成果指標には、GoogleのDORAが提唱したFour Keys[1]が使われる事が多かったのではないかと思います。

DORAはGoogle Cloudが運営する研究プログラムで、「ソフトウェア開発チームが、速く・安定して価値を届けるには何が必要か」を継続的に調査しています。

しかし、先の話に照らすと果たしてFour KeysがAI時代に現役で有用な指標と言えるかは少し疑問に感じます。

実際、DORA公式も2023年から以下のようにいくつかの変遷を経ています。

  • 2023年、MTTRが failed deployment recovery time に改名・再定義
  • 2024年、5つ目の指標 deployment rework rate が追加され、指標は4つから5つに。群も Throughput / Instability へ再編
  • 2025年、レポート名も改称Accelerate State of DevOps ReportState of AI-assisted Software Development Report
  • 出典: DORA’s software delivery performance metrics[2] / A history of DORA’s software delivery metrics[3]

指標そのものが「どれだけ速いか」から「どれだけ壊れていないか」へ重心を移している、とも読めます。こうした変化を踏まえると、生まれた余白をさらに開発に費やすのが正解だとは言い切れない気がしてきます。

開発が速くなると価値の提供も速まるのか

ここで疑問の正体について自問自答したいと思います。

先に結論を述べると「開発が速くなっても価値の提供は速くならないから」だと考えています。

皆さんはTHE GOALを読んだことはありますか?名著なのでぜひ会社の経費で買ってもらってくださいね。

こちらの書籍ではTOC(Theory of Constraints)という概念が用いられており、「仕事の流れの中にある制約」に対処しなければ成果に繋がらない、ということを意味します。

つまり、「開発」や「個人」の速度があがったとしても、それが制約でないのであれば組織全体の成果には繋がらない、ということです。

開発だけ速くしても全体の流量は変わらない

またDORAの話に戻りますが、「局所最適しても全体の流量は改善しない」ことが公式のレポートを見てもわかります。

  • 2024年版:AI採用が25%増えると、デリバリのスループット −1.5%安定性 −7.2%
  • ところが同じ調査で個人レベルは軒並みプラス:生産性 +2.1% / フロー +2.6% など
  • 個人は速くなったのに、デリバリ全体は速くなっていない。THE GOALの主張と完全に同じ形
  • 2025年版:スループットとの関係は負→正に反転。ただし不安定性との関係は負のまま
  • 出典: Accelerate State of DevOps Report 2024[4] / State of AI-assisted Software Development 2025[5] / Announcing the 2025 DORA Report[6]

ただし、これらはサーベイをもとにした相関であって、因果を示すものではありません。それでも、個人の速度とデリバリ全体の速度がずれるという方向自体は、TOCの主張とよく一致しています。

そこでAvvy開発チームでは、スクラムを組み、ツールとしてLinearを使いつつ、独自のカンバンを用意することでボトルネックを発見できる仕組みを作っています。

チームの停滞と優先順位ズレに気づくカンバンをAIで作る ── Chrome拡張とAIコーディングによる可視化の実践

担当者アイコンのリングで滞留日数がわかるカンバン

ボトルネックは日々変動するものなので一概にどれ、というのは難しいですが、Avvy開発チームではPdMに難易度、優先度の高い業務が集中するきらいがありました。

PdMの職責の重さ、難しさ

PdMなどの肩書きは組織によって意味する責務が違う場合が多いと思います。

Avvy開発チームにおけるPdMは会社のビジョン、プロダクトの哲学や思想、その期の目標、あらゆる変数を意識しながら抽象的な施策を具体に落とし込むという非常に難しい職責を持っています。

ちなみに抽象度の話はこちらの書籍が非常にわかりやすいのでおすすめです。

賢さをつくる 頭はよくなる。よくなりたければ。

単純に考えれば、生まれた余白で手伝えばいいじゃん、となるわけですが、PdMの職責の難易度を考えるとそうも簡単に行きません。

また、仮に手伝えるとしても、主担当であるPdMがプロダクトや人の動きを把握できない状況を作るべきでもありません。

越境とその作法 ── 持ち場を空けない

AIが台頭する以前から常に感じていたことですが、職能横断のチームに大事なのは「職責は明確にする。職能は越える。」ことなのではないかと考えています。

ここでいう職能を越える、はよく「越境」と表現されているものと同じだと思ってください[7]

そうは言うものの、先に挙げたように越境も簡単なものではありません。そこで、Avvy開発チームにおいて最近自然と越境の場面が生まれたので、その事例の紹介をしたいと思います。

事の発端は、社内向けの管理画面を使っているメンバーから「管理画面の改善案をまとめたから意見がほしい」と声をかけてもらったことでした。

社内向けの管理画面のようなものは、優先度が低いというより、そもそもバックログに載せる機会がないことがほとんどです。誰かがEpicの形に整えて初めて、優先順位をつける土俵に乗る。裏を返せば、整える人が現れない限り永遠に始まらない領域とも言えます。

先ほど紹介したカンバンで見つけられるのは、あくまで載っているものの滞留です。載っていないものは、そもそも見えません。

個人的にも気にしていたところだったので、PdM1名と開発メンバー2名で内容を確認しました。

見てみると、要件と設計をもう少し詰めれば低コストで高い価値を出せそうな内容でした。そこで「これは開発側で要件をヒアリングして、スプリントに入れるものを提案しますね」という話に持っていくことができました。

これは体制としては「開発メンバーが状況を把握してEpicを整え、結論をPdMに返却し、実行まで移す」という構図になります。PdMから始まりPdMに返すという意味では、AIでいうところのSubAgentと同義です。

ここで重要なのは、

  1. 最終的な判断はPdMに残ったままであること
  2. PdMが把握した上で開発メンバーが動いていること
  3. 自分の職責は全うすること

ということです。

特に3つめに関しては越境先であるPdMに対する敬意として絶対に守らなければいけない点です。

僕はアメフトについて全く詳しくないですが、昔から強く共感しているこのポストを引用させてもらいます。

自分の見えている範囲に自分のできることがあるとどうしてもやりたくなってしまうものだし、それは良いことだと考えがちですが、必ずしもそうではないと気づかせてくれたポストです。

オーバーパシュートと越境の違い

Beyond Boundariesについて思うこと

僕はAnotherBallのビジョンであるこの言葉が好きで、CTOの@tatsushimに「境界は国境だけじゃなくてチームとかメンバーの間とかあらゆるところにあるから、もっと色んな場面で使うべきです」って話をしたことがあるくらいです。

この記事では越境と、その先にある価値について思うところを書いてきました。

決して簡単ではないし、境界の高さはそれぞれ違うし、越えられない境界、越えなくていい境界、越えてはいけない境界、様々あると思います。

ただ、その難しさを乗り越えてこそ得られる信頼関係だったりとか、成果だったりとか、やりがいみたいなこともあると確信してます。

こうやって越境が積み重なると、チームには穴がなくなっていきます。職責がはっきりしているから誰の担当か迷わないし、職能が重なっているから誰かが手を離しても別の誰かが拾える。隙のないチームって、たぶんこういう状態のことなんじゃないかなと思っています。

「職責は明確にする。職能は越える。」、とりあえずこの言葉だけ覚えて帰ってくれたら嬉しいです!

職責と職能は別の軸

We’re Hiring

AnotherBallには、「とにかく良いものを作りたいし、良いチームにしたいんだ!」という感じのメンバーがたくさん揃ってます。扉は開いてますよ、話だけでもどうですか?

AnotherBall Careers

参考文献

  1. Four Keys
  2. DORA’s software delivery performance metrics
  3. A history of DORA’s software delivery metrics
  4. Accelerate State of DevOps Report 2024
  5. State of AI-assisted Software Development 2025
  6. Announcing the 2025 DORA Report
  7. 領域を越境する時のお作法

WWDC26 現地参加レポート ── 熱狂的な3日間!

WWDC26 現地参加レポート

はじめに

こんにちは!AnotherBallのiOSチームです。

2026年6月7日から9日にかけて、東京からiOSチームメンバーが一人 WWDC26 にアメリカ現地参加してきました。
この記事では、現地でのイベントの雰囲気や得た知見、気付きをまとめてお伝えします。

WWDCとSpecial Eventについて

WWDC (Worldwide Developers Conference) はApple年次の開発者会議です。Apple Developer Programに参加していれば抽選を申し込むことができ、当選した開発者は現地のSpecial Eventに招待されます。

当選のメール。受け取った時はとても信じ難かった...

すべてのセッションはオンラインでも配信されていますが、現地ではIn-person Labsで直接Appleのエンジニアと話したり、世界中の開発者と交流したりすることができて、オンラインでは得られない価値があります!

ちなみに、AnotherBallでは技術カンファレンスへの参加は勤務扱いです。今回のWWDCも業務として参加させてもらいました。こうした学びの機会をサポートしてくれる環境には感謝しています。

スケジュール

下記は私が参加したイベントの一覧です。このタイムスケジュールに沿って詳細を書いていきます。

時刻はすべて太平洋時間です。

6/7(日・Day 0)

  • 16:00〜 Welcome Reception @ Apple Infinite Loop Campus

6/8(月・Day 1)

  • 10:00〜11:30 Keynote @ Apple Park
  • 13:00〜14:00 Platforms State of the Union @ Apple Park
  • 14:15〜16:00 In-person Labs @ Apple Park
    • 14:45〜15:15 Design Lab
    • 15:15〜15:30 App Review Lab

6/9(火・Day 2)

  • 10:00〜11:30 Developer Session @ Steve Jobs Theater
  • 11:30〜15:00 Mixer @ Apple Developer Center Cupertino
  • 16:00〜18:00 What’s new in iOS 27? by Paul Hudson @ Residence Inn by Marriott San Jose Cupertino
  • 20:00〜22:30 The Mandalorian and Grogu @ Steve Jobs Theater

Day 0 ── Welcome Reception

Keynote発表の前日に、ウェルカムイベントがありました。会場はAppleの旧本社、Apple Infinite Loop Campusです。

Apple Infinite Loop Campusの入り口

15:30頃に到着した時には、すでに長蛇の列があって、活気に溢れていました。30分程度で入ることができました。

Welcome Receptionの受付列

個人的にびっくりしたのが、Appleスタッフたちの「おもてなし精神」です。受付で参加証のQRコードをスキャンして中に入る時に、盛大に拍手したり、ハイタッチしたり、「Welcome」と元気よく挨拶してくれて、一瞬でこの場所が好きになりました。ちなみに退出時も同じく、下記の動画の通り、盛大に見送ってもらいました😂

Receptionイベントでは、Appleが中庭で飲食を提供してくれて、ベンチでゆっくり他の開発者と話すことができました。芝生が気持ち良すぎる・・・

中庭でのReception

特に印象的だったのが、Swift Student Challengeの優勝者の一人との会話で、彼が作ったのが動画撮影アプリで、散歩中にインカメラ・アウトカメラを切り替えても、端末を回転しても必ず正しい方向の動画が撮れるというものです。身近な課題をアプリで見事解決した、素敵な例だと思います。

大きな木が日陰になっていて良かった。CAの日差しがすごく強かったです。

ちなみに、参加者の出身の世界地図があって、みんなピンを止めていますが、見た通り世界中から人が来ています。ただ、カリフォルニアは元々マルチカルチャーな場所で、いろいろな人種がいるので、外国人として訪れても全然違和感がありませんでした。

参加者の出身を示す世界地図

Day 1 ── Keynote と Platforms State of the Union

いよいよKeynoteの日!そして初めてのApple Park訪問です。この日が一番ワクワクでした。

Apple Park

8:30頃にApple Park Visitor Centerに到着し、受付を済ませてApple Parkに入りました。

第一印象は、「とにかくデカい!」です。リングシェープの4階のCampusのビルは想像以上に大きくて、曲線が美しかったです。

Apple Parkと青空

少し歩くとKeynote会場が見えてきました!

Keynote会場の野外ステージ

野外ステージでとても開放的で、今まで会議室内で聞くプレゼンとは印象が全然違ってとても新鮮でした。

振り返るとApple Park会社ビル、そのスケールの大きさに圧倒

Caffè Macs(社員食堂)で朝食が提供されていて、美味しかったです。

最高級の無料朝食

Caffè Macsでは世界中から来た社員の食事を、毎日12,000食以上提供しているらしくて、規模の大きさが直接分かりますね。

Keynote

10時のライブ配信の前に、Craig FederighiTim Cookが登場して会場を沸かせました。特にTim CookについてはCEO退任前の最後のWWDCということもあり、大きな拍手と歓声が起こりました。

Tim Cookの登場。「人生でこんなにたくさんのiPhoneを見るのは初めてだ」と挨拶

Tim Cookの挨拶の後、動画が始まり、会場全員で生で見ました。

今年のアップデートはAIのインテグレーションにフォーカスしていて、iOSのエコシステムとの連携が強化されたように思います。ただ正直、会場での反応はあまりよくなくて、他のAIがすでにできていることをiOSに取り入れただけに過ぎないと私は思います。正統進化といったところですね。

会場全員で一緒にライブビューイング

ランチ休憩とアプリアイコン

朝食に続いて、ランチも無料で提供されました。

食べたあとは、Keynoteの時に座れなかった芝生エリアに座りましたが、ふとスクリーンを見たら、さまざまなアプリアイコンのパネルが映っていました。

アプリアイコンは一つ一つフリップして入れ替わり、全体の色も徐々に次のテーマ色に切り替わって行きます。

これはもしや、Avvyのアイコンがあるのではないか?と期待しながらオレンジ色になった時を見張っていると、、、

オレンジ色パネルの右下をよく見ると...

なんとAvvyのアイコンが表示されました!

Avvy Iconの登場!!

この瞬間、椅子から飛び出して自撮りしました😂

まさにAvvyアプリが世界中の人に見られる瞬間で、Appleが参加者一人一人をコントリビューターとしてちゃんと認識しているなぁと感じ、ますますAppleが好きになりました❤️

Platforms State of the Union

Day 1の午後のセッションは開発者向けで、新しい機能とAPIを具体的に説明していました。

Platforms State of the Unionのまとめ

App Intents、Core AI、Swift / SwiftUIの改善、AIエージェント駆動のXcode 27、どれも進化しています。特にXcode 27がAIエージェントをサポートしている点が好きで、Xcodeだけで作業が完結するのはとても助かります。実際にベータ版を試しましたが、XcodeとCLIの切り替えがなくなって、コンテキストスイッチングコストがかなり軽減されました!

In-person Labs(Design Lab / App Review Lab)

このセッションでは、Appleエンジニアがトピックごとに分かれていて、2時間の間、誰にでも自由に質問していい時間です。個人的に、このラボこそがWWDCの醍醐味だと思います。普段では繋がりのないエンジニアと直接話せる機会は、ここしかないからです。

会場マップ

様々なLabのうち、Design LabとApp Review Labは事前に予約が必要で、私は予約が取れたので、時間になったら向かいました。

Design LabではAppleのデザイナーと1-on-1で30分間、新機能開発の具体的な話ができて、とても有意義な時間でした。ただ機能を作るアドバイスをくれるのではなく、ユーザーストーリーベースで、誰が価値を感じるのか、対象ユーザーにどう見せるのか等を一緒に考えてくれました。

私は、エンジニアとしてどうやって作るかに重点を置きがちですが、その前に「Why」を明確に考えないといけないと改めて気づきました。

Design Labは3階にあって、会場を見渡せます 中庭と虹ステージ

App Review LabではTestFlight External TestingやTestFlightの運用方法を質問したり、他のAppleエンジニアとは、Xcodeのプレビュー問題やFoundation Modelsについて話しました。Appleのエンジニアはみんな気さくでホスピタリティが高いのが印象的でした。

Inner Ringでのレセプション

セッション後はApple Park内部の芝生が開放されて、あの有名なRainbow Stageと写真を撮ることができました。

Rainbow Stage!

芝生エリアがとても広くて、一度大の字になって、その膨大な大地の力を感じました。気持ちよかったです(笑)

もちろん食べ物もありました😋

Day 2 ── デベロッパーセッション&交流

Day 2はさらにデベロッパーに特化したセッションが用意されました。Steve Jobs Theaterで生で発表が見れるのですが、私はちょっと寝坊して9:30に到着したら、もうすでに席がいっぱいで、隣のApple Developer Centerのシアタールームに案内されました。

シアタールームの大画面で視聴

Developer Session @ Steve Jobs Theater

1.5時間の生配信のセッションを視聴しました。ここではAppleエンジニアがリレー形式でiOS 27の新機能をプレゼンしました。具体的なコーディングが見れて、実装のイメージがDay 1より沸きました。

Appleエンジニアが具体的なコードを見せながら解説

Mixer @ Apple Developer Center Cupertino

午前11:30から午後3時まで、Apple Developer CenterはWWDC参加者に解放されて、自由に交流できるようになっていました。フードトラックで美味しいご飯を食べながら、中国とColoradoから来た開発者たちと話しました。交流の機会が絶えなかったです。

様々なフードトラック 炎天下で食べるソフトクリームが美味しかった😋

他にも、Swift Group Labの視聴会、Tools Lounge、Reality Composer Loungeがあって、Appleエンジニアが個別トピックについてプレゼンしているのを自由に見ることができました。

Xcode 27のAIについてのQ&Aが盛り上がりました。

What’s new in iOS 27? by Paul Hudson @ Residence Inn by Marriott San Jose Cupertino

3時過ぎに、私は近くのホテルで開催されたコミュニティイベントに参加しました。この時はHacking with SwiftのPaul Hudson(@twostraws)さんが登壇者で、ライブコーディングでXcode 27で追加された機能を見せました。一晩で全部試したのが凄すぎますね・・・

Paul Hudsonの発表。彼の記事に何回命を救われたか...

実はこのイベントはCommunityKitという団体が開催したイベントの一部で、イベント自体は6月7日から12日まであったようです。一週間丸ごとiOSデベロッパー向けのイベントがあるのは、日本では考えられない規模ですね。

CommunityKitイベントスケジュール

The Mandalorian and Grogu 上映会 @ Steve Jobs Theater

Day 2の夜、最後のイベントを飾るのは、おそらく世界トップクラスの劇場で見る映画上映会です。朝入れなかった、念願のSteve Jobs Theaterに入ることができました!

シアター内の様子

とにかく建物が美しくて、圧巻でした。ここでWWDC26のシンボルとの写真を撮って、名残惜しくもApple Parkを後にしました。

WWDC26シンボル&Apple Park

さいごに

Appleが好きな一人の人間として、WWDC26をきっかけにApple Parkに訪れられたのがとても光栄なことだし、長年の夢が叶った気持ちです。

現場で発表された内容は、全部動画でもリアルタイムでキャッチアップできます。しかし現場の空気感、高揚感、またはAppleスタッフの優しさは、その場にいないとわからなかったと思います。世界中の開発者と話して、より視野が広くなったのはもちろん、みんなが何かしら面白い課題を解決しているのを肌で感じると、自分でも頑張ろうというモチベーションがこれまでにないぐらい高まりました!

最後に、一つ得た気づきを添えて終わります。Day 2のDeveloper Sessionですが、その登壇者の一人は、前日に実際に話したエンジニアでした。その方が登壇した瞬間、私は「彼らは雲の上の存在なんかじゃなくて、同じ一人のエンジニアだったんだ」と感じました。Appleとの親近感が湧いてよりAppleが好きになった瞬間でした。

夜のSteve Jobs Theaterはとても美しかった

We’re Hiring

AnotherBallでは、最新のApple技術に挑戦したいエンジニアを募集しています。世界中の開発者と肩を並べてプロダクトを作っていく仲間を探しています。もし興味があったら、ぜひ一度お話ししましょう!

AnotherBall Careers

ゲーム開発からネイティブへ ── バックグラウンドが違うことの強み

こんにちは!

AnotherBall モバイルチームのDavideです。AnotherBallでは、私を含めて多くのクライアントエンジニアがネイティブチームに加わる前に、ゲーム業界での経験を積んでいます。

今回は、バックグラウンドの異なるメンバーが集まったチームが何をもたらすのか――その摩擦と利点、そしてAIがどのようにギャップを埋めるのかについてお話ししたいと思います。

解くべき問題の違い

そもそもエンジニアというのは、問題を解くのが好きな人たちです。しかし、多くのエンジニアが集まると、自然と「自分が一番気になる問題」に手が伸びていきます。
これは、ゲーム開発のように異なるバックグラウンドを持つメンバーがいる場合、特に顕著です。ゲームとモバイルアプリは同じ端末で動くものですが、エンジニアの意識を引っ張る方向はかなり違います:

  • 伝統的に、ゲームは「体験を作ること」を目指し、アプリは「タスクを効率的に終わらせること」を目指してきました。
  • 多くのゲーム――特にストーリー重視のものや競技性の高いものは、ユーザーが集中し続けることを前提に設計されていますが、ほとんどのアプリは通知やバックグラウンド、ディープリンク、中断とともに存在しています。
  • クラシックなコンソールやPCタイトルは一度きり、あるいは大きなアップデートでリリースされますが、モバイルアプリやライブサービス型のゲームは継続的にリリースされていきます。

実際には境界線は曖昧ですが、それでも一見すると違いはたくさんあります!

Avvyはハイブリッド

私たちが開発している Avvy は、誰でも1分でVTuberになれるモバイルアプリです。以前の記事でも書いたように、このアプリはゲームとコミュニティアプリの境目に位置しています。つまりユーザーは、ソーシャルネットワークのような洗練された体験と、ゲームのようなエンゲージメントやパフォーマンスを同時に期待しているということです。

Avvyでは、ゲーム的な機能とソーシャル的な機能が共存している

両方の世界にまたがるものを作るということは、どちらか一方の考え方だけに寄りかかれないということです。配信体験はゲームのように生き生きとしている必要があり、それ以外の部分は他のソーシャルアプリと同じくらい馴染みのある作りでないといけません。どちらもおろそかにできません。

バックグラウンドが違うことの強み

バックグラウンドが違うと、同じ問題に対しても違う直感が働きます。ゲーム開発者とモバイルエンジニアが同じ画面を見ても、目に入ってくるものはまったく違うことがあります――そしてAvvyのようなハイブリッドプロダクトでは、まさにそれが私たちの欲しいものです。

良い例として、Avvy体験の中核を担う、リアルタイムのフェイストラッキングを支えるパイプラインがあります。端末のカメラがユーザーの顔の動きをリアルタイムでキャプチャし、Unityに送ってアバターを動かす仕組みです。カメラとUnityは別々のフレームワークに存在するため両者の通信は遅くなりがちですが、アバターが生き生きと感じられるためには、データを安定した60fpsでUnityに届ける必要があります。

Androidでは、標準的なネイティブからUnityへのブリッジが1フレームあたり3 msかかっていました。16.6 msのフレームバジェットのうち約20%を占める計算で、これは没入感を壊すには十分なレベルです。ゲーム開発のバックグラウンドを持つ私は、Androidではメモリマップドファイル、iOSでは生のポインタに手を伸ばすのが自然でした。結果は明確でした:

手法 iOS Android
パラメータ渡し (PInvoke / AndroidJavaProxy) 0.01 ms 3 ms
メモリマップドファイル 0.007 ms 0.11 ms
ポインタ直接アクセス 0.001 ms 非対応

ちなみに、このパイプラインはとても面白いトピックで、別の記事を書く価値があります。近いうちにまとめたいと思います!

AIがギャップを埋める

これが数年前よりもうまく機能するようになった理由はAIです。ゲームからネイティブへの移行はかつて、新しい言語・新しいフレームワーク・新しい慣習という険しい道のりでした。今では、AIが機械的な翻訳の部分を引き受けてくれるので、アーキテクチャやベストプラクティスといった本質的な部分に集中できます。

例えば、Avvyのライブ配信中のエールポイントカウンターを実装したときのこと。私たちのイメージははっきりしていました――サーバーイベントに応じて、オドメーターのように数字がロールし、スクワッシュ・アンド・ストレッチするような動きです。Claude Codeに渡したプロンプトはこちらです:

Disneyの12のアニメーション原則(スクワッシュ・アンド・ストレッチ)を使い、Textをオドメーター風にアニメーションさせて。

この語彙はゲーム開発における基本中の基本です。Disneyの12のアニメーション原則(スクワッシュ・アンド・ストレッチ、アンティシペーション、フォロースルーなどを含む、動きに関する古典的なルール)も、ゲーム開発者なら馴染みのあるものです。

Claude Codeはほぼ一発でSwiftUIのコードに翻訳してくれました。スクワッシュ・アンド・ストレッチは、下端を基点とした縦方向のスケール変形をバウンス系のスプリングでアニメーションさせる形で実装されていました:

1
2
3
4
5
6
7
8
9
10
Text(String(character))
.offset(y: offsetY)
.scaleEffect(x: 1, y: scaleY, anchor: .bottom)
.opacity(opacity)

withAnimation(.spring(duration: 0.6, bounce: 0.5).delay(delay)) {
offsetY = 0
scaleY = 1.0
opacity = 1.0
}

オドメーター的な雰囲気は、各文字を少しずつずらして順番にアニメーションさせることで生まれていました:

1
2
let delay = Double(index) * staggerDelay
withAnimation(.spring(duration: 0.6, bounce: 0.5).delay(delay)) { ... }

数時間の調整とパラメータのチューニングを経て、アニメーションは完成しました。AIなしだったら、まずSwiftUIのアニメーションドキュメントを半日かけて読むところから始まっていたと思います。

実際のアニメーション!

まとめ

バックグラウンドの違いはチームに独自のダイナミクスを生み出しますが、最終的にはチームがより広く問題をカバーできるようにしてくれます。Avvyのようなプロダクトでは特にそうです。そして実際のところ、AIは私たち一人ひとりが持ち込むものを置き換えるものではありません――それは増幅装置のように働き、ドメインを横断する際の摩擦を減らしてくれます。残るのは、AIには任せられない部分――人間の感性、判断力、細部へのこだわり、そして「なんとなくおかしい」と感じたときにそれを掘り下げる姿勢です。

私個人にとっては、ゲームからネイティブへ移った理由は二つに集約されます。ひとつは、もっと頻繁にリリースして実際のユーザーと一緒にイテレートしたかったこと――ゲームではリリースサイクルが数年単位になることも珍しくなく、なかなかできないことです。もうひとつは、新しい挑戦をしてみたかったから(もちろんゲーム作りは今でも大好きですが!)。意外だったのは、思っていた以上に多くのことが持ち越せたという点です。特定のスタック固有のテクニックよりも、しっかりとした原則のほうが長く効きますし、丁寧に使えばAIがその間のギャップを埋めてくれます。経験の幅を広げるかどうか迷っている方がいるなら、今はその一歩を踏み出すには絶好のタイミングだと思います。

We’re Hiring

AnotherBallでは、Swift・Kotlin・Unity・AIを活用したアプリ開発に興味のあるクライアントエンジニアを募集しています。技術力に加えて、細部への注意・好奇心・強いオーナーシップは、AI時代のエンジニアにとってますます重要なスキルになっています。共感していただけたら、ぜひ一度お話ししましょう!

AnotherBall採用情報

KMP × Kotlin 2.3 ── iOSビルド47%改善の裏でAndroidが遅くなった理由

こんにちは!
AnotherBall MobileチームのRIO(@rioX432)です。

先日、Mobile勉強会 ウォンテッドリー × チームラボ × Sansan #24 にて、ライブ配信アプリ Avvy のKMPプロジェクトでKotlin 2.0.20から2.3.20にアップグレードした際のビルドパフォーマンスへの影響について発表させていただきました。

発表資料

まとめ

Avvyでは、KMPモジュール persona-domain-kmm(25リポジトリ、33 UseCase)でiOS/Android間のドメインロジックを共有しています。アップグレード前は、iOS向けXCFrameworkビルドが平均 14.3分(最大28分)かかっており、Kotlin 2.0.20環境で依存ライブラリの更新やAGP 9への移行がブロックされている状態でした。

Kotlin 2.3.20(Ktor 3.4、AGP 8.13、Gradle 8.13も併せて更新)へアップグレードした結果を、GitHub Actions 87回の実測データで検証しました:

  • iOS: 47%高速化 — 平均ビルド時間が14.3分から7.7分に短縮。最大の要因は linkReleaseFramework の46%高速化(7分38秒→4分07秒)で、ビルド全体の約90%を占めるステップです。
  • Android: 約30秒増加(+30%)— compileKotlin が43%増加。原因はK2のJVMコンパイルリグレッション(KT-81883、未修正)。Configurationフェーズも26秒(+32%)増加しています。

iOSの改善がAndroidのトレードオフを大きく上回るため、KMPプロジェクトならKotlin 2.3は入れるべきです。AGP 9移行の先手を打つ意味でも、早めのアップグレードをおすすめします。

Claude Codeで依存関係を常に最新に

Kotlin 2.0.20に留まっていた経験から、依存関係の更新が遅れると問題が複合的に積み重なることを痛感しました。そこで、Claude Codeのカスタムスキル(/update-deps)を作成し、依存関係の更新ワークフローを自動化しています。新バージョンのチェックからchangelogの調査、更新の適用、ビルド検証、Kotlin互換性チェック付きのPR作成までを一貫して行えるため、最小限の手間で依存関係を最新に保てるようになりました。

We’re Hiring

AnotherBallでは、依存関係の更新やコードベースの健全性を保ち、技術負債の少ない環境を心掛けながら開発しています。こうした取り組みに共感いただける方、ぜひお話しましょう!

AnotherBall Careers

SwiftUIアプリにUaaLを組み込む ── Viewのライフサイクル管理方法の紹介

こんにちは!AnotherBallのiOSチームです。

以前の記事では、モバイルチームがAvvyのマルチリポジトリ構成を紹介しました。Kotlin Multiplatform (KMP) と Unity as a Library (UaaL)のビルド、配布、そして5つのリポジトリ間の連携について、全体像をお伝えしました。

Avvyの技術面で面白いところはUaaLとNativeの自然な組み込みにあると思います。今回の記事では、iOSに焦点を絞って、SwiftUIアプリの中にどのようにUaaLの画面を管理しているかを解説します。

前提:UnityとNativeの役割

Avvyの設計思想として、Unityは2Dアバターに関する機能のみを持つようにしています。Unityはアバターの描画やアバターのカスタマイゼーション(着せ替え)のUIを担っています。それ以外の機能やUIはNative側で実装して、Nativeの機能を最大限に使えるようにし、配信アプリらしい体験を提供できるように設計しました。

UaaLの制約:インスタンスは1つだけ

UaaLを使う上で大きな制約があります。Unityランタイムの複数インスタンスの読み込みはサポートされていないため、UaaLのViewは画面上に1つしか表示できません。同時に2つ表示しようとすると、片方が描画されません。

Avvyでは配信画面、アバターホーム、ガチャなど、複数の画面でアバターを表示しています。そのため、画面遷移のたびにUaaLのViewを付け替え、二つ同時に画面に存在しないようにする必要があります。しかし、各画面でこのライフサイクルを意識するのは煩雑で、意図しない不具合の可能性が高まります。

そこで、SwiftUIで専用View「UnityView」を作成して、管理を集約し、各画面からは通常のViewと同じ感覚で使えるようにしました。

配信画面の構成

例として配信画面を取り上げて解説します。配信画面ではUnityは一番下のレイヤーでアバターのレンダリングのみを担当し、NativeUIがその上にオーバーレイされています。

グレー色はUnityのアバターの描画領域で、黄色がNativeのオーバーレイです。Unityの領域はAvvyのiOSアプリではUnityViewと名付けています。SwiftUI側から見ると、通常のViewと同じようになります:

1
2
3
4
5
6
7
8
9
10
UnityView(displayType: .liveStream) // UnityでロードしたいSceneを指定
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea() // セーフエリアを含めて全画面描画
.overlay {
if viewModel.isSceneLoading {
LoadingOverlay() // ローディングインジケーター
} else {
overlayContent // Nativeボタンやコメントリストなど
}
}

Unityのライフサイクルを一切気にせず、画面を開く・閉じるだけでアバターの表示・非表示が切り替わります。

UnityViewの実装

UnityViewUIViewControllerRepresentableで、内部にUnityViewControllerを持っています。なぜUIKitのViewControllerが必要かというと、UnityFrameworkが提供する描画ViewがUIKitのUIViewだからです。

例えばアバターホーム画面から配信画面をモーダルで表示した場合、UnityのViewを自動で最前の画面に付け替える必要があります。UnityViewControllerviewWillAppear/viewWillDisappearのライフサイクルでこれを実現しています:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public struct UnityView: UIViewControllerRepresentable {
public let displayType: DisplayType

public func makeUIViewController(context: Context) -> UnityViewController {
return UnityViewController(displayType: displayType)
}
}

public final class UnityViewController: UIViewController {
// UaaLが提供するUnityのView。記事用に簡略化しています。
// シングルトンなので、全画面で同じインスタンスを使い回しています。
private var unityView: UIView = UnityFramework.shared.rootView

public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addUnityView() // Viewの追加
}

public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeUnityView() // Viewの削除
}

private func addUnityView() {
view.insertSubview(unityView, at: 0) // UnityのViewを最下層に追加
unityView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
unityView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
unityView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
unityView.topAnchor.constraint(equalTo: view.topAnchor),
unityView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}

private func removeUnityView() {
unityView.removeFromSuperview()
}
}

画面が表示されるとき(viewWillAppear)にUnityのViewを追加し、非表示になるとき(viewWillDisappear)にViewを外します。極めてシンプルな実装ですが、これだけで UnityのUIViewが一つのみ存在することを担保できます。

なお、View入れ替え処理と同時に、AvvyアプリではUnityの一時停止、再開も行なって、バッテリー消費とリソース負荷を抑えています。

まとめ

UaaLをNativeアプリに組み込む際、「同時に表示できるViewは1つだけ」という制約がありますが、UIViewControllerのライフサイクルを使ってViewの付け替えとリソース管理を自動化し、SwiftUIの UnityView としてラップすることで、それらを意識することなくアバター表示ができるようにしました。この構成により、AvvyはアバターアプリでありながらNativeアプリとしての操作感を保てています。

次回は、本記事に登場した「DisplayType」を使ってUnityに指定のSceneをロードさせる仕組みなど、NativeとUnityがどのようにコミュニケーションをしているかについてご紹介したいと思います。

We’re Hiring

AnotherBallでは、テスタブルでメンテナブルなアーキテクチャを大切にしており、同じ志を持つエンジニアを常に探しています。こうした仕事に興味があれば、ぜひお話しましょう!

AnotherBall Careers

チームの停滞と優先順位ズレに気づくカンバンをAIで作る ── Chrome拡張とAIコーディングによる可視化の実践

こんにちは!AnotherBallでテックリード兼Avvy事業部技術責任者をやっている @fortkle です。

今回は、スクラムでのタスク管理をより良くするためにChrome拡張を自作した話を書こうと思います。

自由度の高かった物理ホワイトボードのカンバン

現在、Avvyの開発チームでは開発手法としてスクラムを採用しており、日々の透明性と検査・適応を促すためにカンバンボードを活用しています。

カンバンを語るとき、今も頭に浮かぶのが以前の職場で使っていた物理のホワイトボードとポストイットです。

やっていたことはシンプルで、

  • ホワイトボードにマーカーでレーンを引く(Todo / In Progress / In Review / Done)
  • 上から優先順位順にカードを並べる
  • 左端にIssue(親)、その右にSub-issue(子)を横展開

これだけで「今スプリントで何をどの順番でやるか」がひと目でわかりました。

まずはシンプルなルールで始めましたが、スクラムを進めていくうちにカンバンの形も少しずつ変わっていきました。

例えば、メンバーのSlackアイコンを印刷してマグネットに貼り、担当しているタスクの上に貼ってアサインを可視化したり、「このレーンは最大1枚まで」とマーカーで書いてWIP制限したり。

カンバンガイドにも「どのようにフローの透明性を確保するかは、カンバンシステムメンバーの想像力以外に制限を受けない」と書かれていますが、物理のホワイトボードはまさにそれが実現できるツールでした。

ちなみに、このあたりの物理カンバンの活用アイデアはアジャイルコーチの道具箱 – 見える化実例集が非常に参考になるのでおすすめです。

Linearに移行して「あの感じ」を再現したくなった

今のAvvy開発チームは地方や海外からリモートワークで働くメンバーもいるため、物理のホワイトボードとあまり相性がよくありません。そのためカンバンは物理カンバンではなくLinearというデジタルツールを使っています。

LinearにはBoard Viewがあり、カード管理はできるのですが、使ってみるとホワイトボード時代の感覚とは違う部分がいくつかありました。

担当者ごとにグルーピングして表示する例(表示されているデータは全て架空のものです)

  • IssueとSub-issueの親子構造をBoard上で理想の形に配置できない(grouping/sub-groupingなどを駆使すればできなくはないがベストではない)
  • デフォルトのUIだと1画面に表示できる内容に限界があり、スプリント全体を把握しづらい
  • 停滞しているカードが一目でわかりづらい
  • WIPを視覚的に制限できない

「できなくはないけど、あの感じが出ない」という状態が続いていました。

Chrome拡張でカスタムビューを乗せた

そこで試したのが、「Chrome拡張でLinearにカスタムViewをオーバーレイする」という方法です。

Linear APIでIssueとSub-issueの情報、ステータスの変更履歴などが取得できるので、それをもとにホワイトボード時代のレイアウトをブラウザ上で再現しました。

物理カンバンを再現したカスタムView(表示されているデータは全て架空のものです)

特徴としては以下の通りです。

IssueとSub-issueの親子構造を展開
IssueカードからSub-issueがBoard上で展開されるので、全体像が把握しやすくなりました。スプリント全体を1画面で見渡せるのも大きなポイントです。

優先順位順の縦並び
スプリント内のタスクが優先順位順に上から並びます。スプリントが進むにつれてカードは / を描くように右上へ移動していくので、優先順位の低いカードが先に動いているときにすぐ気づけます。

差し込みタスクへの対応
優先順位が可視化されているので、急な追加タスクが入ったときに「優先順位の低いこれを諦めて追加タスクを入れよう」という判断がしやすくなりました。

AIに頼むことでやりたいことが5分で実現できる

技術的には以前から可能でしたが、ここにAIコーディングの進化が加わるとさらに自由度が増します。どういうことかと言うと、AIコーディングを使えば「ちょっとこう変えたい」がすぐ実現できるということです。具体例をあげていくつか工夫した点をご紹介します。

停滞インジケーター

「In Progressで止まっているメンバーがいても、ボードを見ただけではなかなか気づけない」という問題がありました。例えば、物理カンバン時代ならポストイットに正の字で経過日数を表現したりして簡単に可視化できていた部分です。

これもAIに「レーンでの経過時間を可視化したい。担当者アイコンにインジケーターのように5分割したゲージを出し、1日経過するごとにそのゲージを埋めて最大5日まで停滞状況がわかるようにして」と伝え、以下のようなUIを作ってもらいました。ここまでものの数分です。

各カードの右上にある担当者アバターに5分割のリングインジケーターが付いて、In Progressに移動してから1日ごとにゲージが1つ埋まります。「このカード、もう5日止まってるな」とボードをひと目見れば全員が気づけるようになりました。

Linear APIがステータス変更の日時情報を持っているので、サーバーサイドに何も追加することなくクライアントサイドだけで実現できたのも良かったです。

絞り込み・カードへのフォーカス

毎朝のデイリースクラムで「今日やること」を1人ずつメンバーがチームに対して共有するとき、カンバン上のどこにその人のカードがあるか一目でわからない問題がありました。物理カンバン時代なら直接該当するカードを指差しすれば終わっていた部分です。

これもAIに「担当者でカードを絞り込んだ場合にその人の対象カードがどこにあるのか見失うときがある。絞り込んだときだけ、添付画像の位置に←→のようなボタンを置いてその人のカードに1つずつフォーカスする挙動にしたい。ブラウザのページ内検索(Ctrl+F)のようなイメージ。」と添付画像とともに伝えて以下のような機能ができました。

UIなど少しの微調整は行いましたが、あっという間にイメージ通りのものが実装できました。
また、絞り込みというと一般的に「選択したもの以外が表示されなくなる」という挙動が多いですが、今回は意図的に消すのではなく選択されたものをハイライトで目立たせる実装にしています。そのため、絞り込み中も他のタスクとの関係性を確認できる点も使いやすいポイントです。

使い始めて起きたチームの変化

このボードを使い始めてからチームで起きた変化をいくつか列挙すると以下の通りです。

  • 「いま着手しているタスクより、こちらのタスクの方が優先順位が高そうです!」という会話が増えた
    • 優先順位順に並んでいると、ズレに気づきやすくなります。
  • 停滞に早めに気づけるようになった
    • In Reviewで数日止まっているカードにも気づきやすくなり、「詰まってるなら手伝えるよ」という申し出が早めに出せるようになりました。
  • スプリントの取捨選択がしやすくなった
    • 急な追加タスクが入ったときの「これは今スプリントで諦めよう」という合意がしやすくなりました。

一方、デメリットとしてはChrome拡張であるため、スマホや専用デスクトップアプリからは使えない点は注意が必要です。また、社内限定であってもChromeウェブストアで配布する場合は審査に2〜3日かかるため、チームへの展開が遅くなる点も気になりました。

まだまだ使い始めたばかりですが、AIを活用することで理想的なカンバンを作成できる可能性を強く感じました。

チーム内でのChrome拡張の配布について

Chrome拡張はChromeウェブストアで配信しつつ、公開範囲を社内に限定することもできます。具体的な手順はこちらの記事が参考になりました。

Google グループのメンバーに配布する - Google

また、Chrome拡張の審査の提出はGitHub Actionsで自動化できるため、PRをマージすれば自動的に審査中になるようにChrome拡張のリポジトリを整備しています。

まとめ

「物理カンバンの自由度はデジタルツールでは再現できない」と半ば諦めていましたが、AIコーディングで状況が変わった実感があります。

「こう可視化したい」を自然言語で伝えたら数分で動くUIになる。ツールの制約に縛られず、チームが本当に必要な可視化を自分たちで作れるようになってきました。

今後もチームのおかれた状況とニーズに合わせてカンバンをカスタマイズし、育てていこうと思います。

We’re Hiring

AnotherBallでは、今回紹介したようにチームの課題を自分たちで発見し、解決していく文化を大切にしています。そんな環境でプロダクト開発に携わりたいエンジニアを募集しています。

AnotherBall Careers

ZendeskからChatwootへ ── AIを中心にCSフローを再構築

こんにちは!AnotherBallでAIOpsを担当しているFrancisです。AIを社内オペレーションに組み込み、現場の業務改善を進めています。この記事では、カスタマーサポートの問い合わせ対応をZendeskからChatwootへ移行し、初回返信時間を1,186分から1分未満まで短縮できた経緯とその仕組みについて解説します。

Chatwootへの移行

元々CSはZendeskを利用していました。ユーザーはアプリ内の「お問い合わせ」からZendeskのフォームを送信し、CS側はZendeskのチケット画面で対応する、という形です。CS対応の効率化のためにAIを活用したいと考えていましたが、Zendeskは高度なAIカスタマイズを想定しておらず、応答フローのカスタマイズにはさまざまな有料アドオンとひと工夫や小技が必要でした。問い合わせごとに返信のトーン、テンプレートの使用有無、エスカレーションフローなどを設定したかったのですが、Zendeskではやややりづらい部分がありました。

そこで選んだのはChatwootです。Chatwootのほうはそういうカスタマイズがはるかに楽にできます。さらにホスティングプランで運用コストを抑えられますし、将来的に必要になったらオープンソース版へ移行できる選択肢もあります。オープンソースである分、機能や外部サービスとの連携の挙動がわかりやすく、トラブルシュートもしやすいのも大きなメリットです。

課題: Chatwootはチャット中心、我々はフォーム中心

以前使っていたZendeskでは、フォームで問い合わせを受け付けていましたが、Chatwootはチャットを想定したCSツールです。ただ、ほとんどの問い合わせはリアルタイム対応が不要で、むしろ最初のメッセージでできるだけ詳しく状況を書いてもらった方が解決が早いので問い合わせ導線をフォームのままにしたいと考えていました。チャット形式だと情報が足りないまま送られやすく、確認のやり取りが増えてしまうことを懸念していました。

また、問い合わせ対応には端末モデル、OSバージョン、アプリバージョンなどの情報も必要です。既存のフォームではアプリ内から送信する際にこれらが自動入力されるため、ユーザーが手動で入力する必要はありません。しかし、チャットウィジェットでこれを確実に実現するのは容易ではありませんでした。そのため、フォームでの受付を維持しつつ、各問い合わせをChatwootのネイティブ会話に変換する仕組みが必要でした。

解決策: Google フォーム + スプレッドシート + Apps Script

完全に自分でコントロールできるスタックを選びました。

  1. Google フォーム — ユーザーが使うお問い合わせフォーム
  2. Google スプレッドシート — フォームの回答がここで自動的に蓄積される
  3. Google Apps Script — ChatwootとSlackへの連携を担う

フォームが送信されると、Apps Scriptが回答内容をGoogleのサポートメールボックスへメールとして送信します。Chatwootはそのメールボックスを読み込み、届いたメールから自動的に会話を作成します。その後はAPIで問い合わせカテゴリを示すラベルと連絡先の属性を設定します。

もう一個のスクリプトはWebhookハンドラーです。Chatwootは新しいメッセージごとにmessage_createdイベントを発火するので、Apps Scriptがそれを感知しメッセージをSlackに転送します。ユーザーからのメッセージが親スレッドを作り、エージェントとAIの返信はスレッド内に投稿されます。

CSの問い合わせフロー:Google Form → Chatwoot → Slack

すべての問い合わせがGoogle Sheetに蓄積されるのが、この仕組みの最大のメリットです。生データが常にスプレッドシートにあるため、週次のCSダイジェストも自動化できました。毎週金曜日、機能リクエストを含め、直近1週間の問い合わせをLLMで分類し、日英バイリンガルのサマリーをApps ScriptでSlackに自動投稿します。

その結果、チームの反応は早く、CSを超えたアイデアも出てきました。たとえばSNSの反応にも同じようなアプローチを適用し、ユーザーがアプリをどう体験しているのかをもっと深く把握しようというアイデアがありました。ユーザーの問い合わせにただ返信するだけではなく、きちんと理解しにいくという取り組みが始まりました。

Chatwootのプロンプトエンジニアリング

AIの返信を安定させるために、プロンプト設計で工夫した点をいくつか紹介します。

  • テンプレート構造を明示する。 人間のエージェントが使っている見出し、トーンのガイドライン、締めの定型まで、プロンプトにそのまま入れています。構造がはっきり書かれていると、モデルはかなり安定して従ってくれます。

    なお、プロンプト自体は日本語ではなく英語で書いています。日本語で返信する場合でも、指示は英語の方がモデルが安定して従うことがわかりました。

    1
    2
    3
    4
    5
    6
    Role: You write professional emails on behalf of [App] Support.
    Use a greeting, structured paragraphs, and a courteous closing.
    Insert TWO line breaks between paragraphs.

    Greeting: Address the user by username (e.g. "user_12345").
    If no identifier is available, use a neutral greeting.
  • 問い合わせ種別ごとのルーティング。 問い合わせカテゴリに応じてプロンプトを切り替えています。ボットがメッセージ内容から種別を判定して適切なプロンプトを選ぶので、手動でタグ付けすたりする必要はありません。

  • エスカレーション・ロジック。 ボットが自信を持てないときに人間が対応するようにします。

結果

2月16日にGoogle フォームとChatwootを用いたこの新しい仕組みへ切り替えました。

指標 Zendesk平均(1/15〜2/15) Chatwoot平均(2/16〜2/26)
初回返信時間 1,186分(19.8時間) 約1分
解決時間 289.7時間(12.1日) 30時間(1.25日)

Zendeskの平均値はいくつかの外れ値によって引き上げられていますが、中央値は299分 / 172.8時間で、それでもChatwootの平均値を大きく上回っています。まだChatwootの10日分のデータしかないのですが、1ヶ月分のデータが揃ったら改めて見直す予定です。

初回返信が約1分なのは、会話が作成された瞬間に時間帯に関係なくAIが自動返信しているためです。人間のエージェントが対応する場合の平均返信時間は6時間46分です。

データがすべてスプレッドシートにまとまっているので、データ分析がとても楽です。問い合わせ件数の推移を追ったり、最近増え始めた不具合の兆しを早めに拾ったり、週次のバイリンガルCSダイジェストを自動配信したりすることができます。追加の有料分析機能に頼らず、必要なことが回るようになりました。

セットアップにはそれなりに手間もコストもかかりましたが、今は明らかに以前より良い状態です。自分たちでコントロールできるスタックがあり、実運用で動くAI連携があり、すぐに意思決定につなげられるデータも手に入れています。学びを一つ挙げるなら、データと連携レイヤーを自分たちで握ることです。このトレードオフが合うかどうかはチーム次第だと思いますが、AIに本気で投資して高速に改善をしたい我々のチームにとっては、よりオープンで容易にコントロールできるこのスタックがピッタリです。

採用情報

AnotherBallはエンタメとテクノロジーの交差点に立つ会社です。人々が愛するコンテンツやコミュニティとのつながりを深めるプロダクトを作りながら、その実現のためにAIをフルスタックで活用しています。

もしこういう環境に少しでも興味を持っていただけたら、ぜひ採用情報も見てみてください。

採用情報はこちら

KMP × Unity UaaL のモバイルアプリ開発 ── マルチリポジトリ構成と自動化の話

こんにちは!AnotherBall モバイルエンジニアチームです。

私たちが開発している「Avvy」は、ビジネスロジックをiOS/Androidで共通化する KMP(Kotlin Multiplatform) と、Unity製の2Dアバター描画機能をネイティブアプリへ組み込む Unity as a Library(UaaL) を組み合わせた、ちょっと複雑な構成のアプリです。

この記事では、5つのリポジトリをどう連携させているか、そしてGitHub Actionsでどこまで自動化しているかを紹介します。

リポジトリ構成

ビルドの複雑化やチーム間の依存関係といった課題を避けるため、リポジトリを機能ごとに分割しています。

リポジトリ 役割 成果物
shared-kmm ビジネスロジック AAR / XCFramework(KMPライブラリ)
unity-module 2Dアバター描画 Android / iOS向けUaaLライブラリ
android-app Androidアプリ本体 APK / AAB
ios-app iOSアプリ本体 IPA
unity-spm Unity XCFrameworkのSPM配布用 Swift Package

各リポジトリは、ビルド済みのライブラリ(AndroidはAAR、iOSはXCFramework)を通じて連携します。こうすることで、各チームが独立して開発を進められます。

Unityモジュールの役割

Unityモジュールは、アバターの表示とリアルタイム制御を担当しています。

  • アバター表示: 2Dアニメーションでアバターを描画
  • フェイストラッキング: カメラで顔の動きを検出し、アバターに反映
  • カスタマイズ: 衣装やアクセサリーの着せ替え

ネイティブアプリとの通信には工夫が必要です。フェイストラッキングは毎秒60回データを送るため、通常のブリッジでは遅延が発生します。iOSではポインタ経由の直接アクセス、Androidではメモリマップドファイルを使って高速にデータをやり取りしています。

自動化の仕組み

GitHub Actionsを使って、リポジトリ間の連携をほぼ自動化しています。月間約1,200件のPRが自動で処理されています(2025年12月実績)。

サーバーAPIの変更を反映

サーバーのAPI定義ファイル(OpenAPI)が更新されると、KMPリポジトリに更新PRが自動で作成されます。

KMPライブラリの更新

KMPライブラリのリリース → 各アプリリポジトリへの更新PRの作成まで、すべて自動です。

流れはシンプルで、「Publish → Trigger → Update」の3ステップです。

  1. shared-kmmでリリースがトリガーされると、GitHub Packagesに公開
  2. 公開が成功したら、gh workflow runで各アプリリポジトリの更新ワークフローを起動
  3. 各アプリでバージョンを更新したPRが自動作成される
1
2
3
4
5
6
7
8
9
# KMPリリース時(抜粋)
- name: Publish to GitHub Packages
run: ./gradlew publish

- name: Trigger Android update
run: |
gh workflow run update-kmm-version.yml \
--repo AnotherBall/android-app \
--field version=${{ env.VERSION }}
1
2
3
4
5
6
7
8
9
10
11
# Androidアプリ側の更新ワークフロー(抜粋)
- name: Update version in libs.versions.toml
run: |
sed -i "s/kmm = \".*\"/kmm = \"${{ inputs.version }}\"/" \
gradle/libs.versions.toml

- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
title: "Update KMP to ${{ inputs.version }}"
branch: "auto/kmm-${{ inputs.version }}"

Unityライブラリの配布

UnityモジュールをiOSアプリで使うため、SPM(Swift Package Manager)形式で配布しています。

  1. Unity XCFramework をビルドし、GitHub Release にアップロード
  2. ファイルのハッシュ値を計算
  3. Package.swift を自動生成(ダウンロードURLとハッシュ値を埋め込み)
  4. 配布用リポジトリ(unity-spm)にPRを作成

unity-moduleリポジトリでの直接配布も可能ですが、Xcodeの仕様上、SPMパッケージ取得時にリポジトリ全体をダウンロードしてしまいます。Package.swiftのみを格納するunity-spmを用意することで、ダウンロードを高速化しています。

ハッシュ値を埋め込むことで、iOSアプリ側でファイルが壊れていないか確認できます。

リリースブランチの自動PR

release/* ブランチにpushされると、複数のマージPRが自動で作成されます。

  • release/2.10.0main へのPR(本番リリース用)
  • release/2.10.0release/2.11.0 へのPR(バグ修正を次バージョンに伝搬)

バージョン番号を比較して、適切なマージ先を自動で判定しています。これにより、マージ漏れを防げます。

Package.resolved のコンフリクト解決(iOS)

複数のKMM/UaaL更新PRが同時に存在すると、Package.resolved ファイルで競合が発生します。iOSリポジトリでは、この問題を自動解決するワークフローを用意しています。

トリガー: release/* ブランチへのpush

処理の流れ:

  1. GitHub APIでリリースブランチ宛のオープンPRを取得
  2. タイトルが chore: update KMM or chore: update UaaL で始まるPRを絞り込み
  3. マージできるかどうかをチェックし、競合中のPRを特定
  4. 各PRの競合を解決
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# baseブランチをマージ試行
if git merge "origin/$BASE_REF" --no-edit; then
echo "Merge succeeded"
else
# 競合時: PR側のPackage.resolvedを一旦採用
git checkout --ours Package.resolved
git add Package.resolved
git merge --continue
fi

# SPMの依存関係を再解決(baseの変更も反映される)
make resolve-package-dependencies

git commit -m "chore: resolve Package.resolved conflict"
git push

ポイントは make resolve-package-dependencies(内部で xcodebuild -resolvePackageDependencies を実行)で、マージ先ブランチの変更も含めて依存関係を再解決している点です。複数のPRがある場合は並列で処理します。

今後の課題

  • CI/CD実行時間: Gradle/Xcodebuildで40分以上かかることがあり、キャッシュ改善を検討中
  • ワークフローの重複: 似たような処理が複数ファイルにあるので、共通部品として切り出したい
  • 更新PRの自動マージ: 現在はPR作成まで。テストが通れば自動マージまでやりたい

まとめ

KMPとUnity UaaLを組み合わせた複雑な構成でも、リポジトリを分けて成果物経由で連携すれば、各チームが独立して動けます。自動化は一度作って終わりではなく、運用しながら改善していくものだと実感しています。

We’re Hiring

AnotherBallでは、Kotlin/Swift/Unity/AIを活用したアプリ開発に興味のあるモバイルエンジニアを募集しています!
KMPなどの新しい技術を取り入れながら、プロダクトを一緒に成長させていく仲間を探しておりますので、ぜひ下記のリンクから応募いただけますと嬉しいです!