Findy/ファインディのブログ - TECH PLAY

TECH PLAY

Findy/ファインディ

Findy/ファインディ の技術ブログ

221

こんにちは、ファインディでFindy Team+の開発をしているrobinです。 先日ファインディで初の試みとなるエンジニアインターンが実施され、2週間メンターを務めました。前職でもインターンのメンターを何度か経験してきましたが、AI時代にインターンを受け入れるのは今回が初めてでした。 AIもあるし、前職と同じようにやればいい。始まる前はそう踏んでいました。ところが、変わったのはインターン生の進み方だけではありませんでした。 自走はAIがサポートする メンターの役割が変わる AIが働きやすい環境を整える 開発文化もオンボーディングする まとめ 課題は、以前からFindy Team+のご導入企業様から要望が数多く寄せられていた、Epic相当の既存機能の改修です。バックエンドからフロントエンドまで一通り触る必要がありました。 想定していた進め方は次のとおりです。 1日目: 環境構築とオンボーディング 2日目: コードベースと課題のキャッチアップ 1週目の残り: バックエンドの実装 2週目: フロントエンドの実装とリリース 前職での経験からしても、2週間で仕上げるには余裕のないスケジュールです。 このうち、キャッチアップに見込んでいた1日は、結果的にほとんど必要ありませんでした。 今回のインターンを通じて、特に印象に残った気づきは大きく4つです。 自走はAIがサポートする まず驚いたのは、AIを使うことでコードベースのキャッチアップがかなり速くなることでした。 プロダクトや課題の説明をした後は、AIと協業しながらほぼ独力でキャッチアップ〜タスク分解まで進めることができました。開発に着手できたのは、約3時間後です。 メンター側で用意したのは、改修の目的や対象スコープ、仕様の叩き台をIssueへ整理しておくところまでです。具体的な実装タスクへの分解は、AIと相談しながら本人に主体的に進めてもらいました。 従来のインターンであれば、コードベースの説明をしたり、実装箇所を一緒に探したりと、人が細かくサポートする時間がそれなりに必要でした。今回はそのあたりをAIが肩代わりしてくれたので、メンターが付きっきりになる必要はありませんでした。 その後も、小さなタスクへの分解と日々のゴール設定・振り返りを行いながら開発を進めてもらいました。1週目いっぱいかかると見ていたバックエンドの実装は3日で終わり、残りの期間はフロントエンドの実装とバックエンドの改修に充てて、約8日間で機能をリリースしてくれました。 プルリクエスト作成数は1日平均約6.3本、リードタイムも5時間未満と、期待していた以上のアウトプットを出してくれました。 Findy Team+の計測画面 一方で、 AIによってキャッチアップが速くなったからといって、すべてをAIに任せられるわけではない ということも分かりました。 特に、アーキテクチャや設計の歴史的経緯といったコードからは読み取れないコンテキストや、プロダクト特性、解決したい課題、ターゲットユーザーといったドメイン知識については、人によるサポートが必要でした。 実際、こうしたコンテキストの理解が十分でないまま実装を進めたことで、実装方針の認識齟齬による手戻りも発生しました。プルリクエスト作成数57件に対してマージ済みプルリクエスト数は47件で、その差分にあたる10件がマージされなかったプルリクエストです。 プルリクエスト作成数とマージ済みプルリクエスト数の計測結果 AIとの対話では、曖昧な部分を曖昧なまま進めてしまうと、そのまま実装方針に反映されてしまうことがあります。そのため、ペアプロや1on1での図解・読み合わせなど、 AIと人それぞれの得意領域を使い分けることが重要 だと感じました。 メンターの役割が変わる では、サポートの手が空いた分、メンターは楽になったのか。付きっきりで開発を見る時間は、たしかに減りました。ただ、楽になったかというと、そうではありませんでした。むしろ、 AIによってインターン生が自走できるようになることで、メンターの役割そのものも変わっていく のだと思います。 空いた時間には、開発そのものだけではなく、 プロダクトやユーザーについて考える機会を多く作ること を意識しました。 例えば、週次のレビュー会ではエンジニア以外のメンバーに向けて開発した機能を発表してもらい、UI/UXについては直接デザイナーに声をかけて議論してもらいました。さらに、Findy Team+のご導入企業様との打ち合わせにも同席してもらい、実際のユーザーがどのようにTeam+を使い、どんな課題を抱えているのかを直接聞いてもらいました。 こうした機会に対して、本人が臆することなく積極的に飛び込んでくれたことは印象的でした。開発することだけに閉じず、「もっと良いユーザー体験にできないか」「ユーザーは何に困っているのか」と視野を広げていく姿勢は、メンターとしてとても嬉しく感じました。 一方で、メンター側にも新しい難しさがありました。 AIに任せられる部分が増えたとはいえ、レビューやQA、実装方針の相談など、AIだけでは判断しづらい部分へのサポートは必要です。また、今回は短期間でのリリースを目指していたため、メンター側にも素早い意思決定やフィードバックが求められました。 それに加えて、開発を教えるだけではなく、プロダクトが提供する価値やファインディらしい仕事の進め方、AIの活用方法といった細かなソフトスキルまでサポートする必要がありました。リリースを優先する中で、こうした部分まで十分にサポートしきれなかったことは反省点です。次回は、プロダクト価値や組織文化、AIの活用方法まで含めて、より体系的に伝えられるようにしたいと思います。 こうした経験を振り返ると、AI時代のメンターは「開発を教える人」から、プロダクト価値に向き合える環境をつくる人へと役割が変わっていくのではないかと感じています。 AIが働きやすい環境を整える AIを使えば誰でもこう進むのかというと、そうとも限りません。スムーズに進んだ背景には、 AIを使うことそのものだけでなく、AIが働きやすい環境をあらかじめ整備していたこと が大きかったと思います。 実際、インターン生からも次のようなポジティブなフィードバックがありました。 カスタムコマンド、Skillsの最適化が進んでいる 「AIを使っている」のではなく「AIが働きやすい環境を整備している」 コーディングルールを意識しなくても質の高いコードを書けた ファインディには、全社横断でAI活用の効率化・推進を行う組織があります。 全エンジニアが利用できる汎用的なカスタムコマンドやSkillsをPluginとして展開しており、インターンでも初日の環境構築の一環として導入してもらいました。あわせて、普段の開発でよく使うSkillsについても共有しました。 さらにTeam+の開発では、プロダクト固有の知識や開発ルールをClaudeが参照できるよう、開発に特化したSkillsやRulesも整備しています。 これらがあらかじめ用意されていたことで、インターン生もジョイン直後からAIの力を引き出しながら開発を進めることができました。 一方で、 AIに正しく働いてもらうためには、AI自体の性能だけでなく、AIが参照する情報を継続的に整備することが重要 だという課題も見えてきました。 具体的には、次のような点です。 Rules関連のドキュメントが一部陳腐化していた 古い・非推奨なコードの整理が十分ではなかった AIが古いコードを参照し、同じような実装を生成してしまうケースがあった AIは与えられた情報をもとに非常に高速に仕事を進めてくれます。 裏を返せば、 参照する情報が古ければ、その情報をもとに高速に間違った方向へ進んでしまう ということでもあります。 AIの進化が目まぐるしい中で、常に最新のベストプラクティスに追従することは簡単ではありません。それでも、Rulesやドキュメント、コードベースを継続的に整備することは、AIの出力品質を高めるだけでなく、レビューでの無駄なやり取りや手戻りの削減にもつながります。 開発文化もオンボーディングする 整えるべきものは、AIが参照する情報だけではありませんでした。インターンでは、開発環境だけでなく、 ファインディの開発文化をキャッチアップしてもらうこと も意識しました。 ファインディでは、タスク分解やプルリクエストの作成方法など、普段の開発で大切にしている考え方やプラクティスをFindy Libraryにまとめています。 lib.findy.co.jp インターン生にも、開発を進める上で知っておいてほしい内容として、次のようなドキュメントを事前に共有しました。 lib.findy.co.jp lib.findy.co.jp こうした形で開発文化が言語化されていることで、「ファインディではどう開発するのか」をゼロから口頭で説明する必要がなく、インターン生自身が必要なタイミングでキャッチアップできることは、オンボーディングにおいて大きなメリットだと感じました。 AIがコードベースのキャッチアップをサポートしてくれる一方で、どのように開発を進めるのか、何を大切にしているのかといったチーム固有の文化までAIに任せることはできません。 AIが働きやすい開発環境と、人がキャッチアップしやすい開発文化。その両方を整えておくことが、これからのオンボーディングには重要なのだと思います。 まとめ インターンを通じて、 AIによってインターン生の自走が大きく進む一方で、メンターの役割も変わっていく ことを実感しました。 AIに任せられる部分は任せ、人はプロダクトの価値やユーザーについて考え、周囲と議論する。そうすることで、これまで以上に「何を作るか」だけでなく、 「誰のために、なぜ作るのか」まで考えるインターン にできるのだと思います。 AI時代のメンターに求められるのは、開発を手取り足取り教えることではなく、 AIと人それぞれの得意領域を活かしながら、インターン生がプロダクト価値に向き合える環境をつくること なのかもしれません。 AIが働きやすい環境や開発文化をさらに整え、より良いプロダクト開発を行えるようにすることで、今後インターンにご参加いただく方々により良い経験をしてもらえるようにしていきます。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディの開( @hiracky16 )と佐藤( @heizari )です。 2026年8月20日から22日にかけて広島国際会議場で開催された PyCon JP 2026 に参加しました。PyCon JPは日本最大級のPythonコミュニティの祭典で、Pythonを使う人、これから学びたい人、コミュニティに関わるすべての人が集まるカンファレンスです。 ファインディは今年もスポンサーとしてブースを出展させてもらいました。エンジニアも2名で参加しており、参加者の皆様と一緒にセッションを聞いたり、ブースでコミュニケーションを取ったりしてきましたので、その様子をレポートします。 PyCon JP 2026とは Findy Drinkup at PyCon JP 2026 印象に残ったセッションの内容 開: Pythonチュートリアル、venvを作ってpip installと2026年も教え続けますか? 佐藤: Retry Is Not a Strategy: Classifying and Recovering from AI Agent Failures in Python カンファレンスの様子 ファインディブース オフィシャルパーティー Open Spaceでのフリートーク お好みトーク まとめ PyCon JP 2026とは 開催日: 2026年8月21日〜22日(前日の8月20日にDay0イベントあり) 会場: 広島国際会議場 公式サイト: https://2026.pycon.jp/ja 今回は広島での開催ということもあり、地元の学生や企業の方々との交流が多いカンファレンスになりました。 Findy Drinkup at PyCon JP 2026 Findy Drinkup at PyCon JP 2026 を開催し、学生から社会人までさまざまな参加者と交流しました。 意外だったのは、社会人だけでなく学生の参加が多かったことです。プログラミングサークルのメンバーが連れ立って参加しており、研究でPythonを使っているという話や、サークル活動の一環で日頃からコードを書いているという話を聞くことができました。学生と社会人が同じ場でPythonについて語り合える空間になっていたのが印象的でした。 会話の中心になったのは、やはりPyCon JPそのものの話題です。次の日にどのセッションを聴きに行くか、気になっているトークは何かといった話で大いに盛り上がりました。カンファレンス参加をきっかけに初対面同士でも共通の話題ができるのは、Drinkupならではの良さだと感じました。 また、PyCon JP運営の方にも足を運んでいただき、直接お話しする機会もいただきました。 Drinkupにご参加いただきありがとうございました!みなさんとゆっくりお話しできて良かったです☺️🌟🌟 明日からPyCon楽しみましょう〜🐍 #pycon2026_findy pic.twitter.com/yAPIuDzoz1 — いわさき@Findy DevRel (@iwasakitchen) 2026年8月20日 印象に残ったセッションの内容 開: Pythonチュートリアル、venvを作ってpip installと2026年も教え続けますか? 2026.pycon.jp 以前、登壇者のnikkieさんに「最近、なぜみんなuvを使っているんですか? Pythonパッケージ管理の変遷と現在地」というFindy Mediaの記事へ寄稿いただいたことがあり、実は今回のセッションの中でもその記事を引用してくださっていました。この場を借りてお礼をお伝えします。 findy-code.io Findy Mediaの記事を通じて、Pythonのパッケージ管理がvenv・pip installから、poetryやrye、そしてuvへと移り変わってきた歴史的な背景を知ることができました。普段Pythonを使って開発していても、その裏にある思想の部分まで意識することはあまりなかったので、見方が変わるきっかけになった記事です。そんな背景もあり、nikkieさんの発表を楽しみにしていました。 セッションでは、venvやpoetry、rye、uvといった各ツールがそれぞれどのような課題を解決するために必要になったのか、その背景を丁寧に説明されていました。ツールが進化するにつれてできることが広がっていく様子が伝わってくる内容でした。そのうえで、タイトルの問いに対する答えは明快でした。チュートリアルでvenvを作ってpip installと教えるのはやめ、uvを勧めるという結論です。 中でも印象に残ったのが、 inline script metadata の話です。スクリプトに前提となるパッケージを直接記述して仮想環境を作れる仕組みで、 uv run script.py 一発で実行できる手軽さは、コーディングエージェントとの相性が良く、ちょっとした実験にはぴったりだと感じました。 uvの良さとして、次のような点も紹介されていました。 簡単で、動作もはやい uvを使っているだけで、できることがどんどん広げられる FastAPIをはじめ、有名なパッケージでもuvの採用が進んでいる コーディングエージェントが普及したことで「コーディングエージェントがはやいだけでなく、コーディングエージェントが使うツール自体もはやい必要がある」という指摘も印象的でした。uvが高速な理由の一つはキャッシュを活用している点で、 uv cache dir コマンドで実際のキャッシュディレクトリを確認できます。コーディングエージェントがこのキャッシュディレクトリにアクセスできる環境であることが、開発体験の速度を保つ上で重要になるという話でした。 また、サプライチェーン攻撃を踏まえると便利だと感じたのが、uvにcooldown(最新版を一定期間経ってから取得する仕組み)を設定できる点です。最近では、Flatt Security社が提供するTakumi Guardでも、設定を一つ加えるだけでプロジェクトが参照するレジストリを切り替えられるようになっており、手軽にサプライチェーン対策を導入できます。Takumi Guardの対応パッケージマネージャーとしてuvがしっかり採用されているあたりにも、uvがエコシステムの中で標準的な位置を占めつつあることが表れていると感じました。 shisho.dev uv自体にも、既知の脆弱性やマルウェアを検出するuv audit機能がプレビューとして追加されており、依存関係のセキュリティチェックをuvのワークフローに統合できる点も便利だと感じました。 astral.sh セッションのスライドと詳しい解説は、nikkieさんご本人の ブログ記事 でも公開されています。 佐藤: Retry Is Not a Strategy: Classifying and Recovering from AI Agent Failures in Python 2026.pycon.jp Cambridge University Press & AssessmentのAI Centre of ExcellenceでAIソフトウェア開発をされているCyrus Manteさんの発表です。クライアント向けにエージェントのパイプラインを構築する中で、理由を説明できない壊れ方に何度も遭遇したものの、試したフレームワークはどれも最初からやり直すか諦めるかしか返してくれませんでした。この課題を解消するために作ったのが、今回紹介されたtriage-agentです。 タイトルのとおり、問題提起は明快でした。普通のPythonの例外処理と違い、エージェントの失敗には名前がありません。例外自体は飛んでくるものの、それはSDKやツールの都合でついた名前で、エージェントが何を間違えたのかまでは読み取れません。 triage-agentは、間違ったツールを呼ぶ、スキーマが合わない、外部サービスが落ちる、黙って同じところを回り続けるといった失敗を9種類の型として定義し、エージェントをポリシーでラップして運用するフレームワーク非依存のPythonライブラリです。記録された軌跡から原因を分類し、そのまま再試行する、プランを立て直す、チェックポイントまで巻き戻す、それでも駄目なら人間にエスカレーションする、といった回復手段へ振り分けます。 導入も、既存のエージェントを包むだけで済みます。実装は公開されていて、 pip install triage-agent で試せます。 github.com 特に参考になったのは、失敗に型を与えることで、次の挙動を細かく選べるようになる、という点でした。ひとくちにリトライと言っても、そのままやり直せばいいのか、ツールの見直しを促すべきなのか、チェックポイントまで巻き戻すべきなのかは、失敗の内容によって変わります。名前がないうちは、それが全部「失敗したのでもう一度」に潰れてしまい、何度やり直しても同じところで落ちる失敗が残ります。 その名前のつけ方が、普段書いている例外処理の考え方をそのままエージェントの一段上へ持ち上げたものになっているのが上手いと感じました。新しい作法を覚え直す必要がありません。 面白いのは、その分類自体をどう解くかにも選択肢が用意されていることです。ルールベースの分類器と、LLMを使う分類器、その組み合わせが用意されていて、発表ではLLMに判断させる話が中心でした。分類は単純なタスクに閉じているぶん失敗しにくく、テストケースも自分で用意できます。Q&Aでも、Cyrusさんはこの程度の分類なら軽量なモデルで対応できるだろうと答えていました。 ユーザーの介入を減らして自律的にタスクを進めるエージェントを作ろうとすると、予期せぬ動作や失敗をどう扱うかは避けて通れません。 triage-agentでは、どの失敗にどの回復手段を当てるか、何回で人間に上げるかをポリシーとしてコード上に宣言できます。失敗ごとの対処をプロンプトに書き込んでLLMの判断に委ねるのではなく、判断はPythonの側に置いて、再試行のときに必要なヒントや状態だけを型のついた形で渡す。ここが今後のエージェント開発に活かせそうだと感じています。 カンファレンスの様子 ファインディブース ファインディブースでは、例年通りPythonクイズを実施しました。Day1・Day2それぞれで別の問題セットを用意し、来場者の方に挑戦していただきました。 Day1始まりましたね!今年もクイズを準備してます。ぜひブースにお立ち寄りください🫶 #PyConJP2026 pic.twitter.com/aHg3oZiJ6J — いわさき@Findy DevRel (@iwasakitchen) 2026年8月21日 例えば、次のような出題文を用意しました。 t = ([ 1 , 2 ], [ 3 ]) t[ 0 ] += [ 99 ] print (t) このコードを実行すると何が起きるでしょうか? A: TypeError が発生し、 t は ([1, 2], [3]) のまま変化しない B: TypeError が発生するが、 t は ([1, 2, 99], [3]) に変化している C: 例外は発生せず、 ([1, 2, 99], [3]) と出力される D: AttributeError が発生する 正解はBです。タプルの要素であるリストに対して複合代入 += を行うとどうなるかを問う問題で、次の2段階の処理に気づけるかがポイントになっています。 t[0] += [99] はまずリストの __iadd__ を呼び出し、リスト自体がその場で [1, 2, 99] に書き換わる 続けてタプルの要素へ代入し直そうとするが、タプルはイミュータブルなので TypeError が発生する つまり、例外が発生する時点ですでにリストの中身は書き換わっているため、 t は ([1, 2, 99], [3]) のまま残ります。 「難しい」という声を多くいただき、ファインディのDevRelメンバーも実際に挑戦した感想をXに投稿してくれました。 去年に引き続きむずい PyCon JP 2026「Pythonの達人を目指そう!」DAY1で 1/3問正解! 称号は「Python見習い(Python Apprentice)」でした🐍 #PyConJP2026 #PyConQuiz #pycon_findy https://t.co/41AVR7eIAD — 北川雅士@DevRel (@OnigiriMa_shi) 2026年8月21日 ブースに来ていただいた方々ありがとうございました! オフィシャルパーティー Day1の夜にはオフィシャルパーティーが開催され、PyCon JP運営からビールをプレゼントいただきました。カンファレンス初日で疲れた体に染み渡る美味しさでした! オフィシャルパーティーでは、PyCon JP 2026オリジナルビールをご用意しています🍺 ソフトドリンクも取り揃えていますよ🥤 #pyconjp2026 pic.twitter.com/w49Lz4W5WS — PyCon JP (@pyconjapan) 2026年8月21日 立食形式だったこともあり、初日にLTをされた方や他の参加者の方々とたくさんお話しすることができました。中には「ファインディのイベント見ています!」と声をかけてくださる方もいて、とてもありがたく感じました。 Open Spaceでのフリートーク Open Spaceは、参加者が自由にテーマを立てて集まり議論できる時間です。Day1に「Pythonを使ったちょっとした改善のアイデアやネタについて話したい」というテーマを投稿し、話す機会をいただきました。 集まったのは少人数でしたが、その分お互いの前提をすり合わせながら深い議論ができました。話題は昨今のAI活用から、日々の働き方の話まで多岐にわたり、Pythonを使った業務改善というテーマを起点に幅広い視点で意見交換ができたのが印象的でした。 ブースやセッション後などはどうしても関連する話題の会話が中心になりますが、こうしたフリーテーマで参加者同士が交流できる場が設計されているのはとても良いと感じました。 お好みトーク お好みトーク は、広島のお好み焼き文化にちなんで名付けられたライトニングトーク企画です。事前審査はなく、現地参加者であれば誰でも当日に発表を応募でき、実際に登壇するかは現地参加者の投票で決まります。各日3〜5本、1本あたり5分間という枠の中で発表します。 今回、このお好みトークの枠で「500名弱規模の組織のPythonプロジェクト(dbt)をどう管理するか?」というテーマで発表する機会をいただきました。 組織や事業が成長する中、各チームが独立してdbtリポジトリを構築した結果、リポジトリごとにPythonのバージョンやルールがバラバラになり、バージョンアップやセキュリティ対応のコストが線形に増えていくという課題を紹介しました。 その解決策として、共通設定リポジトリを設計するパターンを提案しました。 pyproject.toml や uv.lock をサブモジュール化して集約し、SQLのlintルール( .sqlfluff )を一元管理した上で、Taskfileで操作インターフェースを抽象化する構成です。 変わらない要素である環境や規約を部品化することで、各チームが変わる要素であるビジネスロジックに集中できる体制を作ることの重要性を伝えました。 まとめ PyCon JP 2026では、セッションを通じてPythonのエコシステムやAIエージェント開発の最新動向を学べただけでなく、Drinkupやオフィシャルパーティー、Open Space、お好みトークといった交流の場を通じて、学生から社会人まで幅広い参加者の方々と直接お話しする機会にも恵まれました。 ブースでのPythonクイズでは「難しい」という声を多くいただきましたが、その分Pythonの仕様を改めて見つめ直すきっかけになったのではないかと思います。広島での開催ということもあり、ファインディの認知度にはまだ伸びしろがあると感じた一方、「イベントいつも見ています」と声をかけてくださる方もいて、これまでの発信活動が着実に届いていることも実感できました。ファインディ主催のDrinkupやブースへ遊びに来ていただいた方々、本当にありがとうございました。 今回紹介した以外のセッションでも、AIといっしょにどう開発するか、AIを機能にどう組み込むかについて、登壇者それぞれの工夫を知ることができました。自分たちの興味の範囲に合致していた面もありますが、LLMを使って何をしたかよりも、それらをプロダクトや組織としてどう評価し、どう制御するかという話が多く印象に残り、学びの多い2日間になりました。パッケージ管理の移行やAIエージェントの失敗への向き合い方は、ファインディが複数のPythonプロジェクトを運用し、AIエージェントを活用したプロダクト開発を進める中でも、そのまま議論の材料にできる内容だと感じています。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディ株式会社でプリンシパルエンジニアをしている戸田です。 開発業務そのもののAI活用が進むにつれて、次に効いてくるのが開発以外の仕事、いわゆるトイルの対応でした。なかでも時間を取られがちなのが問い合わせ調査でした。 そこで、この問い合わせ調査をAIに任せることにしました。Slackから投げられた問い合わせに対して調査をして回答を返すエージェントを作り、AnthropicのManaged Agentsを使って、問い合わせが来たときだけ起動する仕組みにしています。 platform.claude.com この記事では、その全体構成と仕組みを紹介します。 Managed Agentsとは Managed Agentsの構成要素 全体の流れ GitHub Actions Sessions API Managed Agent Agentの管理をスキルに寄せる diff apply まとめ Managed Agentsとは Anthropicには、Claudeを使う方法が2つあります。ひとつはMessages APIで、モデルを直接呼び出して、エージェントのループも道具立ても自分で組みます。もうひとつがManaged Agentsで、そのループと実行基盤をAnthropic側に預ける形です。 預けられる範囲は広く、エージェントのループ、ツールの実行、それが動くサンドボックスまで含まれます。サンドボックスの中ではファイルの読み書き、シェルコマンドの実行、web検索、コードの実行ができます。実行環境はAnthropic側のクラウドと自前のインフラのどちらかを選べます。長い対話でのプロンプトキャッシュや文脈の圧縮も組み込みで効きます。 境界を図にすると、こういう分かれ方です。 flowchart TD client["自分たちのアプリケーション<br/>(今回はGitHub Actions)"] subgraph anthropic["Anthropicが管理するインフラ"] loop["エージェントループ<br/>Claudeの推論とツール呼び出し"] sandbox["サンドボックス<br/>ファイル・シェル・コード実行"] end mcp["MCPサーバー<br/>GitHub / Slack"] client -->|"Sessions APIでセッションを作成"| loop loop -->|"ツールを実行"| sandbox loop -->|"ツールを呼び出す"| mcp こちら側に残るのは、APIを叩くコードだけです。エージェントのループもサンドボックスもAnthropicが管理している環境で動くので、コンテナを立てることも、実行環境を保守することもありません。 もうひとつの特徴は、対話が セッション という単位で状態を持つことです。セッションは作られたあと長く生き続け、途中で止まっても再開でき、会話の履歴もサンドボックスの状態もAnthropic側に残ります。数分から数時間かかるような仕事や、リクエストを投げたら結果を待たない非同期の使い方に向いています。 今回の問い合わせ調査エージェントが使っているのは、このうちセッションの部分です。サンドボックスの道具立てはほとんど使わず、有効にしているのはGitHubとSlackのMCPツールだけで、調査も回答の投稿もそこを通ります。GitHub Actionsからセッションを1つ作って終わり、という構成にできました。 整理すると、AgentはAnthropicが管理するクラウド環境で動き、こちらは必要になったときにセッションを1つ作るだけで済みます。問い合わせが来ていない時間に動き続けるプロセスもコンテナもありません。自前で常時稼働の実行基盤を用意して同じことをやろうとすると、待っているだけの時間にも費用がかかりますが、その分が要らなくなります。いつ来るかわからず、来たときだけ数分動けばいい仕事とは相性のいい形です。 調査できる範囲は、つないだMCPサーバーで決まります。今回つないだのはGitHubなので、コードやIssue、Pull Requestに答えがある問い合わせが対象です。SlackやNotion、データベース系のMCPを足せば、過去のやりとりや社内ドキュメント、実データまで探索先を広げられます。 Managed Agentsの構成要素 Managed Agentsでは、エージェントの定義と実行環境と資格情報が別のリソースに分かれています。 リソース 役割 Agent system prompt、モデル、使うMCPサーバーとツールの定義 Environment エージェントが動く実行環境(ネットワーク設定など) Vault MCPサーバーが使う資格情報(GitHubのトークン、Slackのトークン) EnvironmentとVaultは組織でひと組だけ作り、Agentは調査対象のシステムごとにひとつ作ります。セッションを作るときに、この3つのIDを指定する形です。 全体の流れ 利用者から見た動きは単純です。Slackのワークフローで問い合わせを送ると、GitHub Issueが1件作られ、そのIssueにエージェントが回答をコメントします。Slackのスレッドにも要約が返るので、利用者はSlackから離れずに済みます。 裏側では、GitHub Actions・Sessions API・Managed Agentの3つが順番にバトンを渡しています。 sequenceDiagram participant User as 利用者 participant Slack as Slack participant Issue as GitHub Issue participant Actions as GitHub Actions participant Agent as Managed Agent participant MCP as GitHub MCP User->>Slack: 問い合わせを送る Slack->>Issue: 問い合わせIssueを作成 Issue->>Actions: issues.opened Actions->>Actions: ラベルを検証 Actions->>Agent: POST /v1/sessions Agent->>MCP: 対象Issueを読む Agent->>MCP: 定義に書かれたリポジトリを調査 Agent->>Issue: 出典付き回答をコメント Agent-->>Slack: 結論の要約を通知 Slack-->>User: スレッドで受け取る Slackは任意の入口です。GitHub上で直接Issueを立てた場合でも、ラベルが付いていれば同じように調査が走ります。調査範囲はAgentの定義に書いたリポジトリに限定していて、範囲外の質問には推測で答えず、担当範囲外である旨を返します。 GitHub Actions GitHub Actionsがやるのは2つだけです。ひとつは、起動してよい問い合わせかどうかの判定。もうひとつは、Managed Agentのセッションを作るAPIを呼ぶことです。 裏を返すと、それ以外は何もしません。AIは動かしませんし、Issue本文も読みません。リポジトリのチェックアウトもせず、セッションを作ったあとの経過を見にいくこともありません。問い合わせ内容の把握も、調査も、回答の投稿もAgent側の仕事で、ワークフローはそこに一切関与しないという役割分担です。 全文を載せます。 name : Inquiry Research Trigger on : issues : types : [ opened ] workflow_dispatch : inputs : issue_number : description : 'Issue番号を手動実行で処理する場合に指定' required : true type : string concurrency : # 同一Issueへの多重起動を防ぐ。POST中にキャンセルされると送信の成否が # わからなくなるため cancel-in-progress は false(後続はキューされる) group : inquiry-research-trigger-${{ github.event.issue.number || github.event.inputs.issue_number }} cancel-in-progress : false env : ANTHROPIC_API_VERSION : '2023-06-01' ANTHROPIC_BETA : 'managed-agents-2026-04-01' ANTHROPIC_SESSIONS_API_URL : 'https://api.anthropic.com/v1/sessions' TRIGGER_LABEL : 'inquiry-request' jobs : trigger : name : Trigger inquiry research session # job-level if では env コンテキストが使えないため、ラベル名は直書き if : >- github.event_name == 'workflow_dispatch' || contains(github.event.issue.labels.*.name, 'inquiry-request' ) runs-on : ubuntu-latest timeout-minutes : 10 permissions : issues : write # 失敗通知のコメントだけに使う。checkoutしないので contents は不要 steps : # github.event.* は env 経由で受け取り、シェルに直接展開しない - name : Guard (validate issue number) id : guard env : EVENT_NAME : ${{ github.event_name }} EVENT_ISSUE_NUMBER : ${{ github.event.issue.number }} INPUT_ISSUE_NUMBER : ${{ github.event.inputs.issue_number }} run : | set -euo pipefail if [ "$EVENT_NAME" = "workflow_dispatch" ] ; then ISSUE_NUMBER="$INPUT_ISSUE_NUMBER" if ! printf '%s' "$ISSUE_NUMBER" | grep -Eq '^[0-9]+$' ; then echo "::error::issue_number must be numeric: '$ISSUE_NUMBER'" exit 1 fi else ISSUE_NUMBER="$EVENT_ISSUE_NUMBER" fi echo "issue_number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT" # Issue本文は読まない。渡すのはリポジトリ名とIssue番号だけ - name : Build Sessions API payload env : GH_REPOSITORY : ${{ github.repository }} ISSUE_NUMBER : ${{ steps.guard.outputs.issue_number }} AGENT_ID : ${{ secrets.INQUIRY_AGENT_ID }} ENVIRONMENT_ID : ${{ secrets.INQUIRY_ENVIRONMENT_ID }} VAULT_ID : ${{ secrets.INQUIRY_VAULT_ID }} run : | set -euo pipefail MESSAGE_TEXT=$(printf '<inquiry_context>\n <github_repository>%s</github_repository>\n <github_issue_number>%s</github_issue_number>\n</inquiry_context>\n\nこの Issue の問い合わせを調査し、結果を同じ Issue のコメントとして投稿してください。' \ "$GH_REPOSITORY" "$ISSUE_NUMBER" ) jq -n \ --arg agent "$AGENT_ID" --arg env_id "$ENVIRONMENT_ID" \ --arg vault_id "$VAULT_ID" --arg repo "$GH_REPOSITORY" \ --arg issue_number "$ISSUE_NUMBER" --arg text "$MESSAGE_TEXT" \ '{ agent: $agent, environment_id: $env_id, vault_ids: [$vault_id], metadata: { source: "github_actions", github_repository: $repo, github_issue_number: $issue_number }, initial_events: [ { type: "user.message", content: [{ type: "text", text: $text }] } ] }' > payload.json # fire-and-forget: 2xx かどうかだけ確認して終わる - name : Call Sessions API id : call env : ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY_INQUIRY_RESEARCH }} run : | set -euo pipefail # `$(curl ... || echo "000")` と書くと、接続失敗時に -w の "000" と # echo の "000" が連結されて "000000" になる。|| は外に出すこと STATUS=$(curl -sS --max-time 30 -w '%{http_code}' \ -o response.json -X POST "$ANTHROPIC_SESSIONS_API_URL" \ -H "x-api-key: ${ANTHROPIC_API_KEY}" \ -H "anthropic-version: ${ANTHROPIC_API_VERSION}" \ -H "anthropic-beta: ${ANTHROPIC_BETA}" \ -H "content-type: application/json" \ --data-binary "@payload.json" ) || STATUS="000" echo "http_status=$STATUS" >> "$GITHUB_OUTPUT" if [ "$STATUS" -ge 200 ] && [ "$STATUS" -lt 300 ] ; then echo "Session created (HTTP $STATUS)." else echo "::error::Sessions API call failed (HTTP $STATUS)" # 切り分けのためレスポンス本文はジョブログにだけ残す head -c 2000 response.json exit 1 fi # 通知するのは Sessions API 呼び出しの失敗のみ。 # セッション作成後のAgentの停止は fire-and-forget のため検知しない - name : Notify on failure if : failure() env : GH_TOKEN : ${{ github.token }} ISSUE_NUMBER : ${{ steps.guard.outputs.issue_number }} HTTP_STATUS : ${{ steps.call.outputs.http_status }} run : | gh issue comment "$ISSUE_NUMBER" --repo "${{ github.repository }}" \ --body "Sessions API 呼び出しに失敗しました(HTTP ${HTTP_STATUS})" \ || echo "::warning::failed to comment" ステップは4つです。Issue番号を確定するGuard、ペイロードを組み立てるBuild、セッション作成のAPIを呼ぶCall、失敗したときだけ動くNotify。判定とPOST以外のことをしていないので、ワークフロー自体は数秒で終わります。 発火の条件は、特定のラベルが付いたIssueに限定します。ラベルを付けて起票できるメンバーが限られているなら、これだけでも起動条件として機能します。もっと絞りたければ、起票者のアカウントを条件に加えてもよいでしょう。判定を job レベルの if に置いているのは、条件を満たさない問い合わせでrunnerを起動させないためです。 Sessions API 叩くのは POST https://api.anthropic.com/v1/sessions の1本だけです。 curl -X POST https://api.anthropic.com/v1/sessions \ -H "x-api-key: ${ANTHROPIC_API_KEY}" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: managed-agents-2026-04-01" \ -H "content-type: application/json" \ -d '{ "agent": "agent_01ABC...", "environment_id": "env_01ABC...", "vault_ids": ["vlt_01ABC..."], "metadata": { "source": "github_actions", "github_repository": "Findy/example-repository", "github_issue_number": "123" }, "initial_events": [ { "type": "user.message", "content": [ { "type": "text", "text": "<inquiry_context>\n <github_repository>Findy/example-repository</github_repository>\n <github_issue_number>123</github_issue_number>\n</inquiry_context>\n\nこの Issue の問い合わせを調査し、結果を同じ Issue のコメントとして投稿してください。" } ] } ] }' ヘッダーは4つです。 ヘッダー 値 x-api-key GitHub Secretsに置いたAnthropicのAPIキー anthropic-version 2023-06-01 anthropic-beta managed-agents-2026-04-01 content-type application/json anthropic-version はAPI全体のバージョンで、Managed Agentsに限らず共通です。注意がいるのは anthropic-beta のほうで、Managed Agentsはベータのため、この値がないとエンドポイント自体が使えません。リソースのIDが正しくてもリクエストは通らないので、エラーが返ったときに最初に疑う場所でもあります。 ボディのパラメータは5つです。 パラメータ 内容 agent 起動するAgentのID。文字列で渡すと最新バージョンが使われる。バージョンを固定したい場合はオブジェクトで指定する environment_id Agentを動かす実行環境のID vault_ids MCPの資格情報が入ったVaultのID。1つでも配列で渡す metadata 監査と検索のための識別子。実行元と対象Issueを入れている initial_events セッション開始時に送るイベント。最初の user.message として <inquiry_context> を渡す agent ・ environment_id ・ vault_ids は事前に作っておいたリソースを指すだけで、モデルもsystem promptもツールの設定もここには出てきません。それらはAgent側に登録済みだからです。セッションごとに変わるのは「どのIssueに答えるか」だけ、という形になります。 vault_ids は渡し忘れに注意がいる場所です。省略してもセッションの作成自体は成功しますが、GitHub MCPが認証できず、AgentはIssueを読むことも回答を投稿することもできません。エラーにならないぶん気づきにくい失敗です。 initial_events に入れる <inquiry_context> の中身は、リポジトリ名とIssue番号の2つだけです。 質問の中身は入っていません。 POSTのレスポンスが2xxかどうかだけを確認して、ジョブは終わります。セッションが作られたあとのAgentの動きはポーリングしません。 Managed Agent Agentが受け取るのは <inquiry_context> だけです。つまり、リポジトリ名とIssue番号の2つ。何をする役割なのか、どこを調べてよいのか、どう答えるべきかは、すべてAgentの定義に登録済みのsystem promptに書いてあります。セッションのたびに送るものではありません。 セッションが作られると、その定義でAgentが起動します。Vaultに入れておいた資格情報でGitHub MCPに接続され、Agentは次の順番で動きます。 <inquiry_context> が指すIssueを読み、本文と既存コメントから質問内容を把握する 定義に書かれたリポジトリを調査する 質問の中身を知るところからがAgentの仕事、というのがこの構成の要です。ワークフローは「どのIssueに答えるか」しか渡していないので、何を聞かれているのかはAgent自身がIssueを読んで判断します。 調査の仕方も決めています。検索結果のタイトルや要約だけで判断せず、実際のコードやIssue本文、Pull Requestのdiffといった一次情報に到達するまで掘り下げます。範囲外の質問には推測で答えず、担当範囲外である旨を返します。 調査が終わると、その結果を問い合わせ用のIssueにコメントとして投稿します。 Issue本文にSlackの宛先情報が入っていれば、スレッドにも要約を返します。返すのは結論の要約とIssueへのリンクだけで、回答の全文は転記しません。GitHub上で直接起票された問い合わせのように宛先情報がない場合は、Slackへの通知を行わずIssueコメントだけで完了します。 Agentの管理をスキルに寄せる Agentの定義は、一度作って終わりではありません。調査対象のリポジトリが増えれば書き換えますし、system promptを直すこともあります。調査対象のシステムごとにAgentがあるので、対象が増えるほど触る回数も触る人も増えます。 そこで、考え方をIaCに寄せました。Agentの定義はファイルとしてリポジトリに置いておき、そちらを正とします。Anthropic側の状態を直接いじるのではなく、ファイルを書き換えてから差分を確かめ、承認して反映する。Terraformを触ったことがあれば、そのまま馴染む流れだと思います。 この一連の操作をClaude Codeのスキルにまとめ、引数で切り替えられるようにしました。差分を見る diff と、反映する apply の2つです。 定義をリポジトリに置いたことで、Agentの設定変更が普通のPull Requestになりました。誰がいつ何を変えたかはコミット履歴に残りますし、調査範囲を広げるような変更はレビューを通ります。 対象のリポジトリにAgentの定義ファイルを置いて、スキルを実行します。定義はこういう形です。 { " name ": " example-agent ", " description ": " GitHub Issueに起票された問い合わせを調査し、出典URL付きの回答を同じIssueのコメントとして投稿するAgent ", " model ": " claude-sonnet-5 ", " mcp_servers ": [ { " name ": " github ", " type ": " url ", " url ": " https://api.githubcopilot.com/mcp/ " } , { " name ": " slack ", " type ": " url ", " url ": " https://mcp.slack.com/mcp " } ] , " tools ": [ { " type ": " mcp_toolset ", " mcp_server_name ": " github ", " default_config ": { " permission_policy ": { " type ": " always_allow " } , " enabled ": false } , " configs ": [ { " name ": " issue_read ", " enabled ": true } , { " name ": " search_code ", " enabled ": true } , { " name ": " add_issue_comment ", " enabled ": true } ] } , { " type ": " mcp_toolset ", " mcp_server_name ": " slack ", " default_config ": { " permission_policy ": { " type ": " always_allow " } } } ] , " system ": " あなたはFindy社内の問い合わせ調査エージェントです。…… \n\n ## 調査対象リポジトリ(この範囲に限定) \n\n - Findy/example-system-repo \n " } diff 最初に前提を確認します。定義のもとになるファイルがあるか、 ant (Anthropic公式のCLI)が入っているか、認証は通っているか。ここまでAnthropicへの通信は発生しません。 platform.claude.com 次にファイルを読んでAnthropicへ送る形に整え、送る前に検査をかけます。Agent名が決めた名前空間に収まっているか、組み込みのツールセットが紛れ込んでいないか、MCPサーバーのURLが許可リストに入っているか。ひとつでも引っかかれば異常終了するので、危ない定義はそもそも先へ進みません。 ここまで通ったら GET /v1/agents で一覧を取り、Agent名と完全一致するものを探します。一致するものが無ければ、まだ作られていないということなので新規作成の対象として扱います。1件あれば GET /v1/agents/{id} で現物を取り、手元の定義と突き合わせて差分を出します。2件以上見つかった場合は、どちらを対象にすべきか判断できないので何もせずに中断します。 apply diff と同じところまで進んだうえで、差分を提示して承認を求めます。承認されなければ何も書き込まずに終わります。 承認されたら、新規なら POST /v1/agents で作成し、既存なら POST /v1/agents/{id} で更新します。更新には取得したときのバージョン番号を渡します。取得してから書き込むまでの間に他の人が更新していれば、そこで失敗します。 更新が成功すると、Agentは新しいバージョンとして登録されます。既存のバージョンが書き換わるのではなく、版が1つ積まれる形です。動いているセッションは作られたときのバージョンのまま動き続けるので、更新の影響を受けるのは次に作られるセッションからになります。 書き込んだあとは、現物を取り直して手元の定義と一致することと、その名前のAgentがちょうど1件であることを確認します。 この確認を通ってから成功と報告します。 まとめ Managed Agentsを使うと、自分たちで常時稼働のインフラを用意することなくエージェントを動かせます。必要になったときにセッションを1つ作るだけで、エージェントのループもサンドボックスもAnthropicが管理している環境で動きます。待っているだけの時間に費用がかからないので、いつ来るかわからない仕事とは相性がいい形でした。 Agentの定義も、工夫次第でIaCのように運用できます。定義をリポジトリのファイルとして持ち、差分を見てから適用する形にしておけば、変更はPull Requestとして残りますし、対象が増えても手順は変わりません。 社内のトイルを減らす取り組みとして、なかなか具合のいい形に落ち着きました。似たような問い合わせ対応を抱えている方は、ぜひ真似してみてください。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディ株式会社でモバイルエンジニアをしている加藤です。 技術カンファレンス向けのモバイルアプリ「Findy Events」を、React Nativeで開発しています。iOSは App Store 、Androidは Google Play で公開しています。 Findy Conferenceチームでは、職種を問わず、プロダクトを作っている本人がカンファレンスの会場に立ち、参加者へのユーザーヒアリングを実施しています。私も AI Engineering Summit Tokyo 2026 の会場に立った1人です。この会場でチームとして集めた101人分の声から、ナビゲーション構造の作り直しと、次のカンファレンスでの仮説検証まで一周させました。 この記事では、ユーザーヒアリングを通して得られた課題から、どう仮説を立てて検証したのかについて書きます。 モーダルの中にタブバーを置いていた当初の画面構成 101人へのヒアリングで見つかった2つの発見 見た目と文言を直す案に残った引っかかり ターゲットペルソナの認識をチームで揃え直す Appleのガイドラインを読み直して見えた構成の問題 起動直後の画面を直近のカンファレンスのホームに変える 96人への再ヒアリングの結果 まとめ モーダルの中にタブバーを置いていた当初の画面構成 Findy Eventsには、当初からUI/UXの課題感がありました。それは、本来トップレベルに置くべき「タブバー」を一時的なモーダルの中に置いてしまったことです。 アプリを起動すると、参加予定のカンファレンスが並ぶチケット一覧が表示されます。カードの下には「入場チケットを表示」ボタンが並んでおり、タップすると入場用のQRコードが開きます。カード自体をタップした場合はモーダルで詳細画面が開き、そのモーダルの中に、ホームやタイムテーブルを行き来するタブバーを配置していました。 ※本記事に掲載している画面はすべてテスト環境のもので、カンファレンス名や日付はテストデータです。 タブバー は、アプリのトップレベルのセクション間を移動するためのものです。一方で モーダル は、今の画面の上に一時的に重なり、閉じれば消える層であって、トップレベルのセクションではありません。冒頭に書いた課題は、この定義のズレのことです。 Androidで同じ役割を担う ナビゲーションバー も、アプリの画面をまたいで一貫した行き先を並べるものだとされています。モーダルの中にだけ現れる置き方は、こちらの条件も満たしていません。 この構成を選んだ理由は、ユーザーがカンファレンスに参加する前と当日とで、欲しい情報が異なると考えていたためでした。参加前であれば申し込んだカンファレンスやおすすめのカンファレンスを、当日であれば参加するカンファレンスの情報だけを見たくなるはずだ、という想定です。 1stビューはどちらか一方にしか倒せません。両立させるために取ったのが、この苦肉の構成でした。カンファレンス当日もチケット一覧から該当のカンファレンスをタップすれば見たい情報にはたどり着けますし、モーダルの中の操作も使い始めれば慣れるはずだと考えていた側面もあります。 つまり、原理原則から外れていることは理解した上で、代わりの案を出せずにいました。そして開発を続けるうちに、開発者である自分自身がその操作に慣れてしまい、違和感は少しずつ薄れていきました。 101人へのヒアリングで見つかった2つの発見 違和感が薄れたまま迎えたのが、2026年6月8日から9日にかけて開催されたAI Engineering Summit Tokyo 2026です。この会場で、チームで手分けして101人に話を聞きました。 「アプリを見て使いにくかった点や、もっとこういう機能があれば嬉しいなどあれば教えてください」というオープンクエスチョンでヒアリングしました。まだアプリを使っていない方には持ち込んだデモ機をお渡しし、操作していただきながら答えていただく形です。 この質問を用意しながら、内心期待していたのは機能要望でした。あの情報が足りない、こういう画面が欲しい、といった声が集まるものだと考えていたのです。 実際に多かったのは、カードをタップすると開く詳細画面やタイムテーブルの存在に気づかれていない、という話でした。デモ機で見られたのは、一覧を眺める、上下にスクロールする、「入場チケットを表示」からQRコードを出す、設定画面を開く、といった操作です。カードをタップしてモーダルを開くところには至っていませんでした。モーダルの先で既に提供している情報が、そのまま「あったら嬉しい」という要望として挙がることもありました。 もうひとつ、モーダルのタブバーの「ホーム」を連打する様子が見られました。その意図を尋ねたところ、「ホームに戻りたいから」とのことでした。つまり、チケット一覧画面をホームと捉えていた、ということです。モーダルを閉じる導線は左上にありましたが、選択肢として認識されていませんでした。ユーザーが思い描いていた動きと実態のズレです。 この2つから、薄れていた違和感を改めて突きつけられました。使い慣れた開発者に見えなくなっていただけで、構成そのものの問題は残っていたわけです。 見た目と文言を直す案に残った引っかかり 最初に出した答えは、モーダルに気づいてもらうためにチケット一覧のカードをタップできる見た目に寄せることと、タブバーの「ホーム」という項目名を実態に合わせて連打をなくすよう誘導することでした。会場で見えた2つの症状に、それぞれ手を打つ形です。 ただ、この案では、モーダルの中にタブバーがあるという構成そのものは変わりません。気づいてもらえるかどうかを見た目の強さに委ねているだけで、なぜ気づかれなかったのかには答えられていない、という引っかかりが残りました。 ターゲットペルソナの認識をチームで揃え直す 立ち返ったのは、誰に向けたアプリなのかというところでした。 Findy Eventsが向き合うのは技術カンファレンスの参加者で、参加の頻度は年に数回です。前回の利用から数ヶ月空いた状態で起動されることを前提にすると、原理原則から乖離した構成は、アプリを開くたびにユーザーへ再学習を求めることになります。会場で見えたのと同じことが、次の機会にも起こる可能性があるということです。 Appleのガイドラインを読み直して見えた構成の問題 参考にしたのが、Appleの Human Interface Guidelines です。設計の原則がいくつか挙げられており、そのうちPurpose、Agency、Familiarityの3つが、当時の構成を見直す手がかりになりました。 Purposeは、人々がどう使いたいかに沿って最も重要な機能を絞り込むという原則です。参加前と当日の両方を1stビューで抱えようとしていたため、どちらの利用目的にも焦点が合っていませんでした。 Agencyは、ユーザーが主導権を持てるようにするという原則で、目の前のタスクやコンテンツに直接導くこと、特定のフローやモードに閉じ込めないことが述べられています。チケット一覧から段階を踏む構成は当日必要な情報へ直接導けておらず、モーダルを閉じる導線が認識されないままホームを連打していた状態は、ユーザーの行動を不自然な形で閉じ込めているのと変わりません。 Familiarityは、人々が既に知っている概念を使うという原則です。現実世界や他のソフトウェアで得た知識を持ち込むことで、インターフェースは馴染みやすく直感的になるとされています。年に数回の利用が前提のアプリで頼れるのは、アプリの中で積み上がる学習ではなく、人々が普段から慣れ親しんだ知識や経験のほうでした。 最初の案に残っていた引っかかりの正体は、ここにありました。単に表面を整えるだけでは、このPurpose、Agency、Familiarityはどれも変わりません。構成そのものを見直す必要があると考えました。 起動直後の画面を直近のカンファレンスのホームに変える ターゲットペルソナを踏まえると、アプリが起動されるのはカンファレンス開催日の直前から当日である可能性が高いと考えられます。それであれば、参加当日に欲しい情報へ直接導くのが妥当です。代案を出せずにいた「参加前と当日のどちらに倒すか」も、ここで決着がつきました。 そこでアプリ起動時の1stビューを、直近の参加予定カンファレンスのホーム画面に変更しました。これまでモーダルの中に置かれていた画面が、そのまま最初に表示される形です。結果として、モーダルの中にタブバーがある構成も解消されました。 変更前は起動後にチケット一覧が表示され、そこから先に進む必要がありました。変更後は、当日必要になる情報が最初から表示されます。 もちろん、他に参加予定のカンファレンスの情報などへの導線は残しています。行き先を塞いだわけではなく、あくまでも1stビューで最も必要な情報へ導くことを意識した整理になります。 なお、この変更はAndroidの Principles of navigation でいうstart destinationを選び直したことにあたります。Material DesignにHIGの3原則と直接対応するものはありませんが、Androidのナビゲーション原則から外れる判断ではありませんでした。 96人への再ヒアリングの結果 1stビューを変えることで、機能の見落としや、ユーザーが思い描く動きと実態のズレを減らせるはずだ。この仮説を持って、2026年7月22日から23日に開催された AI DevEx Conference 2026 の会場で、同じくチームで手分けして96人に話を聞きました。 1回目は「画面遷移に気づかない」という趣旨の声が15件以上あがり、タブバーの「ホーム」を連打する様子も見られました。2回目は、その声は0件で、連打する様子も見られませんでした。 ただし、これをもって因果の断定はできません。同一人物に聞き直したわけではなく、カンファレンスが異なるため参加者の属性も揃っていないためです。言えるのは、101人と96人というほぼ同規模のヒアリングで、仮説を否定する材料は出てこなかった、というところまでです。 まとめ 改めて感じたのは、現場に出て直接ユーザーの声を聞くことの大切さでした。違和感そのものは、チームの中でも気づけます。ただ、それが本当に直すべき問題なのかまでは確信が持てず、時間とともに薄れていきました。実際に使っていただき、その言葉と操作に触れたことで、はじめて確信に変わりました。 そうして一周させた結果、「恒常的に起動されるアプリではない場合ほど、原理原則に準ずることが効いてくる」という手応えを得ることができました。日常的に起動しないアプリでは、ユーザーが他のアプリを通じて既に学習している使い方をそのまま持ち込めれば、自然に使えるアプリに近づきます。 会場で聞いて直し、次の機会に確かめる。この形で仮説と検証を回しながら、引き続き会場に立ち、聞いた声を改善につなげていきます。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディ株式会社でプリンシパルエンジニアをしている戸田です。 2026年8月5日(水)に、Findy AI Meetup in Fukuoka #7を福岡で開催しました。当日参加くださったみなさま、ありがとうございました! findy-inc.connpass.com 今回のテーマは「AI開発の"今まで"と"これから"を語り尽くそう」です。 皆さまの熱いご支援のお陰で、Findy AI Meetup in Fukuokaは今回で1周年を迎えました。節目となる今回は、AI開発のこれまでを振り返りつつ、これからを見据える内容の登壇が揃いました。 この記事では、ファインディメンバーによる2つの登壇を振り返ります。 Findy AI Meetup in Fukuokaについて え?フロントエンドエンジニアのワイがインフラも!? 「開発は一人です」から始まった新規サービス 武器は社内に揃っていた 2つの誤算 変わったこと、変わらなかったこと VibeCodingからAgenticWorkflowへ 速くなったのは開発工程ではなくコード生成 レベル1「速く作る」— VibeCodingと現場の課題 レベル2「正しく作る」— 協働から委任へ ターミナルへの回帰 — 開発環境そのものが変わった 順番を間違えない — 基本が先、AI活用は後 まとめ Findy AI Meetup in Fukuokaについて Findy AI Meetupは、ファインディのエンジニアが主催する技術系オフラインイベントです。 生成AIやAIエージェントの活用を通じた開発生産性の向上をテーマに、社内での実践事例の紹介やエンジニア同士の交流を目的としています。 福岡での開催は今回で7回目、そして1周年となりました。この1年でAI開発の景色は大きく変わりましたが、回を重ねるごとに参加者同士の交流も深まり、福岡のエンジニアコミュニティとして根付いてきたことを実感しています。 え?フロントエンドエンジニアのワイがインフラも!? まずは、フロントエンドテックリードの新福による登壇です。フロントエンドエンジニアが生成AIとともに専門外の領域へ越境した実体験をお話ししました。 speakerdeck.com 「開発は一人です」から始まった新規サービス きっかけは、新しいサービスの立ち上げでした。ファインディでは生成AI時代のプロダクトが次々に生まれており、立ち上げ期のプロダクトは少数精鋭・スピード重視で職種の枠に囚われないフルサイクル志向となることが多いです。 そんな中で、新規サービス開発を任されることになりました。ただし、人員の確保が難しいため開発は一人です。これまでなら、良いアイデアがあっても専任のメンバーが足りなければ諦めるしかありませんでした。 しかし、生成AI時代ならそれも可能かもしれません。そう考えて、フロントエンドはもちろん、バックエンド、そして完全に初見のインフラ(AWS/Terraform)まで、全部を一人で担当することにしました。 領域 内容 フロントエンド 本来の専門領域 バックエンド Honoを採用、テーブル設計などは専門メンバーによるレビュー インフラ(AWS/Terraform) IAM、VPC、ECS、RDSなど、SREチームによるレビュー 武器は社内に揃っていた 挑戦を支えたのは、弊社SREチームが整備していた社内資産でした。Terraformやバックエンド側APIのスターターキット、社内標準の構成を再利用可能な形にまとめたAWS汎用モジュール、そして環境構築を手助けするエージェントスキルです。 これらの社内資産を活用し、社内の作法に沿ったやり方でAIが書き、人間がレビューして判断するという流れで開発を進めました。 結果、アプリケーション(フロントエンド+バックエンド)に約1ヶ月、完全に専門外のインフラに約1ヶ月、合計約2ヶ月で一人で作り上げることができました。 2つの誤算 一方で、やってみて見えてきた誤算もありました。 1つ目の誤算は「時間」です。 当初は生成AIによる開発期間の短縮を見込んでいましたが、実際は専門メンバーやSREチームによるレビュー待ち、そして試行錯誤のたびに発生するTerraformの反映待ちが作業時間の相当部分を占めました。フィードバックを待つ必要があるものはAIでの高速化が難しく、AIが圧縮したのはあくまで「人間が考える時間」だったのです。 2つ目の誤算は「理解」です。 自分で書いていたときは、仕組みや依存関係を把握しなければそもそも書けないため、両者は常にセットで、疑う機会すらありませんでした。ところが生成AIに書かせると、内容を把握せずとも書けてしまいます。そのまま進めると、なぜ動くか、あるいは動かないかがわからず、インフラでこれは致命的となり得ます。だからこそ、浮いたはずの時間の一部を使って、生成AIの出力を読んで理解する時間を確保する必要がありました。まさに「思考は外注できるが、理解は外注できない」を実感した経験となりました。 変わったこと、変わらなかったこと 生成AI時代では、着手するまでの意思決定コストが下がりました。これまで人員確保がネックになっていた部分も、生成AIが実装や意思決定のコストを下げたことで、諦めなくても良くなったのです。 逆に変わらなかったのは、最終的な責任を持つのは人間だということです。生成AIの出力を判断するためには、結局のところ実装者自身が仕組みを理解し、専門知識を身につけなくてはいけません。 生成AIの発展により、越境しやすい環境になりましたが、専門知識の価値は変わりません。「理解は外注できない」という事実をしっかりと頭に留め、基礎を大事にするというのがこの挑戦から得られたものでした。 VibeCodingからAgenticWorkflowへ 続いて戸田からは、ファインディがこの1年で歩んできたAI活用の変遷を「VibeCodingからAgenticWorkflowへ」と題してお話ししました。 speakerdeck.com 速くなったのは開発工程ではなくコード生成 出発点は、AIを導入して見えてきた現実です。AIが高速化したのは「コードを書く」工程だけで、レビューや検証といった「正しいか」の確認に時間がかかり、開発フロー全体のスループットは横ばいのままでした。これは体感ではなく、可視化した数値が示した事実です。 速くコードを書けても、理解せずに生成されたコードは質が落ち、レビューでの指摘が増え、結局速さの恩恵が消えてしまう。この連鎖を断ち切るために、ファインディでは「正しい作り方と手順」をハーネス化する開発フロー改革に取り組み、AI活用レベルを3段階に分けて段階的に進めてきました。 レベル テーマ 内容 レベル1 速く作る コード生成の自動化 レベル2 正しく作る モノ作り全体の再設計 レベル3 必要なものを作る 他領域への越境 先ほどの新福の登壇は、まさにこのレベル3「他領域への越境」を体現した実例です。ここからは、そこへ至るまでのレベル1とレベル2の道のりを紹介します。 レベル1「速く作る」— VibeCodingと現場の課題 レベル1は、VibeCodingでコードを生成し、Pull requestを作成してレビュー依頼を投げるところまでをAIで自動化するフェーズです。 ただしこのフェーズでは、現場で次のような課題が起きていました。活用レベルの個人差が大きい、AI出力の合否判断ができないまま理解せずにレビュー依頼を出してしまう、Pull requestの質が低下してリードクラスのレビュー負担が増える、そしてAI主導になり人間側の理解が追いつかない「AIに使われている」状態です。 この課題に対して、まずAIが参照するドキュメントやルールを整えるガードレール整備を行いました。READMEやプロジェクトドキュメントで前提や運用ルールを記述し、AGENT.mdやrulesでコード規約・命名規則・テスト方針をAIに参照させ、よくある作業はカスタムコマンドとして規格化する。ガードレールがあって初めて、AIは「使い物になるコード」を出してくれます。 レベル2「正しく作る」— 協働から委任へ レベル2では、正しい方法と手順を用意して、AgenticWorkflowに委任します。 このフェーズの課題は、要件を実現する手順がAIフレンドリーではないことでした。タスクの粒度や手順を誰も決めておらず、生成AIへ何を渡せば精度よく動くかが属人化している。つまり、明確で簡潔なステップ構造、AIに渡す「設計図」が必要だったのです。 そこで、AIが処理しやすい単位へのタスク分解、構造化された設計図を親子Issueで表現するIssue作成、そしてAIと人間でレビュー領域を分割するコードレビューの再定義に取り組みました。人間はレビューで「作り方と実現方法が合っているか」を検証し、設計図にフィードバックする。タスク分解の品質が、そのままアウトプットの品質を決めます。 このレベル1からレベル2への移行は、AIとの関係性の変化でもあります。登壇では「協働」して書くVibeCodingと、「委任」して任せるAgenticWorkflowの違いを次のように整理しました。 観点 AIとの協働(レベル1 VibeCoding) AIへの委任(レベル2 AgenticWorkflow) 関係性 隣で並走するパートナー タスクを任せる実行者 人間の役割 ハンドルを握る運転手 行き先を決める指揮者 AIの役割 助手席のナビゲーター 自走する実行エージェント 任せる粒度 1行〜1関数 タスク/PR/フロー全体 AgenticWorkflowとは、人間がゴールと制約を与え、AIエージェントが計画・実行・自己検証までを自律的に進める開発スタイルです。ゴール指向、計画と分解、ツール使用、自己検証ループという4つの自律性を備えており、人間の仕事は成果物に対するレビューへと移っていきます。 ターミナルへの回帰 — 開発環境そのものが変わった AI委任の並列性は、開発環境そのものも変えました。2026年からファインディではメインツールがIDEからターミナルへ移行しています。 1ウインドウで1タスクずつ進めるスタイルから、複数ウインドウ・ペインで同時にAIへ委任するスタイルへ。IDEの役割は「すべての開発作業を行う場所」から「広域に渡るコードリーディングで理解を深めるとき」に使うものへと変化しました。AIに並列で任せる前提に合わせて、開発環境が「並列委任しやすいもの」へ変わってきているのです。 順番を間違えない — 基本が先、AI活用は後 最後に強調したのは、順番を間違えないことです。土台が弱いと、ガードレールもAIも成果を出せません。 統一規約・型定義・テストコードといったコード品質をまず充実させ、Pull requestの粒度やレビュー文化といった開発文化を育てる。その上でガードレール・ハーネスを整備し、最後にAI SkillやPluginを横展開して組織全体でAI活用を加速させる。基本が固まってからAI活用を載せる、この順番が重要です。 AI時代の本丸は「速く作る」ではなく、「正しく作る」「必要なものを作る」への段階的な越境です。人間の役割はコードの読み書きから、何をどう作るかの判断へと上流に移っていきます。それでも、やるべきことはAI以前から変わりません。基本の徹底こそがAI活用の大前提なのです。 まとめ 今回のテーマは「AI × これまでと、これから」でした。 2つの登壇に共通していたのは、AIによって変わったことと変わらないことの整理です。変わったのは、AIに任せられる範囲です。職種の壁を越えることが現実的な選択肢になり、協働から委任へと関係性も進化しました。変わらないのは、理解と責任が人間に残ることです。AIにどれだけ委任しても、出力を判断する専門知識と基本の土台がなければ、AIは成果を出せません。 この1年でVibeCodingという言葉が当たり前になり、いまはAgenticWorkflowへの移行が始まっています。これからも変化は続きますが、基本を固め、理解を手放さず、段階的に任せる範囲を広げていく。この姿勢は変わらないと考えています。 なお、登壇でも紹介したファインディの開発知見は、ドキュメントサイト「Findy Library」で公開しています。開発の基本からVibeCodingやAgenticWorkflowの実践まで、今回の登壇のベースになっている知見をまとめていますので、ぜひ活用してみてください。 lib.findy.co.jp そして早くも次回開催が決定しました!2026年11月12日(木)に開催予定です。次回は「AI × 並列開発 AIに委任して変わりゆく開発手法」と題しまして、AIへの委任で変わっていく開発手法について語り合う会にしたいと思っています。今回の登壇でも触れた「並列委任」をさらに深掘りするテーマです。ぜひご参加ください。 findy-inc.connpass.com ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディ株式会社でプリンシパルエンジニアをしている戸田です。 先日、GitHubのスタックプルリクエスト(Stacked Pull Requests)がpublic previewになりました。依存し合う変更を複数のプルリクエストに分けたまま、順番に積み上げてレビュー・マージできる機能です。 この記事では、スタックプルリクエストの仕組みと gh stack でのプルリクエストの積み方、GitHubの画面でのレビューとマージの流れを紹介します。最後に、ファインディでAIエージェントの並列実装に組み込んでいる例にも触れます。 スタックプルリクエストの仕組み gh stackでプルリクエストを積む GitHubの画面でレビューとマージを進める ファインディでの活用 まとめ スタックプルリクエストの仕組み 大きな変更を1つのプルリクエストにまとめるとレビューが重くなります。かといって分割すると、後続の差分に先行分の変更まで混ざってしまい、そこで何を変えたのかが読み取れなくなります。テーブル定義とサービス層とAPIをまとめて出すか、テーブル定義のマージを待ってからサービス層を出すか、という二択になりがちでした。 スタックプルリクエストは、baseブランチを1つ前のプルリクエストのブランチにすることでこの二択を解消します。 main ← #301(テーブル定義) ← #302(サービス層) ← #303(API) main をbaseにするのは一番下だけで、2番目以降は直前のプルリクエストのブランチをbaseにします。各段の差分はそこで加えた変更に限定されるため、レビュアーは1段ずつ読めば済みます。 この積み方自体はGitHubに固有のものではありません。baseを指定するだけなので、以前から手動でも運用できました。ただし手作業でやる場合、レビュー指摘で下の層が変わるたびに、その上のすべてのブランチをリベースし直してbaseを付け替えることになります。段数が増えるほど付け替えの手間が積み上がるため、選びにくい手法でもありました。 GitHubが対応したことで、この付け替えが自動化されました。下位のプルリクエストがマージされると、残りのブランチは自動的にリベースされ、その上のプルリクエストのbaseが付け替わります。手元でbaseを張り替えたり、マージ済みの変更を取り込み直したりする作業は発生しません。 もう1つの変化は、一連のプルリクエストがひとつのStackとして紐付いていることをGitHub自身が認識するようになった点です。プルリクエストがStackのどの位置にあるかをGitHub側が把握しているため、画面上でも積み上がりを追えますし、CLIからもStackを単位として操作できます。手元のブランチと自分の頭のなかだけで依存を管理していた頃と比べると、この差は小さくありません。 gh stackでプルリクエストを積む 操作はGitHub CLIの拡張機能で行います。 gh extension install github/gh-stack まずスタックを初期化します。実行すると1段目のブランチ名を聞かれます。 gh stack init あとは普段どおり変更してコミットします。ここまでは通常のブランチ運用と変わりません。 git add . git commit -m "テーブル定義を追加" 2段目からが本番です。 gh stack add で新しいブランチを作ると、いま居るブランチをbaseにして積み上がります。 gh stack add feature/service git add . git commit -m "サービス層を追加" ブランチの作成からコミットまでは -Am でまとめられます。段を重ねるときはこちらのほうが楽です。 gh stack add -Am "APIエンドポイントを追加" feature/api 積み終わったらプルリクエストを作ります。 gh stack submit は全ブランチのpushとプルリクエスト作成、baseの設定をまとめて行います。1つ目は main 、2つ目は1つ目のブランチ、という具合にbaseが設定されます。 gh stack submit すでにブランチを作ってしまっている場合は、 gh stack init に既存ブランチを下から順に並べて渡せば、そのまま採用できます。 gh stack init --base main feature/table feature/service feature/api GitHubの画面でレビューとマージを進める プルリクエストを作ると、画面の上部にスタックアイコンと、それが何段目かを示す番号が表示されます。マージボックスにはスタックマップが出て、Stackに含まれる全プルリクエストとその状態を一覧できます。ここから任意の段へワンクリックで移動できるので、レビュー中に上下の変更を確認したくなったときに便利です。 3段のStackを作った直後のスタックマップが次です。下から順に積み上がり、一番下が main につながっていることが読み取れます。この時点ではまだレビューが済んでいないため、各プルリクエストにNot readyと表示されています。 マージは一番下から行います。Stack全体を一度にマージすることも、1つのプルリクエストだけをマージすることも、途中まで部分的にマージすることもできます。マージコミット、スカッシュ、リベースマージのいずれにも対応しています。 レビューが進むとapprove済みのプルリクエストにReadyが付きます。次は1段目と2段目がapproveされ、3段目がまだレビュー中という状態です。 開いているのは2段目のプルリクエストです。ここでMerge stackを押すと、この2段目と下の1段目、あわせて2本がマージされます。ボタンの数字がそのままマージされる本数です。approveされていない3段目はマージされず、開いたまま残ります。残ったプルリクエストのbaseは自動的に付け替えられるので、手元で何かする必要はありません。 3段目のレビューが終わったあとに3段目のプルリクエストでMerge stackを押せば、Stack全体が一度にマージされます。段ごとに小さくレビューを受けて、取り込みは都合の良いタイミングでまとめて済ませる、という進め方ができます。 一方で、手元のブランチはマージの結果を知らないままです。ここで gh stack sync を実行すると、リモートの取得からtrunkの更新、残ったブランチのカスケードリベース、pushまでを一度に済ませてくれます。 gh stack sync ファインディでの活用 ファインディでは、親Issueに紐づくSub-issueをAIエージェントが並列で実装し、プルリクエスト作成まで進める仕組みを運用しています。 tech.findy.co.jp この仕組みでは、依存関係のあるIssueをどう扱うかが設計上の要になります。たとえば4つのSub-issueが次のような依存関係になっているとします。#201が終われば#202と#203は並行して進められますが、#204は両方の完了を待つ必要があります。 #201 |-- #202 --+ | +-- #204 +-- #203 --+ 以前はこの依存をレイヤーに分け、同じレイヤーのIssueを並列で実装していました。次のレイヤーに進む前に、下位レイヤーのプルリクエストが全てマージされるのを待つ同期の仕組みを自作していたためです。 Layer 0: #201 を実装 → プルリクエスト作成 ↓ Layer 0 のプルリクエストがマージされるまで待機 Layer 1: #202 と #203 を並列実装 → プルリクエスト作成 ↓ Layer 1 のプルリクエストが全てマージされるまで待機 Layer 2: #204 を実装 → プルリクエスト作成 後続の実装が常に最新の main から始まることは保証できます。一方で、実装が終わっていても人間のマージを待つ時間が2回入ります。 スタックプルリクエストに置き換えてからは、実装の進め方とプルリクエストの積み方を分けて考えるようになりました。#202と#203は互いに依存していないので並列で同時に実装し、あとから依存の順に直列化して1本のStackにします。 #201 を実装 ↓ #202 と #203 を並列で実装 ↓ #204 を実装 ↓ 依存の順に直列化してプルリクエストを一括作成 main ← #201 ← #202 ← #203 ← #204 ↓ エージェントはここで終了 人間が下から順にレビューしてマージ(上位プルリクエストは自動でリベース) 実装を終えたブランチを依存の順につなぎ直し、 gh stack でプルリクエストを一括作成した時点でエージェントの仕事は終わりです。下位がマージされれば上位はGitHub側でリベースされるため、待つ理由がありません。レイヤーの間にあった2回の待機はなくなり、レビューとマージは人間の都合で進められるようになりました。自作していた同期の仕組みは削除しています。 まとめ スタックプルリクエストとAIエージェントによる並列実装は、相性の良い組み合わせだと感じています。エージェントは依存の順に手を動かすこと自体は得意で、待ち時間を作っていたのは人間のレビューとマージのタイミングでした。先に積んでおけばその待ちがなくなるので、実装のサイクルとレビューのサイクルを切り離せます。 スタックの考え方や運用上の注意点は、ファインディの知見をまとめたFindy Libraryにも掲載しています。あわせてご覧ください。 lib.findy.co.jp ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディ株式会社でプリンシパルエンジニアをしている戸田です。普段は開発組織のAI推進を専任で担当しています。 生成AIが開発現場に入り込むようになり、Claude CodeやGitHub Copilotなどのエージェント型ツールも一般的になってきました。 その一方で、「AIを導入したのに、思ったほど速くなっていない」「AI推進を進めていきたいけど何から手を付けたらいいのかわからない」という声を、よく聞くようになりました。 そんな中で先日、弊社主催の「AI DevEx Conference 2026」にて、 「生成AI導入の『期待外れ』を乗り越える ─ 開発フロー改革が目指す、真の組織変革」 と題して登壇してきました。 dev-productivity-con.findy-code.io speakerdeck.com この記事では登壇内容を振り返りつつ、その体感と現実のギャップを可視化でどう捉えたか、そこからAI推進をどう組織に根づかせたか、そして改善のサイクルを回し続けた結果として何が変わったかを紹介します。 それでは見ていきましょう! 「開発が速くなった」は本当か──可視化で見えた現実 計測してみたら、想定と違うことが起きていた AIは増幅器──「基本」と「基礎」が先にある AI推進を「兼務」から「専任」に変えた決断 可視化 → 配布 → 浸透のサイクルを回す 育成──個人の基礎力を鍛える 改善のサイクルを回し続けた結果 まとめ──順番を間違えない 「開発が速くなった」は本当か──可視化で見えた現実 AIエージェントを導入してしばらく経った頃、現場では「効率が上がった」「開発のスピード感が増した」という声が聞こえていました。Pull requestの量も増えたように感じます。 一方で、リリースや価値提供のペースはそれほど変わっていないようにも見えました。アウトプットもアウトカムも、本当に増えているのか。体感と現実がズレているのではないか。そう考えて、まずは事実を数値で捉えることにしました。 計測には次の2つの軸を使いました。 軸 ツール 何を見るか 開発生産性 Findy Team+ 開発組織の健康状態・ボトルネックの場所 AI活用 Findy AI+ 誰が・どこで・どうAIを使っているか Findy Team+では、Four Keys(デプロイ頻度・変更リードタイム・変更失敗率・復旧時間)やサイクルタイム、Pull requestなどの各種メトリクスを確認できます。ここで「どこに問題があるのか」を事実ベースで捉えました。 jp.findy-team.io 計測してみたら、想定と違うことが起きていた 2024年と2025年を同じ条件でFindy Team+で比較したところ、次のような結果が出ました。 指標 変化 コミットからオープンまでの平均時間 -10% 1人あたりのPull request作成数 横ばい レビューからApproveまでの時間 +50% 平均コメント数 +30% コミットからオープンまでの時間は短くなっており、コードを書く速度は確かに上がっていました。しかし1人あたりのアウトプット量は横ばいで、レビューからApproveまでの時間はむしろ延び、コメント数も増えています。 つまり、AIが速くしたのは「コード生成」という一工程だけでした。その分ボトルネックがレビュー工程に移り、開発全体のスループットは横ばいのまま。理解が追いつかないままAIの出力をレビューに回す場面も増え、レビュー担当の負荷が上がっていたのです。 「AIに使われている」とも言える状態でした。そしてこれは感覚ではなく、可視化した数値が示した事実です。可視化していなければ「なんとなく速くなった」で終わり、この停滞を見逃していたと思います。ここが、AI推進の出発点になりました。 AIは増幅器──「基本」と「基礎」が先にある 数値が示したのは、「速くコードを書けても、質が落ちて指摘が増えれば、生産性は横ばいに戻る」という連鎖でした。この経験から、AIは生産性も負債も増幅する「増幅器」だと捉えるようになりました。 土台がないままAIを載せると、弱さも増幅されます。規約がなければ生成されるコードの内容がバラつき、テストがなければ不具合や障害が増え、タスクの粒度が荒ければ巨大なPull requestを量産してレビューが破綻します。だからこそ、AI活用を追う前に土台を整える必要がありました。 ここで言う土台を、私は「基本」と「基礎」の2つに分けて考えています。 1つは「基本」です。Pull requestの粒度やレビュー文化、タスク分解の習慣、そしてアーキテクチャや命名規則、型定義、テストコードといった、開発文化とコードの土台にあたるもので、組織として守るべきものです。 もう1つは「基礎」で、AIの出力が正しいかどうかを見極め、良し悪しを判断できる個人のスキルと判断力を指します。 このどちらも、AIの登場以前から開発に必要だったものです。AIによって不要になるどころか、AIの効果を左右する前提になったのです。 AI推進を「兼務」から「専任」に変えた決断 土台の重要性が見えても、それを組織に浸透させる推進体制がなければ前に進みません。ここで最初にぶつかったのが、推進体制そのものの壁でした。 AIのアップデートは毎週のように発生します。その検証・展開・教育を、通常業務と兼務しながら同時並行で進めるのは、物量的に無理がありました。兼務だと優先度が下がり、誰が責任を持つのかも曖昧になりがちです。片手間ではキャッチアップが追いつかず、社内展開が遅れ、やがて使われなくなる悪循環に陥ります。 そこで組織構造を変える決断をしました。AI推進の専任ポジションを置き、情報集約・検証・展開を一気通貫で担う体制にしました。 専任化の前後をFindy Team+で計測すると、AI推進のアクティビティは10倍以上に増えていました。同じ人月でも、構造を変えるだけで施策のスピードは大きく変わります。 可視化 → 配布 → 浸透のサイクルを回す 専任化してからは、AI推進を「可視化 → 配布 → 浸透」という1つのサイクルとして回すようにしました。 まず可視化からの気づきをSkillとして形にします。ちなみに、Findy AI+のAI活用を可視化する機能は、ファインディの内製ツールがベースになっています。改善のサイクルを積み重ねるなかで、可視化は再現性高く改善を回すために欠かせないものになっていきました。 lp.ai-plus.findy.io 形にしたSkillは、Claude Code Pluginsで全社に一斉配布します。SkillやAgent、MCPを1つのリポジトリで管理し、メンバーはワンコマンドで導入・更新できます。組織共通の資産にすることで、誰かの改善が組織全体にすぐ反映される。全員がcontributeできる状態を作りました。 Claude Code Pluginsに関しては別の記事で解説しているので、こちらも合わせて参考にしてみてください。 tech.findy.co.jp 配布して終わりでは、やがて使われなくなります。そこで浸透にも力を入れました。前日にマージされたPull requestをサマリしてSlackに自動通知したり、月1回のエンジニア定例でAI推進の活動をアナウンスしたりと、AI利用を当たり前にする働きかけを続けています。 育成──個人の基礎力を鍛える 浸透と並行して力を入れたのが育成です。AI推進を進めても、それを使う個人のスキルが無ければ、恩恵を受けることは出来ません。 まずVibeCodingペアプロとして、若手エンジニアがVibeCodingしている様子を後ろから見ながら指導します。効率的なAIへの指示の出し方やSkillの使い方を知ってもらうことが目的です。 次に、画面共有でAI活用のライブコーディングをしました。AI推進の自分のターミナル画面を画面共有で垂れ流しにしておき、希望者はそれを見つつ気になったところを質問する。実際にどう使っているかを見てもらうことで、AI活用の解像度を上げたり、答え合わせになります。 さらに、個人のスキル、つまり基礎力の強化にも着手しました。ファインディでは基礎力強化を目的として、基本情報技術者や応用情報技術者などといった資格取得を推奨するようになりました。 AIは増幅器であり、元のスキルが高いほど、AIの出力を正しく判断でき、恩恵も大きくなります。 ソフトウェアエンジニアリングやコンピュータサイエンスの基礎を学ぶことは、AI活用の前提条件となっています。 改善のサイクルを回し続けた結果 こうした「可視化 → 配布 → 浸透」を、単発の施策ではなくワンサイクルとして回し続けました。 改善サイクルを再現性高く回すうえで、やはり要になるのが可視化です。PDCAのP(計画・仮説)とC(評価)の両方で「可視化して数字を読み解く」ことで、仮説と評価の精度が上がり、次の一手の再現性が高まります。 配布したSkillや仕組みは組織に定着し、使われ続けています。Findy AI+で2026年1〜2月と3〜4月を比較すると、セッション数が約3倍、Skillの実行回数が約6倍、累計トークンが約5倍に増え、キャッシュヒット率は93.5%に達しました。 そして、測って改善を回し続けた結果、開発生産性そのものにも変化が表れました。 指標 変化 1人あたりのPull request作成数 約1.5倍 レビュー指摘数 横ばい レビューからApproveまでの時間 横ばい 平均コメント数 横ばい 横ばいだった1人あたりのPull request作成数が、2025年と2026年前半の比較で約1.5倍になりました。注目したいのは、レビュー指摘数もApprove時間もコメント数も横ばいのままだった点です。これは質を落とさずに量を増やせたことを意味します。次の改善のサイクルのターゲットは、これらリードタイムの改善となるのです。 改善は1回で終わるものではありません。可視化と改善のサイクルを回し続け、課題と向き合い続ける姿勢が重要なのです。 まとめ──順番を間違えない 今回はAI推進の流れを紹介しましたが、これらはどれか1つではなく段階的に積み上げて初めて成立する、ということを覚えておいてください。 そして、ここで強調したいのが 順番を間違えない ことです。土台が弱いと、ガードレールもAI Skillも成果を出せません。ファインディが踏んだ順番は次の4段でした。 裏返すと、AIエージェントを入れる前にやっておくべきことは、AI以前から変わっていません。統一規約、テストコード、PR粒度、レビューなどの開発文化といった 基本の徹底 こそがAI活用における大前提です。 あわせて、 個人の基礎力が高ければ高いほど、AIはより効果的に活用できます。 AIは銀の弾丸ではなく、基本と基礎、そして改善を段階的に積み重ねて初めて効果を発揮します。 そして 再現性高く改善のサイクルを回し続けるためには可視化は必要不可欠です。 可視化せずに勘や経験に頼って改善をしたら、たまには当たるでしょう。しかしそれには再現性がないのです。 改善の打率を上げるためにも、可視化からの数字を読み解き、改善のサイクルを回し続けてください。 これをやったらすぐに生産性が一気に上がった!というような決定打は存在しません。 開発フロー改革もAI推進も、小さいことの積み重ねの結果 だということを忘れないでください。 目の前の課題と向き合い続ける姿勢こそが、AI推進が見せるべき姿であり、その先に真の組織改革があるのです。 なお、開発フローそのものをどう作り変えたか、実際に作ったスキルやエージェントの具体例などは、別の記事「 「速く作る」から「正しく作る」へ ─ 生成AI時代の開発フロー改革のロードマップ 」で詳しく紹介しています。こちらもあわせてご覧ください。 tech.findy.co.jp また、ファインディの開発知見を公開するドキュメントサイト「Findy Library」をリリースしました。Pull requestの粒度やタスク分解、テストコードといった「基本」から、AIエージェントとの協働開発やSkillの活用まで、今回の登壇で紹介した取り組みのベースになっている知見をまとめています。登壇内容を自分の組織でも実践してみたいと思った方は、ぜひFindy Libraryを活用してみてください。 lib.findy.co.jp ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディ株式会社でプリンシパルエンジニアをしている戸田です。普段は開発組織のAI推進を担当しています。 このたび、ファインディが社内で実践している知見をまとめたドキュメントサイト「Findy Library」を公開しました。 lib.findy.co.jp Findy Libraryの特徴は、人が読むことだけを想定していない点です。すべてのページはブラウザで読めるWeb版に加えて、生のMarkdownとしても配信されており、Claude CodeやCursorといったAIエージェントに直接読み込ませて開発ワークフローに組み込めるように設計されています。 この記事ではFindy Libraryの紹介と、何ができるのかを解説します。 Findy Libraryとは 人が読む: 実践知見のリファレンスとして AIに読ませる: 複数のアクセス経路 ページ単位で生のMarkdownを取得する MCPサーバーで検索させる 開発に組み込む: 具体的なユースケース プルリクエスト作成前のセルフレビューに使う CLAUDE.mdやルールファイルから参照する まとめ Findy Libraryとは Findy Libraryは、ファインディが日々の業務で実践している知見を、社外の方でも使える形で公開するドキュメントサイトです。社内に閉じていたナレッジを整理し、業界全体の底上げに貢献することを目的としています。 公開時点では、開発の基本を扱う「基礎」と、AI活用を扱う「AI」の2つのカテゴリでコンテンツを提供しています。 すべてのページは日本語と英語の両方で提供しており、ページ上部の切り替えで言語を選べます。 人が読む: 実践知見のリファレンスとして まずは普通に読む使い方です。 Findy Libraryが目指しているのは、網羅的な教科書ではありません。「プルリクエストが大きくなりがちでレビューが滞る」「AIエージェントへの任せ方が定まらない」など、業務の中で何かに困ったときにコンテンツを探しに来てもらい、解決のヒントや一歩踏み出すための後押しを届けることを目的としています。 各ドキュメントは「概念の説明」で終わらず、実際の業務でどう判断するかまで踏み込んでいるのが特徴です。たとえばプルリクエストのドキュメントでは、レビューしやすい粒度への分割方法を具体例つきで解説しています。 lib.findy.co.jp Agentic Workflowのドキュメントでは、Vibe Codingとの違いを人とAIの関係性で整理した上で、コンテキスト境界に沿った役割分割や並列実装といった実践テクニックを扱っています。 lib.findy.co.jp プルリクエストの作り方やAIへの委譲の進め方についてチームで認識を揃えたいときは、該当ページのリンクを共有して参考にしてみてください。 AIに読ませる: 複数のアクセス経路 ここからがFindy Libraryの本領です。Findy Libraryは公開当初からAIエージェントに読ませることを前提に考慮されており、用途に応じて複数のアクセス経路を用意しています。 ページ単位で生のMarkdownを取得する 任意のページのURL末尾に .md を付けると、そのページの生のMarkdownが取得できます。 # 日本語版 curl https://lib.findy.co.jp/ja/development/pull-request.md # 英語版 curl https://lib.findy.co.jp/development/pull-request.md HTMLのパースが不要なため、AIエージェントがトークンを無駄にせず内容だけを読み込めます。特定のトピックだけをコンテキストに入れたいときは、この経路が最も手軽です。 MCPサーバーで検索させる Findy LibraryはMCP(Model Context Protocol)サーバーも公開しています。MCP対応のAIエージェントに登録しておくと、エージェントが必要なタイミングで自律的にドキュメントを検索して参照するようになります。 Claude Codeなら、次のコマンド1つで登録できます。 claude mcp add --transport http findy-library https://lib.findy.co.jp/mcp 登録後は「プルリクエストの分割について、Findy Libraryの知見を踏まえて教えて」のように質問するだけで、エージェントがsearchツール経由で関連する箇所を取得して回答します。事前にどのページを読ませるか人間が選ぶ必要がないのが、 .md 取得との大きな違いです。 開発に組み込む: 具体的なユースケース アクセス経路がわかったところで、実際の開発ワークフローに組み込む例を紹介します。 プルリクエスト作成前のセルフレビューに使う プルリクエストを作る前に、Findy Libraryの知見を観点としてAIエージェントに粒度が適切かどうかチェックしてもらう使い方です。Claude Codeであれば、次のように指示します。 https://lib.findy.co.jp/ja/development/pull-request.md を読んで、分割すべきなら分割案を出して エージェントはドキュメントに書かれた「レビューしやすいプルリクエストの条件」を評価基準として、手元の変更を照らし合わせてくれます。汎用的な「レビューして」という指示よりも、観点が明確な分、指摘の粒度が安定します。 CLAUDE.mdやルールファイルから参照する チームの開発規範としてFindy Libraryを使う場合は、リポジトリのCLAUDE.mdやCursorのルールファイルに参照を書いておく方法があります。 ## タスクの進め方 大きなタスクは着手前に分解する。分解の粒度は https://lib.findy.co.jp/ja/development/task-breakdown.md の基準に従うこと。 こうしておくと、AIエージェントがタスクに着手するたびにドキュメントを参照し、チームの合意した基準で作業を進めるようになります。Findy Libraryのコンテンツは適宜追加更新を行うため、自前で長大なルールファイルを育てる必要がなくなります。 まとめ Findy Libraryは、読むだけのドキュメントサイトではなく、AIエージェントの開発ワークフローに組み込める知見ライブラリです。 まずはブラウザで気になるコンテンツを探してみてください。そしてMCPやスキルからFindy Libraryにアクセスするようにして、普段の開発の中でFindy Libraryの知見を呼び出してみてください。 コンテンツは今後も追加・更新していきます。お楽しみに! ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは!ファインディでプロダクト開発部のVPoEをしている 浜田 です。 Findy Team+ では、コードの複雑さを可視化して技術的負債の解消に役立てられる「コード品質分析」という機能の提供を開始しました。 4つのコード品質に関連する指標を組み合わせて、ファインディ独自のスコアとして可視化しています。実際に活用いただく中で、「スコアが低いとき、限られた工数でどの指標から優先的に改善すればよいか」という、リファクタリングの優先順位に踏み込んだご質問をいただくことが増えてきました。 この記事ではコード品質の可視化が求められる背景から、4つの指標の読み解き方、効率的にリファクタリングを進めるための優先順位、具体的なリファクタリングパターンまでを紹介します。 コード品質分析を使っている方はもちろん、複雑度の指標を使ってリファクタリングを進めたいと考えているすべての方に参考にしていただける内容です。 なぜ今、コード品質の可視化なのか コード品質分析のよくある活用事例 コード品質分析とは 品質スコアが低いとき、どの指標から改善するか まず「入れ子」を浅くする 次に、大きすぎるファイルを役割のまとまりごとに分ける 最後に、残った場合分けを個別に判断する 具体的なリファクタリングパターン ガード節(早期リターン)で入れ子を浅くする ループの中ではcontinueが同じ役割 役割ごとに関数・ファイルを分ける 単純な場合分けは対応表に逃がす どうしても消えない条件は、名前を付けて外に出す 指標についてよくいただく質問 循環的複雑度と認知的複雑度は何が違う? 4つの指標はすべて目安(Good)の値まで下げるべき? どのファイルからリファクタリングを始めればいい? まとめ なぜ今、コード品質の可視化なのか これまで人がコードを書いてきた時代にも、レビュー負荷の軽減や属人化の防止、リファクタリング投資の判断材料にするなど、コード品質を測る目的はありました。 一方で、人がコードを書くスピードには物理的な上限があるため、品質に課題のあるコードが積み上がるペースにも自然と歯止めがかかっていました。 AIがコードを書く時代になると、この前提が変わります。 コードの生産量が爆発的に増える一方で、人がレビューできる量には限界があるため、品質への配慮が不十分なコードがこれまでにないスピードで積み上がる可能性があります。 さらに、既存のコードはAIがコードを生成するときの入力にもなるため、品質の低いコードを放置すると、そこから生成されるコードの品質にも影響が波及していきます。 このような変化から、コード品質の可視化は「あれば便利」なものから、AI時代の開発組織に欠かせない前提条件に変わりつつあると考えています。 ファインディではAI時代の開発組織が見るべきものを「 開発資本(Engineering Capital) 」として定義しています。 開発資本は、Speed・Quality・Controlの3つの軸で開発組織の状態を捉えるフレームワークです。 このうち、コンスタントに価値を届けられるかを捉えるQualityと、変更を予測し制御できるかを捉えるControlでは、コード品質の指標が観点の1つになっています。 この記事で紹介するコード品質分析は、開発資本のQualityとControlをコードの複雑さという切り口から支える機能です。 コード品質分析のよくある活用事例 コード品質分析を活用したくなる場面は、大きく次の3つのパターンに分けられます。 活用事例 内容 リファクタリング投資の意思決定・効果測定 どこに負債が溜まっているか、直した結果どう変わったかを数字で示したい モジュール間やAIと人のコード品質比較 モジュールごとや、AIが生成したコードと人が書いたコードの品質を横並びで把握して、改善に活かしたい OSS化や外部公開の判断 対外的に公開するコードなので、客観的な指標で品質を確認したい いずれの活用事例も、根底にあるのは「コードの質を語れるデータが欲しい」という共通の課題です。 この「質を語れるデータ」を提供するのがコード品質分析です。 コード品質分析とは コード品質分析は、コードの複雑さを可視化し、どこに技術的負債が溜まっているか、どこをリファクタリングすべきかを判断できるFindy Team+の分析機能です。 リポジトリ・ファイル単位で、次の4つの指標を計測しています。 指標 何を測っているか 循環的複雑度 プログラム中の「分岐の数」 認知的複雑度 人間がコードを読むときの「頭の負担」 保守性指数 0〜100の総合スコア。大きいほど良好 最大ネスト深度 「ifの中のifの中の…」という階層の深さ それぞれの指標はソフトウェア開発で一般的に広く使われているものであり、Findy Team+ではこの4つの指標を組み合わせて独自の品質スコアを算出しています。 「読みづらさ・変更しにくさ」という同じ課題を異なる角度から幅広く捉え、バランスよく評価できるように設計しています。 4つを組み合わせることで、「どこが」「どう悪いのか」を具体的にチームにフィードバックできるようになります。 各指標には「Good」とされる目安の水準があり、どのファイルのどの指標が目安を超えているかをツール上で確認できます。 なお、コード品質分析が測っている「保守のしやすさ」は、システムやソフトウェアの品質を評価する国際規格ISO/IEC 25010が定義する品質特性の1つ「保守性」に対応しています。 「保守性のスコアがどう変化したか」という報告は、感覚値ではなく国際標準の品質特性に基づいた説明になるため、経営層やステークホルダーへの報告にも活用できます。 これらの指標を活用すると、リポジトリごとに技術的負債がどこに溜まっているかを数字で把握し、リファクタリング前後で品質がどう変わったかをグラフで示せるようになります。 モジュールやリポジトリを横並びで比較して組織としての打ち手を決めたり、「開発スピードだけでなく、質も担保している」ことをデータで説明したりと、先ほどの3つの活用事例はいずれもこの機能で実現できます。 品質スコアが低いとき、どの指標から改善するか コードの品質は「将来の変更のしやすさ」を左右します。 品質が低いコードは、機能追加や不具合修正のたびに時間がかかり、修正時に新しい不具合も生まれやすくなります。 コード品質分析を導入する前は、「最近触っていて読みづらかったから」といった開発者の勘や記憶を頼りに、リファクタリング対象を選ぶことが多いのではないでしょうか。 導入後はファイル単位で指標が可視化されるため、「どのファイルの、どの指標が悪いのか」という数字を起点に着手先を決められるようになります。 とはいえ、4つの指標すべてが悪化しているファイルを前にすると、どこから手を付ければよいか迷ってしまいます。 そんなときは、品質の低いコードを「長くて読みにくいマニュアル文書」にたとえてみると、それぞれの指標の意味を直感的に捉えられます。 指標 文書にたとえると 最大ネスト深度 括弧の中に括弧、さらにその中に括弧…という入れ子の深さ 保守性指数 1つの章が長すぎて全体像がつかめない度合い 認知的複雑度 読む人の頭にかかる負担の総量 循環的複雑度 「Aの場合は…Bの場合は…」という場合分けの数 このたとえを踏まえて、私がおすすめしている改善の順序を紹介します。 まず「入れ子」を浅くする 「もし〜なら、もし〜なら、もし〜なら…」と何重にも条件が重なった箇所は、読む人が頭の中で覚えておくべき前提が積み重なり、理解の妨げになります。 これを「該当しない場合は先に終了する」という書き方に直します。 後述しますが、この改善は作業が小さく、既存の動きを壊すリスクが最も低いのに、読みやすさへの効果が大きいのが特徴です。 次に、大きすぎるファイルを役割のまとまりごとに分ける 品質が低いファイルの多くは、1つのファイルが複数の役割を抱え込んでいます。 文書で言えば「1つの章に無関係な話題が詰め込まれている」状態です。 役割ごとに分けると、探しやすさ・直しやすさが根本的に改善します。 ただし「短くするための機械的な分割」は、かえって話があちこちに飛ぶ読みにくい文書になるのと同じで逆効果なので、あくまで意味のまとまりで分けます。 最後に、残った場合分けを個別に判断する ここまでの改善を終えると、読み手の負担である認知的複雑度は大部分が自然に解消しています。 最後に残る「場合分けの数」は、業務ルールそのものの複雑さを映していることが多い点に注意が必要です。 実際の業務に10通りの場合分けがあるなら、コードにも10通りの場合分けが必要です。 これを無理に減らすとかえって分かりにくくなるため、「本当に減らせる場合分けか」を見極めながら最後に対応します。 この順序をおすすめする理由は、「安全で小さい改善→構造の整理→慎重な判断が必要な改善」という並びになっていることです。 改善作業そのものが新しい不具合を生むリスクを最小限に抑えながら進められます。 ここまでの「この指標がこう出たらこう判断する」という流れをフローチャートにまとめると、次のようになります。 flowchart TD A["品質スコアが低いファイルを特定"] --> B{"最大ネスト深度が深い?"} B -->|はい| C["ガード節で入れ子を浅くする"] B -->|いいえ| D{"保守性指数が低い?"} C --> D D -->|はい| E["ファイルを役割のまとまりで分割する"] D -->|いいえ| F{"循環的複雑度が高い?"} E --> F F -->|はい| G{"業務ルール由来の場合分け?"} F -->|いいえ| K{"認知的複雑度は下がった?"} G -->|いいえ| I["場合分けを整理して減らす"] G -->|はい| J["条件に名前を付けて読みやすくする"] I --> K J --> K K -->|下がった| H["読みやすさの改善を確認して完了"] K -->|まだ高い| M["残った書き方由来の複雑さを見直す"] フローの最後に認知的複雑度を確認しているのは、この指標が「どこから直すか」ではなく「どれだけ読みやすくなったか」という成果を映すものさしだからです。 入れ子の解消やファイル分割を進めると認知的複雑度は自然に下がっていくので、リファクタリングの前後で見比べれば、狙いどおり読みやすさが改善したかを数字で確認できます。 まだ高いままなら、書き方由来の複雑さが残っているサインなので、対象のファイルを見直してみてください。 具体的なリファクタリングパターン 改善の順序で挙げた「入れ子を浅くする」「ファイルを役割ごとに分ける」「場合分けを整理する」を、具体的なコード例とともに見ていきます。 言葉だけではイメージが浮かびにくいので、代表的なパターンを順番に紹介します。 ガード節(早期リターン)で入れ子を浅くする 改善順序の1番目に挙げた「該当しない場合は先に終了する」書き方は、ガード節(早期リターン)と呼ばれています。 まずは改善前のコードです。条件を満たす場合をどんどん掘っていく書き方で、ネストが4段になっています。 function applyCoupon ( user , order , coupon ) { if ( user ! == null ) { if ( user . isActive ) { if ( order . total >= coupon . minAmount ) { if ( ! coupon . isExpired ) { // ここでやっと本題(クーポン適用) return order . total - coupon . discount ; } else { return order . total ; } } else { return order . total ; } } else { return order . total ; } } else { return order . total ; } } 読む人は「ユーザーがいて、かつ有効で、かつ金額を満たしていて、かつ期限内で…」という前提を頭に積み上げたまま本題にたどり着く必要があります。 さらにelseが下に離れて置かれるため、「このelseはどのifと対応しているのか」を目で追う負担も加わります。 これをガード節で書き直すと、ネストは0〜1段になります。 function applyCoupon ( user , order , coupon ) { // 適用できないケースを先に片付ける if ( user === null ) return order . total ; if ( ! user . isActive ) return order . total ; if ( order . total < coupon . minAmount ) return order . total ; if ( coupon . isExpired ) return order . total ; // ここから下は「クーポンが適用できる」と確定した世界 return order . total - coupon . discount ; } ポイントは2つあります。 1つは、前提を覚えておく必要がなくなることです。「returnを通過した=その条件はもうクリア済み」なので、本題のコードを読むときに頭の中は空っぽでよくなります。 もう1つは、動きが何も変わっていないことです。 条件の書き方を裏返して並べ直しただけなので、既存の挙動を壊すリスクが極めて低い改善です。改善順序の最初におすすめした根拠もここにあります。 ループの中ではcontinueが同じ役割 ループの中で条件分岐が積み重なる場合は、continueがガード節と同じ役割を果たします。 // 改善前: ループ内にネストが積み上がる for ( const member of members ) { if ( member . isActive ) { if ( member . email ! == null ) { sendNewsletter ( member . email ) ; } } } // 改善後: 対象外を先にスキップする for ( const member of members ) { if ( ! member . isActive ) continue; if ( member . email === null ) continue; sendNewsletter ( member . email ) ; } 「該当しない行は読み飛ばす」という宣言が先頭に並ぶので、ループの本題であるメール送信が一目で分かるようになります。 役割ごとに関数・ファイルを分ける 改善順序の2番目に挙げた「大きすぎるファイルを役割のまとまりごとに分ける」も、まずは関数単位で切り出すと着手しやすくなります。 次の関数は、1つの中で「入力の検証」「金額の計算」「通知の送信」という3つの役割を抱え込んでいます。 // 改善前: 1つの関数が検証・計算・通知を抱えている function checkout ( user , order , coupon ) { if ( user === null ) throw new Error ( "ユーザーが不正です" ) ; if ( order . items . length === 0 ) throw new Error ( "カートが空です" ) ; let total = order . items . reduce (( sum , item ) => sum + item . price , 0 ) ; if ( coupon ! == null && ! coupon . isExpired ) { total -= coupon . discount ; } sendEmail ( user . email , `ご注文ありがとうございます。合計 ${ total } 円です` ) ; return total ; } 役割ごとに関数を切り出すと、呼び出し側は「何をしているか」の目次だけを読めば済むようになります。 // 改善後: 役割ごとに関数へ切り出す function validateOrder ( user , order ) { if ( user === null ) throw new Error ( "ユーザーが不正です" ) ; if ( order . items . length === 0 ) throw new Error ( "カートが空です" ) ; } function calculateTotal ( order , coupon ) { const subtotal = order . items . reduce (( sum , item ) => sum + item . price , 0 ) ; const isCouponUsable = coupon ! == null && ! coupon . isExpired ; return isCouponUsable ? subtotal - coupon . discount : subtotal ; } function checkout ( user , order , coupon ) { validateOrder ( user , order ) ; const total = calculateTotal ( order , coupon ) ; sendEmail ( user . email , `ご注文ありがとうございます。合計 ${ total } 円です` ) ; return total ; } 文書でいう「話題ごとに章を分け、目次から必要な章に飛べるようにする」操作に相当します。 1つのファイルが大きくなってきたら、こうしたまとまりを別ファイルに移すことで、ファイルあたりの分量が減り、保守性指数の改善につながります。 単純な場合分けは対応表に逃がす 改善順序の3番目に挙げた「場合分けの数」そのものを減らせるのは、分岐が単純な対応関係になっているケースです。 次のコードは、会員ランクごとに割引率を返すだけの分岐が並んでいます。 // 改善前: ランクの数だけ分岐が増える function getDiscountRate ( rank ) { if ( rank === "bronze" ) return 0 . 03 ; if ( rank === "silver" ) return 0 . 05 ; if ( rank === "gold" ) return 0 . 10 ; if ( rank === "platinum" ) return 0 . 15 ; return 0 ; } 「入力に対して値を1つ返すだけ」の分岐は、対応表(オブジェクト)に置き換えると分岐そのものがなくなります。 // 改善後: 分岐をデータに置き換える const DISCOUNT_RATES = { bronze : 0 . 03 , silver : 0 . 05 , gold : 0 . 10 , platinum : 0 . 15 , } ; function getDiscountRate ( rank ) { return DISCOUNT_RATES [ rank ] ?? 0 ; } 分岐が減るので循環的複雑度が下がり、ランクが増えても表に1行足すだけで済みます。 一方で、分岐ごとに処理の中身が違う場合や、業務ルールとして意味のある場合分けは、無理に表へ押し込めません。その場合は、次のように名前を付けて読みやすくする方向で対応します。 どうしても消えない条件は、名前を付けて外に出す 条件が業務ルールとして本当に必要な場合は、ネストを消すのではなく、条件の塊に名前を付けて1段浅く見せる方法があります。 // 改善前 if ( order . total >= coupon . minAmount && ! coupon . isExpired && coupon . usedCount < coupon . maxUses ) { // クーポンを適用する } // 改善後: 条件の意味に名前を付ける function isCouponApplicable ( order , coupon ) { return order . total >= coupon . minAmount && ! coupon . isExpired && coupon . usedCount < coupon . maxUses ; } if ( isCouponApplicable ( order , coupon )) { // クーポンを適用する } 文書でいう「長い括弧書きを脚注に逃がして、本文は一言で済ませる」操作に相当します。 本文を読む人は「クーポンが適用できるなら」とだけ理解すればよく、詳細が必要になったときだけ中身を読みに行けます。 指標についてよくいただく質問 最後に、コード品質分析の指標についてよくいただく質問と回答を紹介します。 循環的複雑度と認知的複雑度は何が違う? 循環的複雑度は分岐の「数」を機械的に数える指標で、認知的複雑度は読む人の「頭の負担」を測る指標です。 大きな違いは、認知的複雑度はネストが深い場所にある分岐ほど重くカウントされることです。 たとえば、この記事で紹介したガード節への書き換えでは、分岐の数自体はほとんど変わらないため循環的複雑度はあまり下がりませんが、ネストが解消されるため認知的複雑度は大きく下がります。 「循環的複雑度には業務の複雑さ、認知的複雑度には書き方の複雑さが表れやすい」と捉えると、使い分けしやすくなります。 4つの指標はすべて目安(Good)の値まで下げるべき? 目安はあくまで優先順位を決めるためのシグナルであり、すべてのファイルで達成すべきノルマではありません。 特に循環的複雑度は業務ルールの複雑さを反映していることが多く、無理に下げるとかえって読みにくくなる場合があります。 「目安を超えているファイルから順に中身を見て、書き方由来か業務由来かを見極める」という使い方をおすすめしています。 どのファイルからリファクタリングを始めればいい? スコアが悪い順ではなく、「今後も変更する予定があるファイル」から始めるのがおすすめです。 コードの品質が問題になるのは変更するときなので、スコアが低くても今後ほとんど触らないファイルの優先度は下げて構いません。 逆に、これから機能追加を予定している箇所にスコアの低いファイルがあれば、着手前にリファクタリングを検討する価値があります。 なお、優先度を下げてよいというのは、やらなくてよいという意味ではありません。 冒頭で触れたとおり、既存のコードはAIがコードを生成するときの入力にもなるため、品質の低いファイルを放置すると、人が直接触らなくてもAIが生成するコードの品質に影響する可能性があります。 変更予定のあるファイルから着手しつつ、残ったファイルも計画的に改善を進めていくことが大切です。 まとめ この記事では、コード品質の可視化が求められる背景から、コード品質分析の4つの指標の読み解き方、品質スコアが低いときの改善の優先順位、具体的なリファクタリングパターンまでを紹介しました。 4つの指標が同時に悪化していても、「ガード節で入れ子を浅くする→ファイルを役割ごとに分ける→残った場合分けを個別に判断する」という順序で進めれば、リスクを抑えながら効率的に品質を改善できます。 まずは一番安全で効果の大きいガード節から始めてみてください。 AIがコードを書く時代、コードの品質を定量的に把握してデータに基づいて意思決定することは、今後ますます重要になると考えています。 Findy Team+のコード品質分析は、その第一歩として、どこから効率的にリファクタリングを進めればよいかをデータで明らかにします。 Findy Team+に興味を持たれた方は、ぜひこちらのページからお問い合わせください。 jp.findy-team.io
こんにちは。ファインディのPlatform開発チーム(以降、SREチーム)でSREを担当している原( こうじゅん )、富田( @Cooking_ENG )、松本( @mozumasu )です。 2026年6月25日・26日に幕張メッセで開催された「AWS Summit Japan 2026」に、SREチームの3名で参加してきました。 aws.amazon.com それぞれが印象に残ったセッションを1本ずつ取り上げ、ファインディの現状と紐づけてお届けします。 スタートアップにAmazon EKSは早すぎる? マルチプロダクト戦略を加速するPlatform Engineeringの実践 TBSテレビ「ラヴィット!」大規模配信の裏側とAWSサーバーレス設計 実践!Amazon RDSとAmazon Auroraのコスト最適化とパフォーマンス向上 まとめ スタートアップにAmazon EKSは早すぎる? マルチプロダクト戦略を加速するPlatform Engineeringの実践 SREチームの 原 です。 私が印象的だったのは、株式会社ログラスの中井綾一さんによるセッション「スタートアップにAmazon EKSは早すぎる? マルチプロダクト戦略を加速するPlatform Engineeringの実践」です。 ファインディはECSを中心に構成していますが、今後Platformをどのようにしていくかという判断軸を持ち帰りたいと考え、このセッションに参加しました。 ログラスは経営管理サービス「Loglass」を中心にマルチプロダクト戦略を進めている会社で、本セッションではAmazon EKSをベースにしたPlatform基盤の導入から、1年で社内に広げていくまでの実践が紹介されました。 中井さんがEKS導入を判断した時点でのチーム規模は、次の通り紹介されていました。 エンジニア約50名 プロダクト2つ SREチーム4名 「マルチプロダクト戦略・SRE4名」という条件で、なぜ「重い」と言われがちなEKSを選んだのか、というのが本セッションの主題でした。 特に印象に残ったのは、技術選定の軸として中井さんが繰り返し言い切っていた「『今の規模』ではなく『数年後の事業にフィットするか』で判断する」という言葉でした。 基盤選定をする際には、「現時点で十分」「今の人数で運用できる」という現在地ベースになりがちですが、中井さんからはPlatform構築は1〜2年かかる長期投資で、課題が顕在化してから着手したのでは間に合わない、というメッセージが繰り返されていました。 実際に、SRE4名で導入を進めたPlatform基盤上で、1年で5チーム・約20サービスまで展開されたとのことです。 もう1つ刺さったのが「AI時代だからこそEKSプラットフォームに価値がある」という主張です。Coding Agentが高速に変更を生む時代には、Namespace分離・RBACやAdmission Controllerによってチーム・サービスごとの権限境界やポリシーを機械的に担保でき、開発効率と品質・統制を両立できる、という観点でした。 ここで気になるのが、ファインディの現在地です。 ファインディは現在、マルチプロダクト展開、SREチーム4名、基盤はAmazon ECS中心で、インフラ構築はTerraform汎用モジュールを介して開発者主体で進められる体制を整えてきました。 tech.findy.co.jp 直近のSREチーム全体の取り組みは、次のFindy Tech Talkでも紹介しています。 tech.findy.co.jp マルチプロダクトと開発者主体での運用がさらに広がったときに、SREチームがボトルネックにならないPlatformの形をどう先回りで仕込むかの重要性を感じました。「事業に対してPlatformをどう先回りで仕込むか」という考え方は大変勉強になりました。 TBSテレビ「ラヴィット!」大規模配信の裏側とAWSサーバーレス設計 SREチームの 富田 です。 私が参加したのは、株式会社TBSテレビの亀田遼さんによるブレイクアウトセッション「TBSテレビ『ラヴィット!』大規模配信の裏側とAWSサーバーレス設計」です。 『ラヴィット!忘年会 '25』を、想定視聴者数6万人規模で配信した際の裏側を紹介する内容でした。 全く宣伝できていませんでしたが、AWS Summitで初登壇しました!! お聞きいただきありがとうございました🙏 #AWSSummit pic.twitter.com/PtQwcKdcxY — Ryo (@ry_km_u_u) 2026年6月25日 x.com 『ラヴィット!忘年会 '25』の配信プラットフォームには、KustamieというTBSテレビが開発している自社サービスが使われています。配信者と参加者の双方向なやりとりを充実させ、「参加感」を高めるためのイベント向けプラットフォームです。 アーキテクチャはサーバーレス中心で、データ管理にAmazon ECSとAmazon Aurora Serverless v2、クライアントAPIにAmazon API GatewayとAWS Lambda、映像とリアルタイム通信にAmazon IVS (Interactive Video Service) を利用しています。 大規模配信に向け、既存のクライアントAPI構成には次のような課題があったそうです。 キャッシュを前提とした構成になっていなかった 配信参加時のAPIレスポンスに最長12秒程度かかっていた 負荷試験を実施したことがなく、高負荷時の挙動が未知数だった これらの課題に対する解決策の中で、特に印象に残ったのが多段キャッシュ構成によるレスポンス改善のお話でした。 採用されたのは、Amazon API Gatewayのステージキャッシュ・AWS Lambda関数のグローバル変数・Amazon ECSと併設したAmazon ElastiCache (Valkey) を組み合わせた3層構成です。 API GatewayステージキャッシュでGET系レスポンスをエッジから返し、Lambdaのグローバル変数でホットスタート時の関数初期化を省き、ElastiCacheで後段のDBアクセスを軽くする、という形で多段にキャッシュが効くようにしていました。 結果として、最長12,000ms程度かかっていた配信参加時のAPIレスポンスが、平均150ms程度まで改善したそうです。 Amazon SQSとAWS Step Functions Expressによる非同期化も合わせて実施されており、設計全体で大きな改善幅が出ていました。 負荷試験にはOSSの負荷試験ツールGrafana k6が使われており、待機配信前→待機配信中→本編配信中の3フェーズで指数的にVUを増やすシナリオで本番のアクセスパターンを再現したそうです。 直近、私もk6とDatadogでファインディの負荷試験環境を構築していたところだったので、シナリオ設計や規模の見立てが自分の作業と重なって、特に身近に感じられた話題でした。 ファインディでの負荷試験環境構築の取り組みは次の記事で紹介しています。 tech.findy.co.jp 6万人規模の同時アクセスをサーバーレス中心の構成でさばく事例として、設計の引き出しを増やせたセッションでした。 ファインディでも「Findy Conference」を中心に瞬間的なアクセス集中は発生するため、「キャッシュをどの層で重ねるか」「どこを非同期に逃がすか」を設計初期から意識する姿勢は参考にしたい考え方でした。エッジ・Lambda・ElastiCacheで多段にキャッシュを効かせる発想は、今後の負荷特性の変化に備える引き出しとして持ち帰りたいと感じています。 また、Amazon IVSのように、ライブ配信向けに使えるAWSサービスをユースケースとセットで知れたことも収穫でした。 Kustamieは2026年秋にベータ版提供開始予定とのことです。どんなイベント体験が広がっていくのか、リリースが楽しみです。 実践!Amazon RDSとAmazon Auroraのコスト最適化とパフォーマンス向上 SREチームの 松本 です。 私が印象に残ったのは、アマゾン ウェブ サービス ジャパン合同会社の塚井知之さんによるブレイクアウトセッション「実践!Amazon RDSとAmazon Auroraのコスト最適化とパフォーマンス向上」です。 「パフォーマンス向上」と「コスト最適化」は両立できる、というテーマのもと、架空のEC支援企業「AnyCompany」が直面する課題と、その解決策を追体験していく構成のセッションでした。 AnyCompanyには「レポートクエリによる運用DBの圧迫」「複雑クエリでの一時オブジェクト書き出しによるI/Oボトルネック」「メモリに乗り切らないホットデータ」「バックアップコストの肥大化」といった、実務でよく遭遇する課題が並んでいました。 セッション全体で繰り返し問われていたのが、「大きいインスタンスへの垂直スケーリングは、最もコスト効率の良い選択か?」という問いです。 特に印象に残ったのが、垂直スケーリングに頼らずにコストとパフォーマンスを両立する3つのパターンでした。 リードレプリカへのクエリオフロード(レポートクエリを逃がす) RDS Optimized Reads(一時オブジェクトをローカルSSDに逃がす) Aurora Optimized Reads(バッファキャッシュをローカルSSDに拡張する) 1つ目は、リードレプリカへのクエリオフロードです。 社内データアナリストのレポートクエリが運用DBを圧迫している状況で、インスタンスを1段階上げる代わりに、小さめのリードレプリカを足してレポートを逃がす構成にすることで、約15%のコスト削減と顧客APIの安定化を同時に実現できる、という試算でした。 「先に分けることでむしろ安く済む」という発想は、Amazon RDSまわりの構成判断でも応用が効く話だと感じました。 2つ目は、RDS Optimized Readsで一時オブジェクトをローカルSSDに逃がすパターンです。 複雑なクエリで生成される一時オブジェクトがAmazon EBSに書き出されてレイテンシが伸びる、というボトルネックに対し、ローカルNVMe SSDを搭載したインスタンスへ載せ替えて一時ファイルの書き出し先をローカルSSDに切り替える、というアプローチでした。 垂直スケーリングと比べて約43%のコスト削減かつ最大2倍の読み取り性能向上というのは、知らないと選べない選択肢だと痛感しました。 3つ目は、Aurora Optimized ReadsでバッファキャッシュをローカルSSDに拡張するパターンです。 メモリに乗り切らないホットデータをディスクから読み直してしまう、というボトルネックに対し、ローカルNVMe SSDをバッファプールの第2階層として使う「階層型キャッシュ」で対処します。 同等のインメモリキャッシュを得るために大きなインスタンスに上げる代わりに、小さめのインスタンス + I/O-Optimizedの組み合わせを選ぶことで、約90%のコスト削減と最大8倍の読み取り性能を実現する事例でした。 判断のステップとして BufferCacheHitRatio や AuroraEstimatedSharedMemoryBytes でワーキングセットを把握してから選ぶ、という手順も合わせて示されており、「まず可視化してから手を打つ」という基本がここでも徹底されていたのが印象的でした。 バックアップ面でも、自動バックアップの保持期間を短くし、それより古い分はAmazon S3へのParquet形式スナップショットエクスポートに切り替えることで、30%のコスト削減ができるという試算が紹介されていました。 障害復旧のためのリストア要件は直近のデータで足りる一方、監査や過去データの参照は必ずしも即時のリストアを必要としないため、S3への安価なエクスポートに逃がせます。この「リストア要件」と「参照要件」を分けて考えるとコスト構造が大きく変わる、というのはファインディの運用でもすぐに見直せそうな観点です。 どの課題でも、Database InsightsやPerformance Insightsでまずワークロードを可視化し、ボトルネックに応じて適切なインスタンスファミリー・ストレージタイプ・キャッシュ階層を選び直す、という流れで進められていたのが印象的でした。 「見えないものは改善できない」という基本を、AnyCompanyの課題を通して追体験できる構成になっていたと感じました。 まずは BufferCacheHitRatio や VolumeReadIOPs ・ VolumeWriteIOPs を見直し、I/O-OptimizedやOptimized Readsが効くワークロードがないかを棚卸しするところから始めたいと思います。 まとめ 2日間を通して、各社の事例や最新サービスに直接触れられる濃いインプットの機会となりました。負荷試験のシナリオ設計や、Amazon RDS/Auroraのコスト最適化、マルチプロダクト時代のPlatformの形といった、現地で持ち帰った論点を、SREチームの日々の取り組みに少しずつ活かしていきます。 ファインディでは、SREメンバーを募集しています。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディ株式会社でテックリードをしている戸田です。 先日公開したFindy AI Meetup in Fukuoka #6のイベントレポートで、「AIで開発が速くなったはずなのに、レビューが詰まってトータルの生産性が変わらなかった」という話に触れました。 tech.findy.co.jp そこで今回は、そのレビュー詰まりを題材に、 数値を見る → 仮説を立てる → 改善アクションを打つ → 結果を検証する というワンサイクルを1つの実例として紹介します。 Findy Team+ と Findy AI+ を組み合わせて、AI導入前後の数値を可視化し、そこから仮説を立て、改善アクションを打ち、結果を検証するまでの一連の流れを追っていきます。 AIで速くなったはずなのに、1人あたりの生産性は変わらなかった メトリクスを深掘りして見えた「レビュー詰まり」 仮説 改善アクション 結果検証 まとめ AIで速くなったはずなのに、1人あたりの生産性は変わらなかった 開発でAIを使い始めたときの私たちの想定はシンプルでした。 AIがコードを書いてくれるなら、1人あたりが生み出すアウトプットは増え、開発生産性も自然と上がる はずだ、というものです。 実際、ファインディ社内でのAIの利用率は順調に伸びていきました。多くのエンジニアが日常的にAIを使うようになり、実装そのものは速くなったという実感の声もありました。 そこで本当に生産性が上がったのかを確かめるため、Findy Team+の詳細比較機能を使ってAI導入前後の各種メトリクスを比較してみました。 すると1人あたりの生産性はほとんど変わっていなかったのです。人が増え、AIで実装も速くなった。それでも1人あたりのアウトプットは増えていない。この食い違いが、最初の重要な問いになりました。 メトリクスを深掘りして見えた「レビュー詰まり」 AI導入前後で比較した結果、1人あたりのプルリクエスト作成数は横ばいのままでした。 この理由を探るため、Findy Team+で工程ごとのメトリクスに分解して深掘りしました。 ここで注目したのは、開発フローの段階ごとの指標です。変更のリードタイム/サイクルタイム、プルリクエスト作成数、そしてプルリクエストが作られてからマージされるまでのレビュー時間、オープン時間。これらを並べて、どの段階が想定と違う動きをしているのかを見ていきました。 まず変更のリードタイムに注目しました。すると、 コミットからオープンまでの平均時間は10%ほど速くなっていました。 しかし、 オープンからレビューまでの平均時間は15%ほど、レビューからアプルーブまでの平均時間は30%ほど遅くなっていた ことがわかりました。 コミットしてからプルリクエストを作成するまでは若干速くなったものの、プルリクエストのオープンから、レビューをもらって最終的にアプルーブをもらうまでの時間がAI導入前よりも悪化していた ことが、数値から読み解けました。 ここまでで 1人あたりの生産性が変わらなかった理由が、プルリクエストを作成してからマージされるまでのフローにある ということが見えてきました。 プルリクエストを作成してからマージされるまでに行われる工程の1つにレビューがあります。そこでレビューの工程に関するメトリクスに注目してみました。すると、 プルリクエストに対する平均コメント数が50%ほど増えている ことがわかりました。 ここまで深掘りしてわかったことは、AIによって実装にかかる時間は確かに短くなっている一方で、レビューの工程に問題が出ていたということです。 つまり、AIは開発フローの「実装」という一工程を速くしていました。しかし実装が速くなった分だけプルリクエストがレビュー待ちに積み上がり、フロー全体ではかえって詰まりやすくなっていたのです。 仮説 ここまでの分析から1つの仮説を立てました。 AIで実装が速くなった分、プルリクエストの質が下がっているのではないか。その結果レビューでのやり取りが増え、アプルーブまでの時間が延び、最終的にリードタイムが悪化している のではないか、という見立てです。 この仮説の裏付けを取るために、実際にレビューでやり取りが膨らんでいたプルリクエストを取得し、コメントの中身を分類しました。 わかったのは、指摘されていた内容の半分以上が作成者自身のセルフレビューで防げるような内容だったということです。例えば、消し忘れたデバッグコード、命名やフォーマットの不統一、考慮漏れのエッジケースや、そもそもビジネスロジックと要件が一致していないなど、本来は提出前にプルリクエストの作成者が気づかないといけない指摘です。 AIが書いたコードを十分に理解しきらないままレビュー依頼を出してしまい、本来は作成者が気づくべき部分をレビュアーがコメントで補っていたのです。プルリクエストへのコメント数が50%増えていたのは、この肩代わりの表れだったわけです。仮説は、実際のプルリクエストの中身によって裏づけられました。 改善アクション 原因が レビュー依頼前のセルフレビュー不足 にあるとわかれば、打つ手は具体的になります。私たちは、セルフレビューを個人の心がけに任せるのをやめ、仕組みとして組み込むことにしました。 具体的には、複数の観点を同時にチェックするセルフレビュー用のスキルを用意しました。そして、プルリクエストを作成するタイミングでこのスキルが強制的に実行されるようにしました。提出前に、AIが書いたコードをAI自身が複数の観点からレビューする工程を、フローの中に固定したのです。 このセルフレビュー用スキルの設計や中身については、別記事で詳しく解説しています。 tech.findy.co.jp 結果検証 改善アクションを打ったあとは、必ず数値に戻って効果を確かめます。このとき、いきなりリードタイムを見るのではなく、まず 施策がちゃんと使われているか という観点を確認するのがポイントです。 まずFindy AI+で、用意したスキルの実行状況を追いました。導入直後はばらつきがありましたが、運用に乗るにつれてスキルの利用率が上がっていきました。 利用率が十分に上がったタイミングで、改めてFindy Team+の数値を確認しました。すると、 レビューからアプルーブまでの平均時間が10%ほど速くなっている ことがわかりました。実装が速くなった効果が、ようやくフロー全体の速さとして表れ始めたのです。 また、プルリクエストの平均コメント数や変更障害率も若干の改善が見られました。 セルフレビューのスキルを強制したことにより、プルリクエストの質が上がり、不具合も減った ことが数値から読み取れました。 あわせてFindy AI+で、AIの使われ方そのものの推移も見てみました。改善のサイクルを回していくなかで、トークン使用量もセッション数も右肩上がりに伸びていました。そして同じ時期に、プルリクエストの作成数も増えています。 AIの活用と開発生産性がようやく同じ方向に動く、つまりAI推進と生産性がリンクし始めた ことが数値から読み取れました。 もし数値が動いていなければ、仮説か改善アクションのどちらかが外れているということなので、また数値を見るステップに戻ります。当たっていれば、次のボトルネックを探しに行く。これを繰り返すのが、 可視化と改善のサイクル となります。 まとめ このように可視化と改善を繰り返した結果、横ばいが続いていた1人あたりのプルリクエスト作成数も増加に転じました。AI導入前後の2024年から2025年では変化がありませんでしたが、セルフレビューを仕組みに組み込んだあとでは、1人あたりのプルリクエスト作成数が約1.5倍に増えていました。 今回お伝えしたかったのは、 可視化と改善はセットで考える ということです。 数値を可視化するだけでは、現場は変わりません。今回も、Findy Team+で生産性が横ばいだと可視化できただけでは何も解決せず、そこからメトリクスを深掘り、仮説を立て、プルリクエストを分析して、セルフレビューを仕組みに組み込むという改善まで進めて、初めてリードタイムに変化が出ました。 逆に、可視化のない改善は再現性のないものになります。もし可視化をせずに「レビューが詰まっている」という感覚だけで打ち手を考えてしまうと、原因がプルリクエストのセルフレビュー不足にあることにも、施策が効いたかどうかにも辿り着けませんでした。 可視化が改善の入口を教え、改善の結果がまた数値に表れる。 数値を見る → 仮説を立てる → 改善アクションを打つ → 結果を検証する というワンサイクルは、 可視化と改善を再現性高く行う ための型です。 AIを導入しただけではアウトプットも生産性も上がりません。可視化された数値を起点に改善のサイクルを回し、ボトルネックを一つずつ解消していく。これによって初めて、AI活用は開発生産性まで繋がってきます。 私たちもまだ、次の問題を探しながら可視化と改善のサイクルを回し続けている途中です。 開発生産性の向上やAI推進は、可視化と改善のサイクルを継続することで、継続して積み重ねていくもの なのです。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。 CTO室/Platform開発チームでSREを担当している富田( @Cooking_ENG )です。 ファインディの「Platform開発チーム」は全社横断のSREの役割を担っています。当社のサービスがどれほどの負荷に耐えられるかを把握し、性能の問題を表面化・改善することで、ユーザーに安定したサービスを提供できる状態を目指しています。 そこで、Grafana Labsが提供するGrafana Cloud k6を採用し、 負荷試験環境をゼロから構築 しました。今回は、 Findy Conference を対象に負荷試験を実施しています。 Findy Conferenceは、テックカンファレンスに特化したプラットフォームサービスです。 conference.findy-code.io この記事では、負荷試験ツールの選定から、本番トラフィックを再現するシナリオの作成、Grafana Cloud k6とDatadogを組み合わせた負荷試験の実施までの取り組みを、初めて負荷試験を担当するエンジニアの方々や、これから導入を考えているチームに向けて紹介します。 負荷試験環境を作ることになった背景 負荷試験ツールの選定 ファインディで重視した判断軸 候補ツールの比較 負荷試験のシステム構成 負荷シナリオの作成 har-to-k6を使ったスクリプトの作成 負荷のかけ方を調整するOptionsブロックの話 想定する負荷がかけられているかをDatadogを使って確認する方法 作成でつまずいたポイント Options内の値はリテラル必須 認証付きエンドポイントの扱い 負荷試験の実施と結果 負荷規模と必要コンテナ数 Datadogで取ったメトリクス まとめ 負荷試験環境を作ることになった背景 Findy Conferenceでは、セッションの開始や終了のタイミングで、参加者の方々が一斉にアクセスする 瞬間的なスパイク が発生する特徴があります。 過去のBlogにまとめているので、気になる方はぜひ こちらの記事 もご覧ください。 tech.findy.co.jp こうしたスパイクに備えるため、カンファレンス開催前にコンテナ数を増やす運用をしてきました。コンテナ数を決める際の判断はチームの経験値に基づいていたため、本当にその数で十分なのか、過剰になっていないかを根拠を持って判断できない状態でした。 そこで、想定するスパイクに対して 実際の計測値に基づいた最適なコンテナ数を設置できる 状態を目指し、負荷試験環境の整備に取り組むことにしました。 負荷試験ツールの選定 ファインディで重視した判断軸 ツール選定にあたり、ファインディの状況に合わせて次の判断軸を置きました。 インフラ管理が不要であること(環境の構築・運用コストを抑えたい) シナリオ作成の自由度(本番に近いユーザー導線を再現できる) サービスの継続性(SaaSとして長く使えるか) コスト(従量課金で、使わないときに費用が膨らまないこと) 候補ツールの比較 候補として、次のツールを比較しました。 サービス シナリオ記述 提供形態 運営元 特徴・得意領域 Grafana Cloud k6 JavaScript SaaS(OSS版あり) Grafana Labs モダンな開発体験、Grafana統合、専用ダッシュボードが標準。OSS版でローカル実行も可能 BlazeMeter JMeter/GUI SaaS Perforce JMeter資産を活かしやすい。詳細なレポートとGUI操作に強み Gatling Enterprise Scala/Java SaaS Gatling Corp 大規模負荷試験に強み。Javaエコシステムとの親和性が高い Locust Python OSS Locustコミュニティ Pythonでシナリオを柔軟に記述できる。軽量で分散実行にも対応 JMeter GUI/XML OSS Apache Software Foundation 長年の実績と豊富なプラグイン。対応プロトコルが幅広い それぞれのツールには得意な用途があります。今回はファインディの判断軸を基準に評価しました。 評価の結果、Grafana Cloud k6を採用しました。 先に挙げた4つの判断軸それぞれに対する評価は次のとおりです。 判断軸 Grafana Cloud k6での評価 インフラ管理 マネージドサービスのため、負荷試験用の環境を自前で構築・運用する必要がない シナリオ作成の自由度 JavaScriptベースでシナリオを記述するため、フロントエンド/バックエンド双方のエンジニアが読み書きしやすい サービスの継続性 運営元のGrafana Labsが監視・可視化領域のサービスを長く提供している実績があり、長期利用の見通しを立てやすい コスト Proプランの月額が$19、500 VUh/月までは無料枠、超過分も$0.15/VUhの従量課金で、使わないときに費用が膨らまない(2026年6月時点。最新は 公式料金ページ を参照) 負荷試験のシステム構成 負荷シナリオの作成 ここからは、実際に負荷シナリオをどう作っていったかを紹介します。 har-to-k6を使ったスクリプトの作成 本番に近いユーザー導線を再現するには、ユーザーがどのエンドポイントにどの順でアクセスするかをシナリオとして書き起こす必要があります。当初は手作業でシナリオを書こうとしましたが、ステージング環境ではCognitoに加えてファインディ独自の認証基盤「Findy ID」など複数の認証を突破する必要があり、認証の流れをスクリプトで再現するのは簡単ではありませんでした。 そこで、 har-to-k6 を使い、ブラウザの操作ログからk6のシナリオを生成する方法に切り替えました。har-to-k6は、ブラウザの開発者ツールで書き出せる HAR(HTTP Archive)ファイル を入力として、k6用のJavaScriptシナリオを生成してくれる公式ツールです。ブラウザで一度認証を通したあとの操作をHARとして記録すれば、認証後のリクエストをそのまま再現できるため、手作業で書くよりも本番に近いシナリオを効率よく用意できました。 おおまかな流れは次のとおりです。 ブラウザの開発者ツールで、再現したい操作を実行し、HARファイルを書き出す har-to-k6 input.har -o script.js でk6用のJavaScriptに変換する 生成されたスクリプトを、シナリオに不要なリクエストの除去や認証情報の環境変数化などに合わせて整える HARから変換した直後のスクリプトには、画像・CSS・JavaScriptファイルなどの静的アセット取得のリクエストや、HARに含まれていた認証情報(パスワードなど)がそのまま残っています。これらの整理にはClaude CodeなどのAIコーディング支援ツールが便利でした。 ただし、平文のパスワードをそのままAIに渡すのはセキュリティ上避けたいので、先に認証情報を __ENV.TEST_PASSWORD のような形で環境変数化し、それから不要なリクエストの除去をAIに任せました。AIを活用することで、手作業で1つずつ削るよりも短い時間で本番に近いシナリオを用意できました。 負荷のかけ方を調整するOptionsブロックの話 k6のシナリオでは、 options というブロックで負荷のかけ方を細かく制御できます。今回のシナリオでは、 ramping-arrival-rate というexecutorを使って、 スパイクの立ち上がり・ピーク・収束を1本のシナリオで再現する 構造を採用しました。 なお、k6では負荷シナリオの関数が1回実行されることを「イテレーション」と呼びます。今回のシナリオは1イテレーションのなかで複数のHTTPリクエストを送る構成です。 実際のOptionsブロックは次のような形です(数値は記事用のダミー値であり、対象サービスや想定するスパイク規模に応じて調整します)。 export const options = { scenarios: { spike: { executor: 'ramping-arrival-rate', startRate: 0, timeUnit: '1m', preAllocatedVUs: 50, stages: [ { duration: '10s', target: 100 }, { duration: '10s', target: 200 }, { duration: '50s', target: 200 }, { duration: '10s', target: 0 }, ], }, }, cloud: { distribution: { tokyo: { loadZone: 'amazon:jp:tokyo', percent: 100 }, }, }, }; 主な設定項目は次のとおりです。 設定項目 説明 executor 負荷のかけ方のパターン。 ramping-arrival-rate は、各ステージのtargetに向けてイテレーション数をなめらかに増減させ、山型の負荷曲線を表現できる startRate 開始時点のレート。今回は0から立ち上げる timeUnit targetの単位時間。 '1m' にすると目標値を「1分あたりのイテレーション数」で指定できる preAllocatedVUs 目標レートを捌くために事前に確保しておく仮想ユーザー(VU)数 target 各ステージで到達させたい目標イテレーション数/ timeUnit 。 timeUnit: '1m' なら target: 100 は「1分あたり100イテレーション」を意味し、durationをかけてその値までなめらかに増減する timeUnit を '1m' にしているのは、過去のカンファレンスの実測値を「1分あたりのリクエスト数」で把握していたためです。この実測リクエスト数を1イテレーションあたりのリクエスト本数で割ることで、必要なイテレーション数(target)を算出し、シナリオの単位もこれに揃えました。 stagesを4段に分けているのは、本番のスパイクを次のような流れで再現するためです。 1段目(10秒):本番想定の半分まで急速に立ち上げ、アクセス急増の前兆を再現 2段目(10秒):本番想定まで一気に到達させる 3段目(50秒):本番想定の負荷を維持して、システムが安定して捌けるかを観察する 4段目(10秒):目標値を0に戻し、収束させる cloud.distribution では、Grafana Cloud k6上で負荷を発生させるAWSのリージョンを指定しています。今回は実際のユーザーアクセスに近づけるため、東京リージョン( amazon:jp:tokyo )を100%としました。 想定する負荷がかけられているかをDatadogを使って確認する方法 ここがいちばん試行錯誤したところでした。 k6側のレポートで「目標としていたイテレーション数を発行した」と表示されていても、それが アプリケーションのコンテナに実際に届いたリクエスト数 と直接一致するわけではありません。今回のシナリオは1イテレーションで複数のリクエストを送るためです。 そこで、まず「イテレーション数 × 1イテレーションあたりのリクエスト本数」で、アプリケーションに届くはずのリクエスト数(期待リクエスト数)を見積もりました。そのうえで、k6側のtargetを微調整しながら、期待リクエスト数どおりの負荷がかかるように調整していきます。 調整したリクエストが実際に届いているかは、Datadogで確認します。ファインディではもともとDatadogでオブザーバビリティに取り組んでおり、各サービスのダッシュボードを運用しています。負荷試験のメトリクスも普段の監視と同じダッシュボードで確認できるため、状態を把握しやすい環境がすでに整っていました。 ただし、どのメトリクスを見るかで数字の意味が変わります。観測候補をいくつか比較した結果、 ALBのレスポンス数 を見ることにしました。今回はレスポンス数を、アプリケーションが実際に処理した負荷の指標として扱っています。 観測対象 負荷確認の用途として 理由 ALBのレスポンス数 適している アプリケーションが実際に返したレスポンス数。見積もった期待リクエスト数と突き合わせやすい CloudFrontのリクエスト数 向かない CDNキャッシュや静的アセット分が水増しされ、k6の発行数とずれる APMのリクエスト数 やや向かない サンプリングで一部を間引いて記録するため、実際より低めに見えることがある ALBのレスポンス数の推移を見て、見積もった期待リクエスト数どおりの負荷が処理されているかを判断します。値が想定より少ない場合は、CloudFront側で弾かれているリクエストがあるか、シナリオ側で意図しないエンドポイントを叩いていないかを見直すサインになります。 次の図は、ある負荷試験でのALBのレスポンス数の推移です。シナリオで設計したとおり、短時間で立ち上がってピークに達し、収束していく山型になっていることが確認できます。 作成でつまずいたポイント シナリオを書く過程でつまずきがあったので、これから取り組む方向けに共有します。 Options内の値はリテラル必須 Grafana Cloud k6のプレビュー画面では、Optionsブロックの値を静的解析してVUh(仮想ユーザー時間)を試算します。このとき、 Math.round() のような関数呼び出しや、外部の定数を参照する書き方は評価できず、 VUhが正しく計算されません 。 // NG(VUh が計算されない) const MaxTarget = 200; export const options = { scenarios: { spike: { stages: [ { duration: '50s', target: MaxTarget }, // 変数参照は評価されない ], }, }, }; // OK(リテラル数値で書く) export const options = { scenarios: { spike: { stages: [ { duration: '50s', target: 200 }, ], }, }, }; DRYに書きたくなる気持ちはありますが、Optionsブロックに限ってはリテラルで書くことを徹底すると安全です。 認証付きエンドポイントの扱い ステージング環境を本番相当にスケールアップして試験する場合、認証が挟まる導線では CloudFront → Cognito → アプリケーション のようにリダイレクトが連鎖します。最初から本番想定のシナリオを流すと、認証で弾かれてうまく負荷がかからないことがあります。 そこで、まずは /health のような疎通確認用エンドポイントに redirects: 0 を付けてリクエストを送り、想定どおりに200や302が返ってくるかを確認してからシナリオを組み立てるのがおすすめです。 負荷試験の実施と結果 ここまでで作ったシナリオを使い、ステージング環境を本番相当にスケールアップしたうえで負荷試験を実施しました。ここでは、ファインディがどのように 「想定スパイクごとに必要なコンテナ数」を割り出した のかを紹介します。 次の図は、Grafana Cloud k6で負荷試験を実行したときの結果サマリです。設計したとおりに負荷がかかってピークに達し、その間エラーを出さずに処理できていることが確認できます。 ※画像はイメージです。 負荷規模と必要コンテナ数 過去のカンファレンスでの参加者数と実測リクエスト数から、想定する負荷規模(1分あたりのリクエスト数)を複数段階用意し、負荷規模ごとに試験しました。 コンテナ数を判定する指標は、次のように定めました。 項目 内容 判定指標 ecs.fargate.cpu.percent (Datadog) 観測対象 Findy Conferenceのなかで最もリクエストが集中するバックエンドサービス 閾値 50% ボトルネックになりやすいバックエンドサービスのCPU使用率を直接観測することで、コンテナ数の過不足を判断します。 閾値を50%にした背景 オートスケールはより低いCPU使用率で発動するように運用していますが、スパイク時は追いつく前にアクセスが集中してしまいます。 そのため、発動を待つよりも、CPU使用率が50%を超えた時点でコンテナを増やす判断をしたほうが、カンファレンス運営では安全だと考えました。 試験は次のサイクルで進めました。 想定する負荷規模のシナリオをGrafana Cloud k6から実行する DatadogでバックエンドサービスのCPU使用率とSLOに関わるメトリクスを観測する CPU使用率が閾値(50%)を超えた場合はコンテナ数を増やし、同じシナリオを再実行する SLOの範囲内に収まる最小のコンテナ数を、その負荷規模での必要数として記録する この流れを負荷規模ごとに繰り返した結果、想定するカンファレンスの規模ごとに必要なコンテナ数を割り出すことができました。これにより、開催前のコンテナ調整を経験頼みではなく計測値に基づいて行えるようになっています。 Datadogで取ったメトリクス 試験中はCPU使用率のほかにも、次のメトリクスをDatadogで観測しました。 メトリクス 観測する目的 ECSのCPU・メモリ使用率 コンテナのリソースに余裕があるか(コンテナ数判定の主指標) APMのレイテンシ(p95) 遅いリクエストがSLOの範囲に収まっているか DBのコネクション数 データベース側がボトルネックになっていないか エラー発生率 負荷によってエラーが増えていないか また、試験結果の分析にはDatadogのBitsAIも活用しました。試験中のメトリクスからサマリを生成してNotionに集約し、開発チームへの共有や改善提案につなげています。 まとめ 今回は、Grafana Cloud k6とDatadogを組み合わせて、Findy Conferenceの負荷試験環境をゼロから構築した取り組みを紹介しました。コンテナ数の判断を、チームの経験値から計測値に基づくものへ変えられたことが大きな成果です。 ここで整理した手順は他のサービスにも応用できると考えており、今後はファインディで提供している各サービスでも横断的に負荷試験を実施できる体制へ広げていきたいです。 この記事が、これから負荷試験に取り組むエンジニアの方々やチームの参考になれば嬉しいです。最後までお読みいただきありがとうございました! ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。ファインディ株式会社でテックリードマネージャーをしている戸田です。 2026年6月17日に、Findy AI Meetup in Fukuoka #6を福岡で開催しました。当日参加くださったみなさま、ありがとうございました! findy-inc.connpass.com 今回のテーマは「AI×レビュー AIと共に変わるレビューの形」です。 AIで開発が速くなった一方で、レビューが新たなボトルネックになりつつあります。この記事では、当日のメイン登壇「生成AI時代のレビューを再設計する」を振り返りながら、ファインディが1500以上のPRに適用してきたレビューの仕組みを紹介します。 なお今回の登壇は、先日のAI Engineering Summit Tokyo 2026での登壇内容をベースに、レビューにフォーカスして再構成したものです。 tech.findy.co.jp Findy AI Meetup in Fukuokaについて 生成AI時代のレビューを再設計する AIで速くなったはずなのに、レビューが詰まり始めた 何が起きていたか — 4つの連鎖 AIが出力したコードの責任は「人間」にある コードレビューで「AIに任せる範囲」と「人間が見る範囲」を分ける まとめ Findy AI Meetup in Fukuokaについて Findy AI Meetupは、ファインディのエンジニアが主催する技術系オフラインイベントです。 生成AIやAIエージェントの活用を通じた開発生産性の向上をテーマに、社内での実践事例の紹介やエンジニア同士の交流を目的としています。 福岡での開催は今回で6回目となりました。回を重ねるごとに参加者同士の顔なじみも増え、福岡のエンジニアコミュニティとして根付いてきたことを実感しています。 今回はメイン登壇1本とLT2枠の構成で、定員35名は満席となりました。この記事では、そのうちメイン登壇の内容を振り返ります。 生成AI時代のレビューを再設計する メイン登壇では、ファインディ社内でレビューに何が起きていたのか、そこからどうレビューを設計し直したのかをお話ししました。 AIで速くなったはずなのに、レビューが詰まり始めた 最初に共有したのは、2025年のファインディ社内の開発データです。前年と比べると、次のような変化が起きていました。 指標 変化 補足 稼働人数 1.5倍 メンバー数は増加 1人あたりのPR作成数 横ばい 個人あたりの生産量は伸びず レビューからApproveまでの平均時間 +20分 前年比で延びた レビュー1件あたりの平均コメント数 +30% 指摘量が増えた 数値はいずれも2025年通年の社内データを前年と比較したものです。人数が1.5倍になればチーム全体の開発量は増えるはずなのに、1人あたりのPR作成数は横ばいで、レビューにかかる時間とコメント量はむしろ増えていました。 AIで開発を速くしたはずが、品質の確認とレビューが新しいボトルネックになる。この逆転現象が、社内のあちこちで起き始めていたのです。 何が起きていたか — 4つの連鎖 詰まりの正体を分解すると、次の4段階の連鎖になっていました。 AIでコード生成が高速化し、PRの提出ペースが上がる 書いたコードを十分に理解しないままPRを提出する 「本来は作成者が理解しておくべき部分」までレビュアーが肩代わりする レビュアーに負荷が集中し、人間レビューが詰まりの主因になる つまり、AIで速くなった工数がそのままレビュアー側に流れ込んでいた、ということです。上流の確認が追いつかないまま、その負担を下流のレビューが引き受ける構造になっていました。 AIが出力したコードの責任は「人間」にある ここで整理したのが、工程ごとの担当と責任の所在です。コードを書くのもPRを作るのもAIに任せられますが、品質と最終判断の責任は人間が引き受けます。 工程 担当 責任 コード変更 Pull request作成 セルフレビュー AI コード変更に対する責任 レビュー 人間 作っているものに対する責任 ポイントは、セルフレビューまでを「コード生成」の工程に含めてしまうことです。「AIに書かせて終わり」ではなく「AIに書かせ、AIにセルフレビューさせるところまでが生成」と捉え直すと、人間のレビュアーは初めて見るコードの粗探しから解放され、責任の境界がはっきりします。 コードレビューで「AIに任せる範囲」と「人間が見る範囲」を分ける 責任の境界が決まると、レビューで見るべき観点も自然と整理できます。登壇では、レビューを次のように2つの領域へ切り分けました。 AIに任せる (セルフレビュー) 人間が確認する (レビュー) コード規約・命名 ビジネスロジックが要件をクリアするか 型定義 アーキテクチャ・設計の妥当性 テストコード・テストケース 明確なセキュリティリスク AIに機械的なチェックを任せることで、人間の視点はコードそのものから一段引き上がり、より抽象的な観点に集中できます。レビューが「規約に沿っているか」の確認作業から、「この設計でいいのか」を考える時間へと変わっていく感覚です。 実際にファインディで運用しているレビュー用のSkillなどの解説は、登壇資料や以前の記事でも紹介していますので、是非ご覧になってください。 tech.findy.co.jp tech.findy.co.jp まとめ 生成AIの登場で、レビューには変わったことと変わらないことの両方があります。 変わったのは、AIに任せられる範囲が広がったことです。規約・命名・型・テストといった機械的なチェックはAIに委ね、人間のレビューはビジネスロジックや設計、要件への適合といった、より抽象的な観点へと移りました。 変わらないのは、最終的な品質と判断の責任を人間が引き受けるという点です。AIがどれだけコードを書いても、それを世に出す責任は人間に残ります。 AIに任せる範囲 人間が確認する範囲 コード規約・命名 ビジネスロジック 型定義 アーキテクチャ・設計 テストコード・テストケース セキュリティリスク 機械的なチェック全般 要件への適合 この境界を成り行きに任せず、意図的に設計すること。それが、生成AI時代のレビューだと考えています。 そして早くも次回開催が決定しました!2026年8月5日(水)に開催予定です。 皆さんの熱いご支援のお陰で、Findy AI Meetup in Fukuokaは次回で1周年となっております。そこで次回は「AI × これまでと、これから AI開発の"今まで"と"これから"を語り尽くそう」と題しまして、AIに関して熱く語る会にしたいと思っています。ぜひご参加ください。 findy-inc.connpass.com ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは、Findy AI+のエンジニアをしている山岸です。 エンジニアの毎日は、頭をフル回転させることの連続です。 難しい課題に向き合い、コードと格闘した日ほど、ランチの一皿がじんわり沁みる――。 午後のスイッチを入れ直してくれるそんな一食は、エンジニアにとってちょっとした活力源だと思っています。 前回好評だった天ぷら特集の Part1 に続き、今回は Part2 を執筆しました。 焼肉と唐揚げ、それぞれの「これが私のご褒美ランチ!」をお届けします! 大阪・梅田:精肉卸問屋直営焼肉店 牛次郎 東京・五反田:話食よし おわりに ■ dan / AI+開発 ■ 普段はFindy AI+でバックエンド・フロントエンドの開発をしているdanです。 大阪・梅田:精肉卸問屋直営焼肉店 牛次郎 お店の基本情報 場所 : 大阪府大阪市北区曽根崎2-5-22 日宝パティオ曽根崎 1F 営業時間 : 月〜金 15:30〜27:00 / 土日祝 12:00〜27:00 価格帯 : ¥1,000〜 雰囲気 : 客層は幅広く、ひとりでふらっと入っても、仲間とワイワイ行っても馴染める気取らないお店 梅田ダンジョンに飲み込まれたくない方は、BIGMAN → HEP FIVE → お初天神のルートがおすすめです。 梅田周辺は飲食店が多すぎて、逆にどこに行くか迷いがち。牛次郎はお初天神の通りをまっすぐ進んでいくとたどり着けるので、梅田に慣れていない人でも比較的行きやすい場所にあります。 おすすめはハラミとユッケ。 ランチ写真 ハラミは焼いて食べてみると、脂っこすぎず、でもしっかり肉の旨みがあります。タレでごまかす感じではなく、肉だけでちゃんと美味しい。これ食べたらもう〆に行っていいレベルです。 ユッケは、焼肉屋でよくある細長くカットされたスタイルではなく、薄切りのお肉をお皿に広げ、その上に卵黄と薬味がのった独特のスタイル。薬味は少しスパイシーで、卵黄のまろやかさと合わさってパンチのある味わいです。 食べる前後の楽しみもあります。ミナミの雰囲気はちょっと怖いけど大阪らしさを感じたい、グラングリーン大阪でチルしたいなど、牛次郎の周辺には寄り道したくなるスポットが揃っています。美味しい焼肉と街歩きをセットで楽しめるのも、梅田ならではの魅力です。 ■ 山岸真悠子 / CTO室 AI推進 / AI+開発担当 ■ こんにちは、CTO室のAI推進でFindy AI+の開発を担当している山岸です。 ファインディのオフィスは大崎にあるので、ランチには隣の五反田までよく足を運んでいます。 今回ご紹介するのは、五反田駅前にある創作和食のお店、 話食よし です。ランチの唐揚げ定食がとにかく美味しいので、ぜひ知っていただきたく筆を執りました! 東京・五反田:話食よし お店の基本情報 場所 : 東京都品川区西五反田1-7-1 リビオ五反田プラグマGタワー2F(五反田駅から徒歩1分、大崎広小路駅から徒歩6分) 営業時間 : ランチ 平日11:30〜14:00 / ディナー 17:00〜23:30(不定休) 価格帯 : ランチ ¥1,000〜¥1,999 / ディナー ¥5,000〜¥7,999 雰囲気 : 五反田駅前なのに落ち着いていて、ひとりでもカジュアルに入れる和食店。半個室があるので会食にも使えます 五反田の駅前というと賑やかなイメージがあるかもしれませんが、ここは一歩入ると驚くほど落ち着いた雰囲気です。 気取らずにふらっと入れるのに、ちゃんと美味しい。そんなちょうどいいお店です。 ランチ写真 おすすめは 唐揚げ定食 。 まずは全景から。メインの唐揚げに、ごはんと味噌汁、それに卵焼きやひじき、千切りキャベツ、漬物の小鉢がついた王道の定食スタイルです。 彩りも良く、運ばれてきた瞬間にテンションが上がります。 そして主役の唐揚げ。衣がカリッ、サクッとしていて、おそらく片栗粉で揚げているタイプです。 中はジューシーで、ポン酢と大根おろしでさっぱり、マヨネーズでこってり、と一皿で二度おいしいを楽しめます。 ごはんはツヤツヤ。一粒一粒がしっかり立っていて、これだけでも箸が止まりません! さらに嬉しいことに、 味噌汁はおかわり自由 です。 ちなみに夜に行っても外れなし。鮮魚を使った創作和食が美味しく、半個室があるので少人数の会食やチームの集まりにもぴったりです。 最近五反田ではお気に入りのお店の閉店が続いていて、「もっと通っておけばよかった」と思うことが何度かありました。 美味しいお店との出会いも、結局は通い続けてこそだよなあと。推しのお店には、無理のないペースで足を運んでいきたいですね。 五反田でランチに迷ったら、ぜひ話食よしへ。みなさんもぜひご賞味あれ〜 おわりに 今回は焼肉と唐揚げのお店をご紹介しました。どちらもエンジニアの日々を支えてくれる、元気の源になるランチです。 ファインディには「社内コミュニケーション補助」という制度があり、組織や拠点を超えた交流が促進されています。おすすめのランチを共有し合えるのも、このシリーズの楽しみのひとつです。 また、今回ご紹介したように、ファインディでは全国各地からさまざまなエンジニアが活躍しています。技術にこだわり、より良いものを追求する仲間とともに働いてみませんか? 現在、新しいメンバーを募集しています。興味のある方は、ぜひこちらからチェックしてみてください!
こんにちは。ファインディのPlatform開発チーム(以降、SREチーム)でSREを担当している原( こうじゅん )です。 SREチームでは、AWSのユーザーとGitHubのアカウントの管理をTerraformで運用しています。Slackから申請が来たらTerraformのコードを書き換えてPRを出す、という作業を以前はDevinで自動化していました。 しかしDevinのアカウント利用形態が変わったことをきっかけに、この自動化基盤をClaude Code Actionsへ移行しました。同じようにAIツールでインフラ運用を自動化している方や、Claude Code Actionsの実践的な使い方を探している方に向けて、移行に至った判断理由と、移行後のアーキテクチャについて紹介します。 背景:Devinでアカウント管理を自動化していた 移行を決めた理由 移行後のアーキテクチャ triageジョブ:申請の振り分け routine-prジョブ:ファイル編集とPR作成 escalateジョブ:自動処理できない申請の通知 まとめ 背景:Devinでアカウント管理を自動化していた ファインディでは、GitHub Teamへのメンバー追加やチームの作成・廃止といったアカウント管理を、SREチームがTerraformで運用しています。 エンジニア・ビジネスサイド問わず、メンバーがSlackから申請を出すと、SREがTerraformのコードを手動で書き換えてPRを出し、レビュー・マージ後にapplyされる、という流れでした。毎回決まったパターンのYAML編集とPR作成を手作業でやるのは、典型的なトイルです。 このトイルを減らすために、Devinを使って自動化していました。Slackの申請内容をDevinに渡すと、Terraformリポジトリのコードを編集してPRを作ってくれる仕組みです。この取り組みの詳細は次の記事で紹介しています。 tech.findy.co.jp 移行を決めた理由 きっかけは、 2026年4月1日のDevinのアップデート です。このアップデートでDevinアカウントを持たないユーザーはSlackのDevinスレッドに参加できなくなりました。 アカウントを持たない申請者が@Devinに返信してもDevinが反応せず、「動かない」というSREへの問い合わせが増えました。コンプライアンス上、全メンバーへのアカウント一律払い出しも現実的ではありません。そのため、Devinからの移行に踏み切りました。 移行先の選定にあたっては、次の3つを重視しました。 全メンバーが個人アカウントなしで使えること — Anthropic公式の@Claude(Claude in Slack)も検討しましたが、各ユーザーにClaude Codeのアカウントが必要で同じ問題を抱えて断念しました 申請者の体験を変えないこと — Slack Workflowのフォームはそのまま、バックエンドだけを差し替えます チームのツール統一 — 開発チーム全体がすでにClaude中心の開発体制になっており、自動化基盤もそれに合わせました この選定理由より、今回のアーキテクチャとなりました。 移行後のアーキテクチャ 移行後の全体構成は次のとおりです。 flowchart TD subgraph Slack["Slack (申請者の体験)"] F1["メンバー申請"] F2["チーム申請"] Thread[("申請者スレッド")] end subgraph GHA["GitHub Actions"] Triage["triage\n(Claude Code Action)\n整合性チェック・名前解決"] RoutinePR["routine-pr\n(Claude Code Action)\nファイル編集・PR作成"] Escalate["escalate\n自動処理不可を通知"] end SRE["SRE承認・マージ"] Apply["terraform apply"] F1 -->|workflow_dispatch| Triage F2 -->|workflow_dispatch| Triage Triage -->|routine| RoutinePR Triage -->|escalate| Escalate RoutinePR -->|PR作成| SRE RoutinePR -->|受付通知| Thread Escalate -->|エスカレ通知| Thread SRE -->|マージ| Apply Apply -->|完了通知| Thread classDef unchanged fill:#e6f4ea,stroke:#34a853,stroke-width:2px,color:#1b5e20; class F1,F2,Thread unchanged style Slack fill:#f1f8f4,stroke:#34a853,stroke-width:2px; Slack Workflowからの申請がGitHub Actionsのworkflow_dispatchをトリガーし、正常系ではtriageジョブとroutine-prジョブの2つのClaude Code Actionが順に処理する構成です。 ただし、Slack Workflow Builderから直接workflow_dispatchは呼べません。間にSlack Custom Function(Deno Slack SDK)を挟み、GitHub APIへの認証付き呼び出しを行っています。 api.slack.com sequenceDiagram participant WF as Slack Workflow Builder participant CF as Custom Function<br/>(Deno Slack SDK) participant GH as GitHub API WF->>CF: フォーム入力値を渡す CF->>CF: GitHub App秘密鍵でJWT署名 CF->>GH: JWT → Installation Access Token取得 GH-->>CF: Token返却 CF->>GH: workflow_dispatch(Token認証) GH-->>CF: 202 Accepted 申請者から見ればSlackのフォームに入力するという体験は変わっていませんが、裏側ではCustom FunctionがGitHub Appの認証(JWT署名 → Installation Access Token取得)を行い、workflow_dispatchを呼び出しています。 triageジョブ:申請の振り分け 最初のClaude Code Actionは、申請内容の整合性チェックと振り分けを担当します。 チーム名の名前解決(申請者が入力した表記ブレを、リポジトリ上の実際のディレクトリ名に解決する)、申請内容のバリデーション、そしてroutine(自動処理可能)かescalate(自動処理不可)かの判定を行います。 triageとPR作成を1つのジョブにまとめることもできますが、責務と権限を分離するためにあえて分けました。triageジョブは --max-turns 5 と少ないターン数に制限し、使えるツールも Read と Bash(ls:*) だけに絞っています。判定がおかしければPR作成に進まない安全弁にもなります。 判定結果はJSON Schemaで構造化出力させるので、後続のジョブが確実にパースできます。 claude_args : | --max-turns 5 --allowedTools Read,Bash(ls:*) --json-schema '{"type":"object","properties":{"verdict":{"type":"string","enum":["routine","escalate"]},...}}' routine-prジョブ:ファイル編集とPR作成 triageでroutineと判定された申請は、2つ目のClaude Code Actionが処理します。TerraformリポジトリのYAMLファイルを編集し、ブランチを切ってPRを作成するジョブです。 --max-turns 20 とターン数を多めに確保し、ファイル編集やgit操作、PR作成に必要なツールを許可しています。 claude_args : | --max-turns 20 --allowedTools 'Read,Edit,Write,Bash(git checkout:*),Bash(git add:*),...,Bash(gh pr create:*)' PR作成後はSlackの申請スレッドに受付通知を送り、SREがレビュー・マージすればterraform applyが走って完了通知が届きます。 escalateジョブ:自動処理できない申請の通知 triageが自動処理できないと判断した申請は、Slackの申請スレッドにその旨が通知されます。Claude Code Actionは使わず、シェルスクリプトで通知を送るだけのシンプルなジョブです。 まとめ Devinのアカウント利用形態変更をきっかけに、アカウント管理の自動化基盤をClaude Code Actionsへ移行しました。Claude Code Actionをtriage(振り分け)とroutine-pr(PR作成)の2段階に分け、それぞれの責務と権限を絞った設計にしています。 現在、GitHubアカウント管理の移行は完了し、運用を開始しています。今後はAWSのユーザー管理なども移行を進めていきます。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
ファインディの森( @jiskanulo )と西村( @sontixyou )です。 2026年6月11日に開催されたAnthropic主催のイベント「Code w/ Claude: Extended | Tokyo」に終日参加してきました。 claude.com 本記事はその参加レポートです。私たちがセッションを聴いてどう感じたか、セッションの内容をどう活かすかの感想も書いていきます。 Workshopで気になったセッション How we Claude Code Ship your first Managed Agent ドメインエキスパートがソフトウェアを作る What happens when domain experts can finally build The 1% problem: How domain expertise + Claude let a 2-person team hit #1 on a global classification benchmark まとめ ファインディでは一緒に働く仲間を募集しています Workshopで気になったセッション 西村は主にAnthropicメンバーによるWorkshopへ参加しました。Anthropicが取り入れている設定や仕組みを弊社のプロダクト開発フローに適用できるかを意識して聴きました。 How we Claude Code claude.com Anthropicメンバーが実際に使っているClaude Codeのセットアップ方法を紹介するものです。 今回のイベントでのワークショップで説明された題材は次のリポジトリで公開されています。 github.com このセッションで最も印象に残ったのは「徹底インタビュー」と「HTML先行プロトタイプ」の2つです。 「徹底インタビュー」では、Claude Codeにインタビュアー役を担わせて要件を引き出します。 インタビューでは、つぎのインタビュープロンプトをClaude Codeに渡して、HTMLのプロトタイプを作るための要件を引き出す設計になっていました。 I want to build a bill-splitting app, can you help me brainstorm with me on who the audience is, and then interview me in-depth using the AskUserQuestion tool about what to build, focusing on pulling out any ambiguities to create a spec. 上記のプロンプトを実行し、Claude Codeとのやりとりを通して、要件の曖昧さを潰していき、SPEC.mdを作成する流れが紹介されました。要件の曖昧さを上流で潰すことで、実装のやり直しを減らすという設計思想です。 つぎに「HTML先行プロトタイプ」ではSPEC.mdをもとに、4つの異なるデザインを提案させて、HTMLのプロトタイプを作る流れが紹介されました。 Read ../phase-1-planning for its spec file, then help me figure out the overall design of this app. Explore 4 different designs, and create a set of HTML files with important screens for each. 上記のプロンプトを実行すると、つぎのHTMLのプロトタイプが出力されました。 Markdown形式でデザイン仕様を書き出すのではなく、クリックできるHTMLのプロトタイプを出力することで、見た瞬間に判断できます。 設計するタイミングで、Claude Codeとの認識合わせが視覚的にできることで、実装フェーズでのやり直しが少なくなり、トークン使用量の削減につながります。 また、既に運用しているサービスではデザインシステムといったデザイン基盤があれば、精度よくHTMLを出力できる可能性があります。それによって、新しい機能のデザインをするときの認識合わせがスムーズになりそうだと思いました。 Ship your first Managed Agent claude.com Claude Managed Agents(CMA)の全体像を、インシデントレスポンスのデモを通して整理するワークショップです。 このセッションで印象に残ったスライドが「The brain left the box」でした。 以前のアーキテクチャは、Agent loopとTool executionが同じコンテナの中に同居していました。Managed AgentsではAnthropicが管理する1つのサービスとして動き、ツール実行のSandboxはツールが必要なときだけオンデマンドで起動します。クライアントが切断されていてもループは動き続けるため、ローカルのターミナルを閉じてもセッションが止まりません。 これまで西村の開発環境のClaude Codeの使い方では、ローカルPCでClaude Codeを稼働させているため、プロンプトを入力しないとAIエージェントが動き出せませんでした。また、並列セッション数も最大8が限界でした。 Managed Agentsはこの2点を変えられると感じました。オンデマンドで非同期で動き続ける実行環境があれば、人間の合図を待たずに実装する用途が一気に現実的になります。 試したい用途として思い浮かんだのは、着手できていないイシューの開発・リファクタリング、そして人間が介在せずにPRをマージすることです。 最後の「自律マージ」には前提条件を考えなくてはいけません。最低限必要な前提条件を決めておかなければなりません。 例えば、テストカバレッジが一定基準を超えていること、テストの堅牢性が担保されていることなどです。 これらの前提条件を満たすための仕組みを整えることが、AIエージェントによる自律マージの実現に向けた重要なステップになると感じました。 ドメインエキスパートがソフトウェアを作る 森はFounder stageを中心に話を聞いていました。 いずれのセッションもドメインエキスパートの知識・決定が重要であることを話されていました。 いくつかご紹介します。 What happens when domain experts can finally build claude.com 登壇はクイーンズランド大学のJason Tangen教授。AIネイティブ時代のプロダクトは、汎用的な人材ではなく深いドメイン知識を持つ個人から生まれるという主張です。 「去年までソフトウェアを一行も書いたことがなかった」という彼が、6ヶ月で3つの本番アプリをデプロイしました。 登場したアプリはいずれも学生向けのもので、シラバス生成ツール(classbuild.ai)、法廷証言トレーニング(crosscoach.ai)、学生向け説明ツール、学生の顔と名前を覚えるゲームなど。 専門家の頭の中に眠っていたアイデアが、AIにより作るコストが下がり具現化できるようになりました。 最後に聴衆へ「あなたが何年も密かに欲しいと思っていた小さなツールは何ですか?」「チームの中で、実際にその仕事をやり続けてきた人は誰ですか?」と問いかけられました。 コードを書く能力ではなく、「何を作るべきか」を知っていることが大切なのだと感じました。 The 1% problem: How domain expertise + Claude let a 2-person team hit #1 on a global classification benchmark claude.com 登壇者はGahee Seo氏。分類の誤りが金銭的損失に直結する貿易コンプライアンス領域で、ドメイン知識とClaudeの組み合わせが基盤モデル単体を超える優位を生むことを示します。 韓国からEUへの輸送など、さまざまな規制が複雑に絡み合う貿易の領域では、どんなAIモデルを使うかよりも専門家の判断フローをどう再現するかが優位を決めるとのことです。 最初のプロトタイプはSingle agentで1件の分類に6分かかっており、また、アウトプット精度にも課題があったとのことです。 課題を解決するために分類器を新しく訓練するのではなく、専門家が5段階で判断するワークフローを複製したという設計思想を発表してくれました。 入力が曖昧なまま処理が走らないようinput gateを設けたこと 全規制ルールをsystem promptに詰め込む設計をやめ、必要な文書を検索する Retrieval と出力結果を検証する Verifierという構想に転換 これらの対応をとりあげていました。さらにエージェントを役割ごとに分割することで30秒まで短縮しました。 アウトプットではなくワークフローを教える、という発言もありました。 期待する出力形式を示すのではなく、専門家がどういう順番で何を見て判断するかをシステム化する。職人芸をインフラに変える、という表現が使われていました。 まとめ 西村が午前に参加したAnthropicのワークショップ2つには、共通した問いがありました。 How we Claude CodeのHTMLプロトタイプは、実装前に人間の判断を前の工程に集約する仕組みです。Ship your first Managed Agentの非同期実行は、後工程での人間の介在を手放すための基盤です。 方向は逆に見えますが、根底にある問いは同じだと感じました。「人間はどこで関与すべきか」を先に設計する、という考え方です。その2点を組み合わせることで初めて、AIと人間の役割分担がきちんと設計できると思いました。 森の参加したセッションは「個人開発者・アーリーステージな創設者向け」という性格のものが多かったです。個人が専門知識を武器にプロダクトを作る事例が多く、それ自体は示唆に富んでいました。 どのセッションでも専門家の知識、決定が大事であるという主張を別の角度から語っていました。 一方で、チームでどう使っていくか・チームの中でどう成長していくかという話はありませんでした。 最近個人的に悶々としている「専門家がいない領域でどう専門性を担保するか」「専門家をどう育てるか、時間をかけるしかないのか」という問いには答えが見つからなかったので引き続き考え続けます。 ファインディでは一緒に働く仲間を募集しています ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。 ファインディ株式会社でテックリードマネージャーをやらせてもらっている戸田です。 生成AIが開発現場に入り込んでから1年あまり。Claude CodeやGitHub Copilotなどのエージェント型ツールも一般的になってきました。 その一方で、「AIを導入したのに、思ったほど速くなっていない」「むしろレビューが大変になった」という声を、社内外でよく聞くようになりました。 そんな中で先日、弊社主催の「AI Engineering Summit Tokyo 2026」にて 「速く作る」から「正しく作る」へ ─ 生成AI時代の開発フロー改革のロードマップと実行 ─ と題して登壇してきました。 ファインディ社内で1年強かけて見えてきた「AI導入の落とし穴」と、そこから組み立てた3段階のロードマップを共有する内容です。 ai-engineering-summit-tokyo.findy-tools.io この記事では登壇内容を振り返りつつ、AI導入の効果が伸び悩んでいる組織に向けて、ファインディがどのような順番で開発フローを作り変えてきたかを紹介します。 それでは見ていきましょう! AIを入れたのに、アウトプットは伸びていなかった 「速く作る」だけでは限界がある AI活用レベル レベル 1:AIエージェントでコード生成 レベル 2:AIエージェントでモノを作る レベル 3:AIで価値を生み出す まとめ AIを入れたのに、アウトプットは伸びていなかった ファインディも同様に生成AIの本格活用を進め、Claude CodeやCodexなどのAIエージェントが日常的な開発フローに入り込んできました。社内の体感としては「1人あたりのPR作成数も増えていそうだし、開発のリードタイムも短くなっているはず」というものでした。 しかし、Findy Team+で1年分の数値を計測してみると、想像とは違う景色が見えてきました。 まずポジティブな変化として、PR作成総数は前年比で伸びていました。ただ、その内訳を見ると、稼働メンバー数が約1.5倍に増えていたことが大きく、1人あたりのPR作成数はほぼ横ばいだったのです。 さらに、レビュー開始からApproveまでの時間は前年比でおよそ20分延び、PR1本あたりの平均コメント数・レビュー数も約30%増えていました。AIによるコード生成が増えた一方で、レビュー側の負荷が確実に積み上がっていたわけです。 シニア層と若手層で傾向を分けて見ると、もう一段深い構造が見えてきました。AIの出力を読んで検証できるシニアメンバーはアウトプットが上がる一方、経験年数が浅いほどAI出力の合否判断に苦戦する傾向があり、結果としてレビュー側に判断の負荷が集中していました。 組織全体として、 AIに使われている 状態に近かったとも言えます。「体感と事実がズレているかもしれない」と疑い、各種数値を可視化していたからこそ、実は「1人あたりのPR作成数は増えておらず、それどころかレビューの負荷が増えており、結果的に開発のリードタイムは長くなっていた」ことに気づくことができたのです。 「速く作る」だけでは限界がある 計測結果から見えてきたのは、「コードを書く速度が上がっても、ボトルネックがレビューに移り変わったために、全体のリードタイムは短くならなかった」、ということでした。 AIでコードを書くスピードは確かに上がります。一方で、内容を十分理解せずに生成するケースが増えると、PRの一貫性や正確性が落ちます。 指摘の量が増えると、リードクラスのレビュー時間が膨らみ、リードタイムが悪化します。最終的に、トータルのアウトプットはAI導入前とほぼ変わらない、という結果に着地します。 AIの成果物に対する確認や検証に時間がかかるようになり、レビューの負荷が増える。これがAI導入の落とし穴の一つです。そのため、AIの成果物の品質を再現性高く担保するための仕組みを整えることが必要になります。 そのために必要になるのが、「正しい作り方と手順」を仕組み化することです。馬を御する馬具(ハーネス)になぞらえ、AIの動きを暴走させず目的の方向へ導くための仕組みを整えることを ハーネスエンジニアリング と呼びます。 このハーネス化を、ファインディは 開発フロー改革 として進めてきました。具体的には、AI活用のレベルを3段階に分け、土台から段階的に積み上げていくロードマップを描いています。 AI活用レベル 開発フローを分解し、AIで何を肩代わりできるかをマッピングすると、3つのレベルが浮かび上がりました。 レベル 1:速く作る :コード変更とPR作成を中心に、AIで代替できる範囲を自動化する レベル 2:正しく作る :タスク分解とIssue作成までAIに任せ、「正しく作る」仕組みを整える レベル 3:必要なものを作る :要件定義やQAという「AIで代替しづらい」とされてきた領域に踏み込む ポイントは、「どれか1つを単独でやる」のではなく、Lv1 → Lv2 → Lv3 と段階的に積み上げて初めて効果が出るという点です。ここから各レベルを順に見ていきます。 レベル 1:AIエージェントでコード生成 レベル 1の目的は、コード変更とPull request作成までをAIエージェントに任せ、人間は本質的なレビューに集中することです。 このフェーズで真っ先に向き合う必要があるのは、「AIが出したコードの責任は誰にあるか」という問いです。 どれだけ自動化されても、AIが出力したコードの責任は人間にあります。品質と判断の最終責任は人間が引き受けることになります。 その前提の上で、レベル 1の工程を整理してみました。 コード変更とPull request作成はAIに全て任せることが出来ます。そしてレビューに関しては、コードの責任という観点から人間が行う必要があると考えていました。しかし、本当にそうなのか疑問に思いました。 自分自身がコードを書いてPull requestを作成していた頃を思い出してみてください。自分でコードを書いて、Pull requestを作成する。レビュー依頼を出す前にやっていたことがありました。セルフレビューです。 そしてセルフレビューで気づく内容と、実際に他のメンバーからもらうレビューの内容は観点や内容が違います。これを今回の開発フロー改革に当てはめました。 レビューをセルフレビューとレビューに分け、レベル 1ではセルフレビューまでをAIに任せることにしました。セルフレビューではコード変更そのものに対するレビュー、レビューでは人間が最終判断しないといけない内容にフォーカスしてレビューというように切り分けることにしました。 ここで重要なのは、AIを入れる前提として「AIと関係なく当たり前のこと」が揃っている必要がある、という点です。 アーキテクチャ・命名規則・型定義といったコード設計、十分なテストカバレッジ、一貫した設計パターン、そしてPRの適切な粒度・レビュー文化・タスク分解の習慣。 これらはAI以前から品質を保つために必須でしたが、AIエージェントが入ると一気に効いてきます。土台が弱いと、AIはその弱さを増幅する方向に働くからです。 ファインディがこの「土台」をどう積み上げてきたかは、次の記事で詳述しています。 tech.findy.co.jp その土台の上に、AIが参照するドキュメントとルールをガードレールとして整備します。 READMEやプロジェクトドキュメントで開発前提・アーキテクチャ・運用ルールを記述し、AGENT.mdやrulesでコード規約・命名規則・テスト方針をAIに自動参照させ、カスタムコマンドやプロンプトテンプレートで依頼タスクを規格化する。 この整備があって初めて、AIは使い物になるコードを出力してくれます。 ファインディではレベル 1を支える仕組みとして、Claude CodeのSkillを複数組み合わせています。代表的なものは次の通りです。 Pull request作成:typecheck/lint/test/buildといった品質チェックの自動実行、ブランチ命名規則の強制、Conventional Commitに沿ったコミット生成、PRテンプレートからのbody自動生成までを1コマンドで実行 Pull request作成前の自動セルフレビュー:セキュリティ/コード品質/規約準拠/Simplify観点/要件検証/チェックリスト照合の6観点で並列分析。信頼度の高い指摘のみを報告してノイズを抑制し、2026年4月時点で1500以上のPRで運用中 AI併用レビュー:Codex CLIを別系統として並行運用し、メインAIのレビューと統合してPRコメントに提示。AIの偏りに依存しない複眼チェックを実現 定期セルフレビュー自動化:平日の朝方にGitHub Actionsで起動し、直近1ヶ月変更されていない技術的負債となりうる既存コードに対して修正Pull requestを自動作成 チェックリスト自動更新:過去レビューコメントをGitHub APIで収集し、LLMで指摘パターンを分類してチェックリストへ反映。レビュアーの暗黙知をSkillに形式知として残す セルフレビュー周りの仕組みについては、それぞれ次の記事でも紹介しています。 tech.findy.co.jp tech.findy.co.jp これらは1リポジトリにSkill/Sub Agent/MCPとしてまとめており、Pluginとして運用することで /plugin install によるワンコマンド配布を実現しています。全員がcontributeできる構造にすることで、改善がそのまま組織全体に反映される回り方になっています。 レベル 2:AIエージェントでモノを作る レベル 1で「速く作る」の足回りが整うと、次にぶつかるのが「要件をどう実現するか」の手順自体がAIフレンドリーではない、という壁です。タスクの粒度や手順を誰も明示的に決めていないため、生成AIに何を渡せば精度よく動くかが属人化していました。 ここで必要になるのが、「作りたいもの(What)」と「作り方の設計図(How)」を分離して扱う発想です。 Whatをタスク分解の形でHowに落とし込み、それをAIに渡せば、AIはそのステップどおりに実装してくれます。そしてレビューでは、出来上がったコードよりも先に「作り方と実現方法が合っているか」を検証し、設計図のほうにフィードバックする。タスク分解の品質が、そのままアウトプットの品質を決める構造です。 このフェーズでは、AIとの関係性が「協働」から「委任」に変わります。 Vibe Codingが「AIは隣で並走するパートナー」だとすると、Agentic Workflowは「AIは自走する実行エージェントで、人間はその指揮者」になります。 任せる粒度も、1行〜1関数のレベルから、タスク/PR/フロー全体へと拡張されます。 Agentic Workflowの定義として4つの自律性を意識しています。 ゴール指向 :「何を」を与え、「どう実現するか」はAIが組み立てる 計画と分解 :大きなタスクをサブタスクに分けて順序付けて実行 ツール使用 :ファイル・Skill・コマンド・検索・MCPを能動的に使う 自己検証ループ :テスト失敗→修正→再実行を自律的に繰り返す 興味深かったのは、AI委任の前提が変わると開発環境そのものが変わったことです。 2026年に入ってから、ファインディではコード生成のメインツールがIDEからターミナルへ変化しました。 1ウインドウで1タスクずつ進めるのではなく、複数ウインドウ・ペインで同時にAIへ委任するスタイルになったため、並列委任しやすい場所として、ターミナル+tmuxのような構成に自然と寄せていく流れになっています。IDEの役割はコードを書く場から、広域に渡るコードリーディングや理解を深める場へとシフトしています。 このレベル 2を支えるのが、要件構造化&Issue自動生成のSkillです。次の6ステップで動きます。 要件理解 ─ インタラクティブな質問で曖昧さを解消 コード探索 ─ 並列の探索Agentが複数観点で同時調査 要件明確化 ─ 不足情報を補完してスコープを定義 設計提案 ─ 実装方針のドラフトを生成 タスク分解 ─ 実装単位に分解(粒度判定Skill連携) Issue作成 ─ Sub Issue/relationshipを含む構造化Issue このSkillで生成したIssueは累計3000以上にのぼり、親Issue(Feature)→子Issue(DB層→API層など)が blocked_by の依存関係付きで自動構成されます。 例えば「ユーザー通知機能の追加」という親Issueに対し、「#1 DB層:通知テーブル追加」「#2 POST API追加」「#3 DELETE API追加」のような子Issueが、依存関係込みで一気に並ぶイメージです。 実装フェーズでは、これを「Issue × Worktree × Agent」の並列モデルで走らせています。Team Lead Agentが blocked_by に従ってLayerごとにWorker Agentを起動・同期し、同じLayer内はworktreeを切ってWorker Agentが完全並列で実装する。Layer 0でDB層が完了したら、Layer 1のPOST/DELETE APIを2つのworktreeで同時に進める、といった動かし方ができます。 この並列モデルの詳細は次の記事で解説しています。 tech.findy.co.jp コードレビューの分担も、レベル 2では明示的に再定義しています。 担当 レビュー領域 AI コード規約・命名、型定義、テストコード・テストケース 人間 ビジネスロジックの要件適合、アーキテクチャや設計、データベース構造、明確なセキュリティリスク 視点はコードそのものから抽象的なところに寄せていきます。 結果として、レベル 2の導入後、1人あたりのPR作成数は前年比で1.5倍を超えました。AIフレンドリーな「設計図(タスク分解+構造化Issue)」を誰でも作れる状態になり、作りたいものを再現性高くアウトプットできるようになった、というのがその答えです。 レベル 3:AIで価値を生み出す レベル 2まで進むと、開発スピードに対する次のボトルネックが見えてきます。「何を作るか」の上流が詰まり、せっかく整えた実装力を活かしきれない状態です。 具体的には、要件の実現可能性を調査できるのがエンジニアだけになっていたり、システムとプロダクトの概念が離れていて、お互いを十分知らないまま施策や検証が進んでしまったりします。 レベル 3の目的は、要件定義(PdM領域)とQA領域というAIで代替しづらいとされてきた領域に、AIで踏み込むことです。 レベル 3の起点になるのが 現状把握 です。現状把握の対象は広い範囲に及びます。コードベース・Google Analytics・プロダクト文書・GitHub Issues/PR・Datadog・各種KPIなど、必要なコンテキストは多岐にわたります。 まず要件定義では、これらを毎回手動で集めるのは現実的ではないため、専門Agentチームが各ソースから必要な分だけ自動収集する仕組みを組みました。 ファインディの要件定義Skillでは、7つの専門Agentが並列で動きます。 目的・成果分析 ─ WHY/WHAT仮説の自律生成 データ・コンテキスト収集 ─ GitHub Issues・Notionから数値収集 プロダクト文書抽出 ─ docs/配下のKPI・ポリシーを抽出 コードベース分析 ─ リポジトリの制約・パターンを分析 スコープ分割 ─ MVPと拡張項目に分割 技術的実現可能性評価 ─ 解決アプローチの実現可能性を評価 アクセス解析データ収集 ─ Google Analyticsから自動収集 これらのAgentがAgentTeamsとして並列で稼働し、お互い会話しながら必要な情報を集めて分析します。 ユーザーは分析結果を修正・補足し、最後にAIが構造化&品質チェックしてGitHub Issueとして出力します。ユーザー操作は入力・レビュー・承認の3回のみで、それ以外はAIが自律的に進める設計です。出力されたWHY/WHAT構造化済みIssueは、そのままレベル 2のIssue自動分解Skillに連携できます。 もう1つの挑戦がQA領域です。ユーザビリティ・アクセシビリティ・UI/UXといった非機能要件のテストはAI単独では難しいため、AIで代替しづらい領域です。ファインディは「代替」ではなく「支援」、つまりAIがQAエンジニアの判断を最大化する形を仮説にしています。 次にQA領域は3つのSkillで一気通貫にしました。仕様ソース(Issue/Figma/Notion)を入力に、次の流れで進みます。 QA観点抽出:観点を自動抽出してQA観点mdを出力 QAテストケース生成:観点→ステップ/期待結果/前提条件に展開してMarkdown+CSV化 QA自動実行:Playwright MCP経由でClaude Codeから直接ブラウザを操作し、Pass/Failとスクリーンショット付きのレポートを出力 QA観点抽出では、仕様分析/画面構造・UX探索/影響範囲判定の3つのAgentが並列で動き、観点設計のたたき台を数分で生成します。 テストケース生成では、観点→具体ケースへの落とし込みにかかる工数が数時間から数分に短縮されました。生成されたQAリストは、認証・認可/入力バリデーション/表示・UI/ファイル操作/外部連携/メール送信の6軸で共通基準と照合し、観点漏れ・粒度のばらつきを検出します。 人間が集中すべきは、ユーザビリティ評価、例外シナリオ、クライアント要件の確認といった「判断が必要な領域」です。反復可能なケースはAIが淡々と実行し、失敗時はスクリーンショット付きレポートで原因特定が速くなりました。 まとめ AI活用のレベルをレベル 1(速く作る)/レベル 2(正しく作る)/レベル 3(必要なものを作る)と分けてきましたが、最大の主張は、これらは「どれか1つ」ではなく段階的に積み上げて初めて成立する、ということです。 そして、ここで強調したいのが 順番を間違えない ことです。土台が弱いと、ガードレールもAI Skillも成果を出せません。ファインディが踏んだ順番は次の4段でした。 裏返すと、AIエージェントを入れる前にやっておくべきことは、AI以前から変わっていません。統一規約、テストコード、PR粒度、レビューなどの開発文化といった 基本の徹底 こそがAI活用における大前提です。 AI時代の本丸は、「速く作る」ではなく、「正しく作る」「必要なものを作る」への段階的越境です。あなたの組織が今どのレベルにいるか、そして次のレベルへ進むためにどの土台が弱いかを確認する目安として、このロードマップが役立てば幸いです。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
はじめに こんにちは。ファインディでデータエンジニアをしている開( @hiracky16 )です。 今回はLookerに搭載されている会話分析機能を使って、ユーザーがより自律的にデータ抽出や分析ができるようにした話をします。セマンティックレイヤー(Explore)を会話分析に使用する設計や開発・テスト・評価までの運用のノウハウを紹介します。 会話分析(Conversational Analytics)は、Lookerに搭載されている機能で、自然言語で質問を投げるとエージェントが裏でクエリを組み立てて実行し、表やグラフを添えて答えてくれます。たとえば「1月から5月までのXXX数をUUベースで集計できますか?」と聞くと、次のように月別の集計結果とグラフが返ってきます。 はじめに 会話分析にセマンティックレイヤーを使う理由 セマンティックレイヤーを会話分析の入口にするメリット 参照先のデータを意味ある単位に限定できる コンテキストの肥大化を避けられる デメリット 運用 エージェント定義をGitで管理する 回答品質の評価 評価ケースを定義する 軸1: 回答テキストの品質をLLMで採点する 軸2: 発行されたクエリを決定論的に検証する フィードバックサイクルを回す 成果 まとめ 会話分析にセマンティックレイヤーを使う理由 以前公開したブログでは、Lookerを導入した背景や、登録ユーザーの7割がダッシュボードの作成・閲覧のために日々訪れていることを紹介しました。 tech.findy.co.jp tech.findy.co.jp これらの記事で触れたとおり、重要な指標や共通のディメンションは、セマンティックレイヤーへの集約を進めてきました。 集約した指標やディメンションを使ってダッシュボードを作り社内に展開していますが、「別の切り口で見たい」「集計ロジックを変えたい」といった要望に都度対応する運用となり、その工数が無視できなくなってきました。 一方で、会話ベースでデータ抽出・集計・分析ができるエージェントが世の中に広がってきました。私たちもGoogle製のADKを使って自作してみましたが、こちらもメンテナンスコストがかかります。さらに、全テーブルを対象にするため、SSoT(Single Source of Truth)が整っていない領域や、コンテキストが薄い領域では精度が出ず実用には届きませんでした。 ここで着目したのが、すでに集約を進めてきたセマンティックレイヤー(LookerのExplore)です。これを会話分析エージェントの入口に据えることで、ダッシュボード運用の工数と、自作エージェントのコンテキスト不足という両方の課題を解決できると考えました。全体像は次のとおりです。 flowchart LR subgraph DWH["データウェアハウス"] direction TB NOTE1[/"会社マスタが複数あり<br/>テーブル単位ではSSoTが曖昧"/] T1[fct_scouts<br/>送信済みスカウト] T2[fct_scout_replies<br/>スカウトへの返信] T3[fct_matches<br/>成立したマッチング] T4[dim_users<br/>ユーザー情報] C1[dim_companies_raw<br/>会社情報(raw)] C2[dim_company_master<br/>会社情報(master)] end subgraph SL["セマンティックレイヤー"] direction TB NOTE2[/"業務単位で集約し<br/>使う会社マスタもExploreで判断"/] E1["スカウトExplore<br/>送信数・返信率を定義"] E2["マッチングExplore<br/>成立件数・成立率を定義"] end T1 & T2 & T4 --> E1 T3 & T4 --> E2 C1 -->|スカウトはrawを使う| E1 C2 -->|マッチングはmasterを使う| E2 E1 --> A1[スカウト分析エージェント] E2 --> A2[マッチング分析エージェント] A1 & A2 --> U([利用者の質問]) 左側のデータウェアハウスには大量のテーブルが並び、どれを使えばよいか、指標をどう計算すればよいかはテーブル単位では自明ではありません。たとえば会社マスタが dim_companies_raw と dim_company_master のように複数存在し、用途によってどちらを使うべきかが曖昧、といったこともあります。これをそのままエージェントに渡しても、うまく答えられないのは当然です。 そこで、Explore側で「スカウト」「マッチング」といった業務の単位にテーブルを集約し、送信数や成立率といった指標の定義まで含めて整えておきます。会社マスタのように複数の候補があるものも、Explore側でどちらを参照すべきかを判断します。たとえばスカウトExploreでは dim_companies_raw を、マッチングExploreでは dim_company_master を使う、といった使い分けをExploreの定義として固定しておくわけです。エージェントは生のテーブルではなくこのExploreだけを見るので、何をどう集計すべきかが定まった状態で質問に答えられます。 セマンティックレイヤーを会話分析の入口にするメリット セマンティックレイヤーを入口に据えると、大きく2つのメリットがあります。 参照先のデータを意味ある単位に限定できる 1つのエージェントは1〜5個のExploreに紐づきます。エージェントが触れられるデータがExploreの範囲に限定されるためエージェントの役割がはっきりします。 また、SSoTが担保できていないテーブルであっても、Lookerから参照する形にすればExplore上でSSoTを表現できます。生のテーブルをそのまま渡すのではなく、Lookerというフィルタを通すことで、エージェントが扱うデータの意味を整えられます。ダッシュボードと会話分析で使用するExploreは同じなので、両者で数値が食い違うという事態も避けられます。 コンテキストの肥大化を避けられる 全テーブルをエージェントに渡そうとすると、コンテキストが膨れ上がり、かえって精度が落ちます。Exploreを入口にすれば、LookMLのdescriptionや指標定義がそのまま「正しい意味」のソースになり、必要な範囲のコンテキストだけを渡せます。 LookerはKnowledge Catalog(旧Dataplex)との連携も進んでおり、メタデータをカタログ側で一元管理できるようになってきています。 docs.cloud.google.com 今はLookMLのdescriptionにコンテキストを持たせていますが、今後はKnowledge Catalog側にも用語や定義を蓄積し、それをエージェントのコンテキストとして活かせるようになることを期待しています。 デメリット 一方で、当然ながら弱点もあります。複数のExploreをまたぐ横断分析やファネル分析は苦手です。たとえば、ファインディが主催するイベントへの参加やメディア閲覧といったイベントを起点に、実際に企業とのマッチングに至るまでの流れを追う、といった分析は守備範囲の外でした。それぞれのイベントが別々のExploreに分かれていると、エージェントはどちらか一方しか見られず、つながりを追えません。 そこで、ユーザーという意味のある単位でフェーズをまたいだデータを1行にまとめた累積ファクトを作り、Exploreとして用意することで対処しました。次のように、1行=1ユーザーで各フェーズの日付を横並びに持たせておきます。 user_key イベント参加日 メディア閲覧日 マッチング日 u_001 2026-04-01 2026-04-03 2026-04-20 u_002 2026-04-05 (なし) (なし) u_003 2026-04-10 2026-04-12 (なし) こうしておけば、1つのExploreの中で「イベント参加からマッチングまで到達した割合は?」といったファネルをたどれます。横断的な問いに答える必要があるものは、あらかじめこの形に整形しておき、エージェントからも参照できるようにしています。 運用 LookMLの開発方法は前述のブログで紹介しているので割愛し、今回はLooker会話分析に使うエージェントの開発・運用について説明します。 エージェント定義をGitで管理する 会話分析のエージェントは、Lookerの画面上からでも作成・編集できます。ただ、画面で直接編集すると、誰がいつ何を変えたのかが追えず、システムプロンプト(instructions)の変更履歴も残りません。 そこで、エージェントに必要な項目をYAMLファイルとして定義し、Gitで管理することにしました。1ファイルにエージェントのID、名前、参照するExplore(sources)、システムプロンプト(instructions)、そして後述する評価ケース(cases)をまとめています。 id : hogefugapiyo # 既存エージェントの更新用。新規作成時は省略 name : マッチング分析エージェント description : 企業と求職者のマッチング状況を分析するエージェントです。 code_interpreter : false # このエージェントが参照するExplore sources : - model : sample_recruiting explore : fct_matchings - model : sample_recruiting explore : fct_daily_snapshot_matchings # システムプロンプト instructions : | あなたはマッチング分析の専門アシスタントです。 LookerのExploreを使って、マッチングの成立件数・成立率・ セグメント別トレンドに関する質問に回答します。 ## 用語集 - マッチング : 企業と求職者の間で選考が始まった状態 - 成立率 : マッチング成立件数 ÷ いいね件数 ... # 評価ケース(評価の章で後述) cases : - prompt : "今月のセグメント別のマッチング成立率は?" reference : "セグメントでグループ化し、成立件数をいいね件数で割った値を回答する" ExploreやYAMLが編集されると、GitHub ActionsでLookerのエージェントに反映されます。 回答品質の評価 エージェントを運用に乗せると、「ちゃんと正しく答えられているか」が気になります。システムプロンプトやExploreを変更するたびに、回答の質が上がったのか下がったのかを判断したくなります。 そこで、エージェント定義に評価ケースを書いておき、CIで自動評価する仕組みを用意しました。 評価ケースを定義する 評価ケースは、前述したエージェント定義のYAMLに質問(prompt)と期待する答え方(reference)の組として書いておきます。たとえば次のように、想定される質問のバリエーションを並べておきます。 cases : - prompt : "今月のマッチング成立件数は?" reference : "当月のマッチング成立件数の合計を回答する" - prompt : "セグメント別のマッチング成立率を高い順に教えて" reference : "セグメントでグループ化し、成立件数をいいね件数で割った成立率を降順で回答する" - prompt : "先月と今月でマッチング成立件数はどう変わった?" reference : "先月と当月の成立件数を比較し、増減と差分を回答する" referenceには「完全一致の正解テキスト」ではなく、答えの作り方の説明を書いています。会話分析の回答は表現に幅があるため、文章の一致ではなく方向性で評価したいからです。 この評価ケースを使って、次の2軸で品質を測ります。 軸1: 回答テキストの品質をLLMで採点する 1つ目は、エージェントが返した回答テキストそのものの質です。これは Agent Platform(Vertex AI)の評価サービス を使い、LLM-as-a-Judge、つまり別のLLMに採点役をやってもらう形で測っています。 採点は4つの観点で行われます。 質問の指示どおりに答えているか(Instruction following) 与えられたコンテキストに基づいているか(Groundedness) 十分な情報量で答えているか(Completeness) 読みやすく整理されているか(Fluent) これらをまとめたスコアを段階評価で受け取り、品質が落ちていないかを確認します。 軸2: 発行されたクエリを決定論的に検証する LLMによる採点は揺れるため、それだけに頼るのは不安が残ります。そこでエージェントが実際に発行したクエリの中身を機械的に検証しています。 会話分析エージェントは、質問を受けると裏でLookerのクエリを組み立てて実行します。このクエリが「どのExploreを使い、どのフィールドを選び、どんなフィルタをかけたか」を期待値と照合して判定します。回答テキストの良し悪しではなく、構造として正しいクエリを組めているかを、ぶれのない形で担保します。 期待値は、評価ケースに assertions として書き足しておきます。 cases : - prompt : "今月のセグメント別のマッチング成立率は?" reference : "セグメントでグループ化し、成立件数をいいね件数で割った成立率を回答する" assertions : explore : fct_matchings # このExploreを使っているか fields_include : # これらのフィールドが含まれているか - segment - matching_rate filters_include : # このフィルタがかかっているか - field : matched_month value : this month これらの評価はGitHub Actionsで自動実行され、結果はPRにコメントされます。QA Qualityの平均スコアやケースごとの内訳、アサーションの判定がひと目で分かるので、変更が品質に与えた影響をレビュー時に確認できます。 フィードバックサイクルを回す 評価ケースは社内メンバーから寄せられるフィードバックをもとに育てています。「この聞き方だと答えてくれない」「この数字が期待とずれている」といった、実際に使う人がぶつかった声ほど価値のあるテストケースになります。 寄せられた声は評価ケースに加えるだけでなく、エージェントに足りないコンテキストの手がかりとして、内容に応じてシステムプロンプト・LookML・BigQueryテーブルのどこに手を入れるかを判断します。こうしてフィードバックを起点にしたサイクルが回っています。 flowchart LR FB([社内メンバーのFB]) --> C[評価ケースに追加] C --> FIX{どこを直す?} FIX -->|答え方| P[システムプロンプト調整] FIX -->|定義不足| D[LookML description補強] FIX -->|データ構造の問題| M[dbtデータモデリング見直し] P & D & M --> EVAL[評価を実行] EVAL --> SCORE[スコアを比較] SCORE -.改善を確認.-> FB 成果 前回のブログで、Lookerの登録ユーザーの7割が週次でログインしていることをお伝えしました。いまではそのうちの半分が、会話分析を通じて自律的にデータ抽出や分析を行っています。 データチームへの影響も大きく、月に十数件あったデータ抽出の依頼はほぼなくなりました。代わりに届くのは、会話分析だけでは解けないより高度な分析を要する相談が中心です。単純な抽出作業から解放されて本来注力すべき仕事に時間を割けるようになりました。 利用者からのフィードバックも増えてきました。「便利になった」という好印象の声もあれば、期待した結果が得られなかったという指摘もあります。後者はチームにとって学びの多い情報で、どこにコンテキストが足りていないのかが明確になります。 このフィードバックサイクルが回り始めたことで、エージェントのinstructionsやLookMLの修正、データモデリングの見直しがこれまで以上に進むようになりました。前述したSSoTについても、Exploreで吸収するだけでなく、テーブルレベルで担保できるよう改善を進めています。 まとめ 本記事では、セマンティックレイヤー(LookerのExplore)を会話分析の入口に据える設計と、Git管理・2軸評価・改善サイクルまでの運用を紹介しました。この設計が効くのは、すでにLookMLで指標やディメンションの定義が進んでいる組織です。土台があるからこそすぐに始められ、成果につなげることができたと思います。 運用で意識しているのは、クイックウィンの積み重ねです。Lookerや生成AIは安価ではないため、小さな成功体験を積み上げ、利用者に定着してもらうことが重要です。そのためにはフィードバックを集める仕組みが重要で、社内勉強会で使い方を共有したり、要望を気軽に投げてもらえるチャンネルを用意したりしています。会話分析を実現させるためには技術的な仕組みと、声が集まる仕組みの両輪が必要だと感じています。 ファインディでは、データ基盤やデータ活用の仕組みづくりに一緒に取り組んでくれる仲間を募集しています。少しでも興味を持っていただけたら、まずはカジュアルにお話ししましょう。 herp.careers
こんにちは。ファインディ株式会社でアプリケーションエンジニアをしている西村です。 ファインディの開発組織ではここ1年ほど、Claude Codeを使った開発プロセスのSkill化を進めてきました。Issue生成やセルフレビュー、タスク分解といった作業をSkillにして、社内のClaude Code Pluginに追加するのが日常になっています。 ただ、便利なSkillを揃えて配っただけでは、それが開発フローの中でどれだけ使われ、成果につながっているかまではわかりません。 そこで今回は、開発組織内で配布したPluginやSkillの利用データをどう集め、どう見て、どう改善に回しているか、その考え方と運用を紹介します。 作成したSkillが使われ続けるかを見る 定着と成果を組み合わせて評価する 利用データから使われ方そのものを変える 定着と成果のデータをFindy AI+で可視化する まとめ 作成したSkillが使われ続けるかを見る 開発プロセスのSkill化については、これまでのテックブログでも何度か紹介してきました。 【Claude】Pluginsで簡単横展開 - 開発手法の標準化 - Findyの爆速開発を支えるAI×チェックリスト型セルフレビュー Findyの爆速開発を支えるAIによるタスク分解の粒度設計 繰り返す作業があればSkillとして作成し、社内のClaude Code Pluginへ追加する流れは今も続いています。 一方で、配ったSkillが日々の開発に定着するかどうかは別の話です。配布した当初は新しい取り組みに前向きなメンバーが使ってくれますが、その後も使い続けるとは限りません。Skillの数と種類は充実しているのに、実際に利用されているのは一部だけ、という状況が生まれます。 「作った」で満足せず、配ったあとも「使われているか」を継続的に見ていく必要があります。 定着と成果を組み合わせて評価する ファインディでは、Claude Codeの活用度や作成したSkillの利用状況を計測して可視化しています。集めたデータから、開発工程のどこでどのSkillが効いているかを見て、改善しつづけています。 データを見てみると、よく使われていても成果につながっていないSkillもあれば、利用回数は少なくても効きどころで使われて大きな効果を出しているSkillもあります。つまり、Skillの利用回数だけを成果とみなすと、この違いを見落とすことがわかりました。 そこで私たちは、Skillが定着しているかを測る指標と、成果への貢献を測る指標を組み合わせて評価することにしました。 定着を測る指標は、一定期間内にSkillがどれくらいの頻度で使われているか、つまり利用頻度です。 成果への貢献を測る指標は、Skillを使い始めた後にPRの作成からマージまでの時間といったリードタイム等が短縮したかです。 利用データから使われ方そのものを変える ファインディでは2026年3月に、セルフレビューSkillを導入しました( 導入の経緯はこちらの記事で紹介しています )。ただ作成するだけでなく、ドッグフーディングして、AgentTeamに対応させたり、テストのカバレッジが十分かを見る観点を加えたりと、Skillの中身を改善してきました。 セルフレビューSkillは、変更したコードのバグを見つけたりテストの網羅性を高めたりする効果があり、組織全体での定着率を上げていきたいSkillです。 ただ、利用データを見て気づいたのは、改善すべきは中身だけではないということでした。導入直後の3月時点で月298回と一定の利用はあったものの、セルフレビューSkillを呼ぶかどうかは各メンバーの判断に委ねていました。そのため、エンジニア全員の開発フローに浸透するほどには使われていませんでした。 2026年3月時点のSkillの利用回数(左:アウトプット上位5名、右:組織全体96名)です。セルフレビューSkillの呼び出しは、アウトプット上位で117回、組織全体でも298回でした。 アウトプット上位 組織全体 そこで、Skillの使われ方そのものを変えました。SlackのPluginリリース告知チャンネルで使い方を周知し、あわせてPR作成Skillの中からセルフレビューSkillを呼び出すようにして、PRを作るときには必ずセルフレビューSkillを使用する環境に変えました。 その結果、セルフレビューSkillの呼び出しは、アウトプット上位で185回、組織全体で479回まで増えました。セルフレビューSkillが組織全体の標準的な動作として定着したことがわかります。 アウトプット上位 組織全体 さらに、導入前後でリードタイムを比べると、コミットからオープンまでの平均時間は22.2時間から16.3時間に縮みました。AIが書いたコードを人間がセルフレビューしていた手間を減らせたことが、短縮の一因だと考えています。 定着と成果のデータをFindy AI+で可視化する これまで紹介してきたSkillの呼び出し回数データは、Findy AI+の分析機能を使っています。Findy AI+ではClaude CodeのMonitoring機能を活用し、Skillやコマンドの実行ログを収集して、「どのSkillがいつ実行されたか」を一元的に追えるようにしています。 集めたログからは、Skillごとの呼び出し回数や、それが一部のメンバーに偏っているか組織全体に広がっているかといった定着の度合いが見えてきます。ここにTeam+のPR作成数やレビューのリードタイムといった成果指標を重ねると、定着と成果のデータが揃い、「どのSkillで、どの指標に効いているのか」を分析できます。 まとめ PluginやSkillは、配って終わりにすると少しずつ使われなくなっていきます。配ったあとも「使われているか」さらに「アウトプットの増加に貢献できているか」まで見て初めて、改善の打ち手が定まります。 もしすでにPluginやSkillを配っている方がいれば、まずは「どのSkillが、誰に、どれだけ使われているか」を1つでも数字で見えるようにするところから始めてみてください。配ったあとのデータがそろうと、次にどこへ手を入れるべきかが自然と見えてきます。 Findy AI+の分析機能については、 Findy AI+の紹介ページ もあわせてご覧ください。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
こんにちは。Findy Freelance開発チームの久木田です。 今回は、社内で運用している支払明細書PDFの生成基盤を、Lambda + Puppeteerから @react-pdf/renderer へ全面的に移行した話を書きます。最終的に処理時間はP50(中央値)で約27倍速くなり、メモリ消費も実測で約1/4まで落とせました。 これまでのPDF生成基盤と課題 対症療法でしのいだ期間 根本対応を決めた3つの背景 技術選定: 戻れる順に試す 移行で押さえておきたい実装ポイント 設計と実装のキモ テンプレート構造 esbuildで橋渡し どう変わったか 学び これまでのPDF生成基盤と課題 現在、システムから発行しているPDFはいくつかありますが、本記事では一例として支払明細書PDFに絞って紹介します。ファインディからフリーランスエンジニアへの支払明細として月次で一括発行しているPDFです。 支払明細書PDFは1件あたり1ページで、支払先、支払者、明細表、特記事項、ファインディの社印を配置した構成です。情報量としては小規模ですが、月次でフリーランスエンジニア全員分を一括発行する必要があり、1回のバッチで数百件規模のPDFを並列生成していました。Rails側からParallelライブラリで並列にLambdaを呼び出す構成です。 このPDFは、AWS Lambda上でPuppeteerを起動し、EJSテンプレートから組み上げたHTMLをヘッドレスのChromiumでレンダリングしてPDF化する構成で動いていました。フロントエンド向けのHTML/CSSをそのまま流用できるため初期実装は速かったのですが、運用が進むにつれ次の課題が顕在化しました。 コールドスタートが重く、Chromiumバイナリの起動だけで5秒前後を消費していた 大量生成で /tmp が枯渇し、一括処理で複数のレンダリングを並走させると、エフェメラルストレージが先に尽きてエラー終了する タイムアウトやエラーが恒常化し、数百件規模の一括出力は途中で停止して再実行が必要になる ボトルネックはChromiumの不安定さでした。Chromium起動のたびにプロファイル・キャッシュ・ソケットファイルが /tmp 配下に生成され、 browser.close() 後もディスク上に残存します。さらに、レンダリング中にChromiumがハングした場合は、一時ファイルを保持したままプロセスが停止します。その間にも新規リクエストでLambdaが起動するため、ウォームスタートで使い回されるインスタンスでは /tmp の残存ファイルが蓄積し、一定量を超えた時点で全体が停止してしまう挙動になっていました。 この蓄積に備えて、メモリと /tmp は常にバッファを含めて多めに割り当てる必要があり、1Lambdaあたり2-3GB相当の確保を継続していました。それでもハングと並列度のピークが重なる局面では詰まるため、設定値を引き上げては別の閾値で詰まるという状態が続いていました。 一括出力は今後数倍に増加する見込みであり、現状の構成のまま継続することは現実的ではありませんでした。 対症療法でしのいだ期間 課題を踏まえ、徐々に対策を施すことにしました。Lambdaのメモリ割り当て増、タイムアウト延長、エフェメラルストレージの拡張、並列度の調整など、設定値で吸収できそうな対策は一通りしました。短期的な改善にはなったものの、根本にあるのはChromiumをサーバーレス環境で動かすこと自体のコストと描画コストが入力規模に比例して伸びる構造です。設定の積み増しではレイテンシーもエラー率も思うように改善しませんでした。 根本対応を決めた3つの背景 根本対応に踏み切る判断は、次の3点が同時に揃った時点で下しました。 現状の課題: レイテンシーが悪化傾向、エラーも日次で観測される 将来の悪化見込み: 一括処理の件数は今後さらに増える計画があり、対症療法の余地がもう残っていない 対症療法の限界: 設定値の調整ではレイテンシーやエラー率の改善が頭打ちで、いずれの打ち手も効果が薄れてきた 課題が大きくなったため、対策を施しました。逆に言えば、どれか1つでも欠けていたら、もう少し対症療法を続けていたと思います。 技術選定: 戻れる順に試す 根本対応の方針として、大きく2案を比較しました。 A案: Lambdaを維持し、 @react-pdf/renderer でProgrammaticにPDFを組み立てる B案: LambdaからECSなど別のランタイムへ移行する A案は、いまのLambda構成を保ったままPDF生成方式だけを差し替える案です。 @react-pdf/renderer はJSXを書く感覚でPDFを組み立てるライブラリで、Chromiumのヘッドレスブラウザは利用しません。 react-pdf.org そもそも @react-pdf/renderer が候補に挙がったのは、ヘッドレスブラウザ以外でPDFを生成する方法を調べていたからです。継続的な利用料金が発生する外部サービスを使う選択肢は外し、OSSのプログラマティック生成ライブラリの中で、Reactと同じJSXで書ける @react-pdf/renderer を選びました。Reactはファインディの他プロダクトでも広く使われており、馴染みがあった点も決め手になりました。 B案はメモリやストレージの制約には強くなりますが、コスト構造が変わり、検証の立ち上げにも工数がかかります。Lambdaのタイムアウトに収まらない処理や、Chromiumの表現力(複雑なCSS・JavaScript描画など)をどうしても残したいケースではB案が候補ですが、今回のPDF生成はそのどちらにも当てはまりませんでした。 そのため、最終的には次の3つの理由からA案を選びました。 戻れる: Lambda構成のままPDF生成方式だけを差し替えるので、問題が出てもPuppeteer版に戻すことが容易 テスト可能: PDF生成ロジックがJSXで書けるため、単体テストや出力差分テストを書きやすい AIで移行コストが現実的になった: EJSテンプレートからJSXへの変換は、生成AIに任せられるレベルまで来ていた B案はA案が失敗した場合の代替案として残し、まずは戻れるA案を試すことにしました。 移行で押さえておきたい実装ポイント EJSからJSXへの書き換え自体は生成AIで一気に進められましたが、 @react-pdf/renderer の実装スタイルに合わせるために事前に押さえておきたい点がいくつかありました。 @react-pdf/renderer v4はESM-onlyで、tscのCommonJS出力からは読み込めないため、esbuildを入れてESMをCJSにバンドルした 日本語フォントは Font.register() で明示的に登録しないと文字化けする Puppeteerの scale: 0.8 相当が無いため、フォントサイズや余白を手で再計算した HTMLの <a> 自動展開は再現されず、URL部分だけ <Link> 化する小さなコンポーネントを自作した HTMLエスケープは自動化されていて、旧実装のエスケープ処理が不要になった(副産物) 特に大変だったのがJSXの空文字列の扱いです。 {stringValue && (...)} と書くと、空文字列がchildとしてそのまま流れ込み、 WARN Invalid '' string child outside <Text> component が大量に出ます。Reactの文法としては正しいのですが、 @react-pdf/renderer の <View> / <Page> 配下では {!!stringValue && (...)} と明示的にboolean化する書き方に揃える必要があります。さまざまなデータでPDFを作成していく過程で警告ログが出ていることに気付き、該当箇所をまとめて修正しました。 設計と実装のキモ ここからは、 @react-pdf/renderer をLambdaに載せていくときに考えた設計面のポイントを2つに分けてまとめます。 テンプレート構造 PDFそのものを1つのReactコンポーネントとして組み立てる構成にしました。リンクの自動展開のように共通で必要な要素は、専用の小さなコンポーネントとして切り出して再利用しています。 EJS時代は部分テンプレートをincludeで組み合わせる作りでしたが、JSXに移ってからはコンポーネントの組み合わせとして自然に再構成できました。 esbuildで橋渡し @react-pdf/renderer v4はESM-onlyですが、Lambda側はCommonJSで動かしています。tscの出力では直接読み込めなかったため、esbuildでESM→CJSのバンドルを作ってLambdaにデプロイする構成にしました。 // esbuild.config.js(抜粋) { entryPoints: ['src/index.ts'], bundle: true, platform: 'node', format: 'cjs', } 設定としてはシンプルですが、ここを通さないと依存関係の解決でつまずくことになるため注意が必要です。 どう変わったか 支払明細書PDFの一括作成処理について、移行前後をCloudWatchメトリクスで比較した実測値が次の通りです。表のP50 / P95 / P99は、実行時間を昇順に並べたときの中央値 / 95パーセンタイル / 99パーセンタイルを表します。 指標 Before After 倍率 実行時間 P50 約3,963 ms 約145 ms 約27倍高速 実行時間 P95 約4,707 ms 約212 ms 約22倍高速 実行時間 P99 約5,249 ms 約458 ms 約11倍高速 平均メモリ 約912 MB 約222 MB 約1/4 最大メモリ 約1,589 MB 約239 MB 約1/7 特に改善幅が大きいのはP50です。旧構成では実行時間そのものの遅さに加え、Puppeteer/Chromium由来のエラー(ブラウザの接続切れやハングなど)が起きると、一括処理の中で個別のPDF生成がLambdaのタイムアウトに到達し、リトライしても最後まで完成せずエラーとして残るケースがありました。表のP99の大きさにそれが現れています。移行後はこれらのエラー、エフェメラルストレージの逼迫、コールドスタートによる遅延がいずれも解消され、Lambda上での実行を意識する必要のない構成になっています。 学び 今回の移行で特に有効だったのは、判断軸として置いた「戻れる順に試す」です。Lambda構成を維持したままPDF生成方式だけを差し替えるA案は、もし行き詰まっても旧版のLambdaに切り戻す選択肢を残せました。ランタイムごと載せ替えるB案を最初に選んでいたら、検証のために抱えるリスクははるかに大きくなっていたはずです。 もうひとつは、生成AIの活用で技術選定の前提条件が変わったことです。A案はEJSテンプレートのJSXへの全件書き換えを伴います。AIなしで工数を見積もると、規模だけでA案は採用候補から外れていました。書き換えだけでなく、旧PDFと新PDFのレイアウト差分の特定と修正案の生成までAIに任せられたため、A案の工数は当初の想定より低く収まりました。 最後に、 @react-pdf/renderer を使った所感をまとめます。 メリットとして大きかったのは、Lambdaの割り当てメモリを大幅に減らせたことと、ヘッドレスブラウザを使っていたころよりテストが格段に書きやすくなったことです。PDFをバッファのまま受け取って中身を検証できるので、ブラウザを起動しない軽量な統合テストをCI上でも組めるようになりました。 一方で、HTML/CSSではなく <View> <Text> といったPDF専用のプリミティブをFlexboxで組み立てる、React Native寄りのコンポーネントモデルです。HTML/CSSしか経験が無い場合は、最初は書き方に戸惑う場面もあると思います。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers