
C言語
イベント
マガジン
技術ブログ
みなさんこんにちは。ソリューションアーキテクトの大村です。 AWS Japanでは、AIを使ってアプリケーションのモダナイゼーションを進める取り組みを「AI-Driven Modernization」と呼び、お客様やパートナーの皆様と一緒に進めています。このブログでは、AI-Driven Modernizationの考え方と、その中核となるツールとしてAWS Samplesに公開した「AI Modernization Flow(AIMF)」をご紹介します。 AI Modernization Flowは、古い技術スタックで動いているシステムの移行作業を、KiroやClaude CodeといったAIエージェントに決められた手順で進めさせるためのワークフローです。 GitHubで公開しており( https://github.com/aws-samples/sample-ai-modernization-flow )、どなたでも手元のコードで試せます。 AI-Driven Modernizationとは モダナイゼーションという言葉が指す範囲は広く、インフラのクラウド移行を指すこともあれば、業務の見直しを指すこともあります。私たちはAI-Driven Modernizationを 「従来は過大な工数やコストで断念していたアプリケーションのモダナイゼーションをAIによって実現すること」 と定義しています。 10年、20年と稼働してきた業務システムでは、言語ランタイムやフレームワーク、ライブラリが古くなっていきます。サポートが終了した技術スタックを使い続けると、保守できる人材の確保が難しくなり、変更のたびに時間とコストが膨らみます。それでもアプリケーションの改修を伴う移行は大がかりな開発プロジェクトになるため、多くのシステムで後回しにされてきました。 この前提が、AIによって変わりつつあります。人が数ヶ月かけていたコード解析も、AIなら数時間です。存在しない、あるいは古くなったドキュメントやテストも、AIがコードから作れます。これまで費用対効果が合わずに見送ってきた移行も、AIを使えば現実的な選択肢になります。 2つのアプローチ:RefactorとReimagine AI-Driven Modernizationでは、移行のゴールに応じて2つのアプローチを使い分けます。 Refactor(技術スタックの最新化):ビジネスロジックは変えずに、言語ランタイムやフレームワーク、ライブラリを新しくします。AIエージェントが既存コードを分析し、変換と検証を進めます。 Reimagine(再構築):AIエージェントが既存コードから仕様書を起こし、その仕様をもとに新しいアーキテクチャで作り直します。 どちらの場合も、移行後に業務が同じように動くことを確かめる受け入れテストが欠かせません。この受け入れテストの自動化にもAIを使います。 今回公開したAI Modernization Flow は、このうちRefactorを担うツールです。 AIに「移行して」と頼むだけでは何が足りないのか コーディングエージェントに「このアプリをJava 21に移行して」と頼めば、それらしいコードは返ってきます。しかし、私たちがお客様と一緒に試す中で、ビルドもテストも通ったのに一部の機能が動かない、AIが移行先で機能を勝手に削る、なぜこの実装にしたのか後から誰も説明できない、といった問題を繰り返し見てきました。作業の品質が担当者の移行スキルに大きく左右されることも分かりました。 なかでも見つけにくいのが、エラーを出さずに機能が失われる不具合(サイレント障害)です。たとえばWebフレームワークのStrutsを7系に上げたところ、新しく既定で有効になったセキュリティ設定のために、画面に表示されるはずの値が空になりました。例外は出ず、HTTPのステータスは200で、単体テストもすべて成功していましたが、実際は正しい実装を行えておらず、この検出には人による画面操作や自動E2Eテストの追加が必要でした。 このように、AIを活用した移行プロジェクトを成功させるには、AIに変換させる技術に加えて、移行プロジェクトの進め方を知っていることが必要です。両方の知見を持つ人はまだ多くありません。AI Modernization Flowは、移行プロジェクトの進め方とAIに守らせる作業規律を「型」としてAIエージェントに与え、使う人のスキルに左右されずに一定の品質で移行作業を進められるようにします。 AI Modernization Flow (AIMF) の特徴 分析から最終確認までをAIがガイドする AIMFは、移行作業を6つの段階(Phase)で進めます。 Phase 0a 分析 現行コードから構成を把握し、技術的負債を分析する Phase 0b 移行判断の材料 移行先の候補と規模・リスクをまとめ、移行方針を決める Phase 1 準備 現新比較の方式を決定し、非機能要件の検討範囲を決める Phase 2 計画 作業計画を作り、成功基準を確定する Phase 3 実施 作業計画に従って段階的に移行し、段階ごとに検証する Phase 4 最終確認 全機能を検証し、別の視点からも確かめる 開始時に必要な情報は、対象のソースコードの場所だけです。移行先の技術スタックは、事前に指定することも、現行システムを分析した結果を見てから決めることもできます。Phase 0aの分析にはAWS Transform customを使います。複数のサブシステムのリポジトリを並列に分析した場合は Phase 0b で結果を統合することが可能です。 作業は途中で止めることもできます。Phase 0bで止めれば、移行先の候補と比較、概算規模、主要なリスク、移行方針の案が得られます。まずアセスメントだけを行い、移行に着手するかどうかや予算を判断する、という使い方ができます。 人が判断すべき場面で止まる AIの判断をすべて人がレビューすると、承認待ちばかりで作業が進みません。AIMFは、後戻りにかかるコストに応じて判断を3段階に分けています。ユニット内部の実装は、検証が通ればAIがそのまま進めます。インターフェースやデータ形式の変更、機能の除外、Phaseの境界では、AIが作業を止めて選択肢と影響を示し、人の判断を求めます。止まるかどうかは、AI自身ではなく、あらかじめ決めた条件で判定します。 人に意思決定を求めるのは、ドメイン知識を伝承するための仕組みでもあります。移行をすべてAIに任せると、移行の過程で今後の保守担当者に業務の知識を引き継ぐ機会も失われます。既存システムに詳しい担当者と新しい技術スタックに詳しいエンジニアが一緒に判断し、その理由をADRに残すことで、知識は移行後の保守へ引き継がれます。 作業完了は検証スクリプトによるテストで判定する 各工程の完了は、実際の動作の結果を確認して判定します。例えばビルドが成功しただけでは完了とせず、アプリケーションを動かして自動テストによって動作を確かめた時点で完了とします。 文章で書いたルールは、AIが「守りました」と自己申告するだけで通ってしまうことがあります。そこでAIMFは、守らせたい規律をできるだけ検証スクリプトに置き換えています。先ほど例に出したサイレント障害に対しては、手を入れるべき箇所を台帳にまとめて未対応をゼロにすることと、利用者の操作の流れをブラウザで再現するテストを、完了の条件にしています。 判断と知見と作業内容を記録に残す AIMFは、設計判断をADR(Architecture Decision Record)として記録します。「なぜこの技術を選んだか」「なぜこの機能を対象外にしたか」を後から確認できます。作業中に得られた知見をプラクティスとして記録し、以後の作業に活用します。作業内容の記録はworklogに、現在の作業状態はsession-contextに記録します。担当者が交代しても、日をまたいでも、またエージェントが途中で終了してしまった場合でも作業を再開できます。 3層の構成 AIMFは、移行の進め方を定めるMethod、移行ドメインごとの知見をまとめたPlaybook、AIに守らせる作業規律を実装したHarnessの3層でできています。Playbookは現在、次の3つを用意しています。 Javaモダナイゼーション(Java 8/11、javax名前空間からJava 21、Jakarta EEへ) .NET Frameworkからモダン .NETへ C言語のSolaris x86からAmazon Linuxへの移植 新しい移行ドメインに使う場合は、MethodとHarnessはそのままで、Playbookだけを追加します。お客様の開発標準や過去の移行で得た知見をPlaybookに加えていくほど、次の移行の質が上がります。 実際の移行記録から AIMFを使うと、移行したコードとは別に、何を判断し、何を検証したかの記録が残ります。OSSのアプリケーションを移行した例を紹介します。 Apache Roller(Java) Apache Roller は、Javaで書かれたオープンソースのブログ用CMSです。Java 11、Java EE(javax)、Struts 2.5、Spring 5で構成された約15万行のアプリケーションを、Java 21、Jakarta EE 10、Struts 7、Spring 6、PostgreSQL 17の構成へ移行しました。分析から最終確認までにかかったのは、人とのやり取りを含めて4日間でした。作業時間は日中のみです。 移行の終わりに、AIは画像にあるような移行完了報告を作成します。表はその一部で、作業計画で決めた成功基準ごとに、何で確かめたかが書かれています。 成功基準 確認方法と結果 既存の単体テスト 157件成功。OAuth 1.0の除去に伴い1件を正当に減らした javaxからjakartaへの全面移行 検証スクリプトで残存0件を確認 主要画面の動作 ブラウザ操作のE2Eテストで、ユーザー登録、ログイン、ブログ作成、記事投稿、画像のアップロード、コメントまでを確認 セキュリティ既定値によるサイレント障害がないこと 実際に見つかった複数の障害を修正し、再発を検出する検証スクリプトを追加 性能・リソースリーク・耐障害性 移行後の独立したタスクとして延期(人が判断し、ADRに記録) 報告には、完了した項目だけでなく、対応を保留した項目とその理由も書かれています。この場合は、メンテナンスが終了したXML-RPCやOpenIDといった古いプロトコルについては、AIが選択肢と影響を示し、人が「恒久的に除去する」と判断した経緯がADRに残りました。 これらの移行完了報告やADRは、お客様やプロジェクトの関係者に「何を判断し、どこまで確かめたか」を説明する材料として使えます。 AIMFでできること・できないこと AIMFが向いているのは、ビジネスロジックを変えずに古くなった技術スタックを更新する移行(Refactor)です。既存コードを使わずに新しい言語やアーキテクチャで作り直す場合は、既存コードから仕様を生成して再開発を行うReimagineのアプローチが適しています。 Refactor型アプローチでは基本的にビジネスロジックを変更しないため、従来と同様のテストを行うことで品質を確認します。一方で、従来のテストが手動であった場合、移行作業自体がAIによって迅速に行えても、テストに多大な時間や工数を要してしまい、その効果が限定的になる場合があります。この課題を解決する方法としてWeb画面を操作する受入れテストを、Playwrightで自動化することが考えられます。 Reimagineの際のAIによる仕様生成、受け入れテストの自動化 (UAT Automation) についてはAWS担当営業へご相談ください。 現在のAIMFは、アプリケーション自体の移行を対象にしています。アプリケーションを稼働させるクラウド環境の設計と構築は、AIMFの対象外です。 ただし、クラウドへ移すときには、アプリケーション側の修正が必要になることがあります。たとえば、オンプレミスで動いていたWebアプリケーションをAmazon CloudFrontやApplication Load Balancerの配下で動かす場合、アプリケーションが出力するURLのパスや、セッション管理の仕組みを見直す必要があるかもしれません。こうした修正はAIMFのフローに明示されていませんが、アプリケーションの移行が完了した後に、クラウド環境に合わせた設計と修正をAIエージェントに指示すれば進められます。 性能試験や障害試験を行うかどうかは、Phase1で決める非機能要件の範囲によって変わります。性能や耐障害性を対象にすれば、その検証もAIMFの完了条件に加わります。ただし、本番に近い条件で性能や耐障害性を確かめるには、移行先のインフラ構成が決まっている必要があります。そのため、インフラの設計と合わせて、アプリケーション移行後のフェーズで計画するのがお勧めです。 AIMFは、使えば最適なモダナイゼーションが自動で完成する魔法の道具ではありません。AIの推奨に従えば動くものはできますが、その技術スタックがお客様のシステムにとって適切かどうか、その後もメンテナンスし続けられるかどうかは、アプリケーション開発の知識を持つ人が判断する必要があります。モダナイゼーションを実際に進めるには、プロジェクトとしての体制と予算も必要です。お客様の内製チーム、あるいはSIパートナーと一緒に進める体制を前提としています。 よくいただく質問 Q. オンプレミスで動いているシステムにも使えますか。 A. 使えます。AIMFはソースコードを分析して移行するため、現在の稼働環境は問いません。 Q. 社内の開発標準やコーディング規約を守らせられますか。 A. 守らせられます。規約や命名規則をMarkdownのファイルとして所定の場所に置くと、AIが参照しながら作業します。確実にルールを守らせるにはLint等を併用するよう指示してください。 Q. 移行後の品質を、お客様や社内にどう説明すればよいですか。 A. AIMFの作業内容については、移行完了報告、ADR、検証結果などが説明の材料になります。しかし移行したシステムの品質は、適切なテストを行うことでしか保証はできません。AIMFの中では実装にあたってのベースラインとして現行システムと新システムの動作比較(現新比較)テストの方針を確認し、その範囲でテストを計画・実施して作業を行います。それ以外の必要なテストについてはAIMFとは別に検討、実施が必要です。 Q. すぐに移行する予定はありませんが、何から始めればよいですか。 A. Phase 0のアセスメントのみの実行から始めるのがお勧めです。内部で使用しているAWS Transform custom の comprehensive code analysisによる分析は数十万行のコードでも1-2時間程度で結果が出ます。現行システムの技術的負債と移行の規模感が分かり、移行計画や予算を検討する材料になります。 利用方法 現時点で対応しているコーディングエージェントはKiroとClaude Codeです。ワークスペース(移行対象のソースコードを置くディレクトリの親)で次のコマンドを実行すると、AIエージェント用のルールと作業記録用のリポジトリが準備されます。 cd /path/to/workspace git clone https://github.com/aws-samples/sample-ai-modernization-flow.git ./sample-ai-modernization-flow/install.sh --project myapp-1.0 ¥ --playbook java-modernization --lang ja あとはワークスペースでAIエージェントを起動し、ソースコードの場所を伝えて「Phase 0aからアセスメントを開始して」と指示します。プロジェクトの情報はAIが起動時のインタビューとソースコードの読み取りで埋めるので、設定ファイルを手で書く必要はありません。 ドキュメントは読者に合わせて用意しています。AIMFの考え方を知りたい方は「 基本コンセプト 」、全体像を知りたい方は「 かんたん解説 」から、実際に使う方は「 使い方 」からお読みください。 AWSと一緒に AI-Driven Modernization を始めましょう このブログでは、AWS Japanが取り組むAI-Driven Modernizationと、技術スタックの更新を行うツールとしてAI Modernization Flowをご紹介しました。 AI Modernization Flow を実際に使うと、AIを使ったモダナイゼーションの進め方を具体的に体験できます。まずお手元のコードのアセスメントや移行方針の検討にご利用ください。そして一度AIMFを使って移行作業を行ってみてください。AIを使えばモダナイゼーションの作業は多大な時間と工数を要する一本道の作業ではなくなります。一度移行作業を試して知見をPlaybookに溜めた後、もう一度同じコードに対して移行作業を行っても良いのです。そうして得られた知見をもとに、本格的なPoCや、移行計画の立案に取り組んでみてください。 AWS Japanでは、AI-Driven Modernizationを進めるためのご相談を受け付けています。Refactor型モダナイゼーションではAIMFを使ったアセスメントおよびPoCを、そしてReimagineや受け入れテストの自動化についてもご相談いただけます。 保守期限が迫っているシステムがある、移行したいが規模が読めない、AIでどこまでできるのか試してみたい、といった方は、ぜひAWSの担当営業またはソリューションアーキテクトにご相談ください。SIパートナーの皆様からのご相談も歓迎します。 What’s next AIMFはお客様からのフィードバックをもとに、Playbookの拡充やフローの改善を続けていきます。AIモデルの進化に合わせてフローも変わっていくため、利用する際はGitHubで最新の安定版をご確認ください。 ぜひ手元のコードで試してみてください。GitHubのIssue、またはAWSの担当者までフィードバックをお寄せください。 著者:大村 幸敬(AWS Japan, Manager, Specialist Solutions Architect)
はじめに ITD2-2-3のI.Hです。今回、RubyKaigi 2026 in 函館に参加する機会をいただきました。 RubyKaigiは、Ruby本体やエコシステムに関する最新の知見が集まる技術カンファレンスです。 今回の参加を通して、Rubyそのものについて学べたのはもちろん、他社エンジニアとの交流やスポンサーイベントでのLT登壇など、普段の業務だけでは得られない多くの経験をすることができました。 この記事では、Rubyに関する知見の共有だけでなく、技術イベントに参加すること自体の意味や価値についてもあわせてまとめました。 Day0(イベント前日) 函館に前日入り 場所が北海道の函館と言うことで、当日出発では間に合わないため前日の夜に出発しました。 空港に到着すると、空港の至る所にRubyKaigiの看板や垂れ幕がありました。 また、路面電車の車内外にもRubyKaigiの看板が。町おこしさながらの盛り上がりでした。 空港で明らかにrubyistの方(第一rubyist)を見かけたので、タクシーに相乗りしました。 有名なラーメン屋さんで降りるとのことだったので、一緒に食べながら技術の話やお互いの会社の制度・AIツールの導入状況などに花を咲かせました。 Day1 会場へ 会場は、JR函館駅から市電で30分ほどの場所にある「 函館サーモン・まるなまアリーナ/ホール 」でした。命名権を地元の水産会社が取得されたらしく、函館感全開の良い会場名とロゴになってました。 チェックインすると、首から掛けるストラップと名札をいただきました。 会場で「どこの会社の方ですか?」と度々聞かれるので弊社のロゴを手書き。 なんとか伝わりました。 基調講演 The Journey of Box Building 発表資料はこちら 初日の講演は、rubyコミッターでもある田籠 聡さんの基調講演から始まりました。 内容は、2025年12月25日にリリースされた Ruby4.0.0 の新機能の一つである、 Ruby::Box についてでした。 Ruby Boxはクラス等の定義の分離/隔離のための機能を提供する、実験的機能です。 実験的な機能として位置付けられているとのことでしたが、安全にコードの分離ができるようになる点は業務にも活かせそうでした。 Exploring RuboCop with MCP 発表資料はこちら Keynote以外の公演は基本的に大ホール・小ホール・サブアリーナで行われ、同時刻に開催される講演に同時に参加することはできないため、タイムスケジュールと発表内容を見て選ぶ形式でした。 英語での講演も多く、同時翻訳アプリで聴くことにも挑戦しましたが、やはり日本語の講演の方が理解しやすく、自然と日本語の講演を選ぶことが多かったです。 そんな中で、初日の午後に参加した伊藤浩一さんの RuboCop x MCPの話が印象に残りました。 RuboCopは人間や他のプログラムによってトリガーされていました。AI時代においては、AIエージェントが新たなトリガーとして登場しました。本講演では、生成型AIとリンターおよびフォーマッターを組み合わせる実践的な方法について議論します。 決定性がLinterとしての価値だった中で、非決定性を持つLLMを組み合わせることでどんな価値が創出できるのか(又は失われるのか)という試行錯誤の話が面白く、こういったイベントに参加して直接コミッターの方のお話を聞く醍醐味だと感じました。 Day2 基調講演 Twenty Years of JRuby 2日目は20年以上 JRuby (Java仮想マシン上で動作するRubyの処理系)を開発している、Charles Nutterさんの基調講演から始まりました。 内容はJRuby が誕生してからの20周年の振り返りと、JRuby 10.1 のリリースについてでした。 業務を含めこれまでCRuby(C言語で実装されたRubyの処理系)しか触ったことがなかったのですが、JRubyやMRubyのような別の処理系にも興味が湧きました。 Practical TypeProf: Lessons from Analyzing Optcarrot 発表資料はこちら 2日目は TypeProf 開発者の 遠藤侑介さん の講演が印象に残りました。 メインの話は、TypeProfを実際に適用してみた事例です。題材に選ばれたのは、発表者自身が開発したRubyで書かれたNES(ファミコン)のエミュレータで、約6000 行の複雑なコードに TypeProf を適用したところ600個以上のエラーが出たとのことです。 対応としては、型推論結果に強く影響する箇所にRBSを書くことと、TypeProf 側の誤認識の根本原因を直すことの2方向から進められ、最終的には Ruby 55行+RBS 67行、合計122行の変更でゼロエラーに到達したとのことでした。SteepやSorbetと比べても、既存コードへの変更量がかなり少ないという結果でした。 終盤では、AI コーディングエージェントが普及する中でTypeProfをどう位置づけるのか、という問いも提示されていました。エディタ支援を主目的としてきたツールが、AI 時代にどんな価値を持てるのかという点についても言及されていました。 Day2の交流会 2日目の夜は、Drink Upに参加し、LT枠が空いていたので発表しました。 3日目のRubyKaigiがより面白く聞けるようにと思い、Rubyが実行されるプロセスをParserの話からGCの話まで、一通りまとめてみました。 資料は marp + Opus4.6で作成 Day3 Lightning-Fast Method Calls with Ruby 4.1 ZJIT 発表資料はこちら RubyKaigi最終日は、 国分崇志 さんのZJITの講演が印象に残りました。 注目度の高い ZJIT ですが、今回の発表では特にメソッド呼び出しの高速化に焦点が当てられていました。 Rubyでは依然としてメソッド呼び出しのオーバーヘッドが大きく(動的型付けだから仕方ないが)、YJIT によって改善が進んできた現在でも、なお大きなボトルネックとして残っているそうです。ZJIT ではこの処理の最適化が大きなテーマになっており、バックトレースや例外処理、ローカル変数アクセスに必要なメタデータをどう保持しつつ、無駄なメモリライトを減らすかが論点になっていました。 そこで紹介されていたのがLightweight Framesでした。これは、メソッド呼び出し時に必要なフレーム情報を最初からすべてメモリに書き込むのではなく、必要になるまで遅延させ、まずは最小限の情報だけを持つことでコストを下げるアプローチです。 Rubyの柔軟な書き心地は維持しつつ、高速に動くようにしていく取り組みをしていただいてる事に感謝です Matz Keynote もちろん最後は、Rubyを作った「 まつもと ゆきひろ 」さん(Matz)の基調講演でした。 ここ半年くらいは自分でコードを書かずに、ほぼ全てAIに書かせるという縛りを自らに課しているとのことでした。コードレビューやプロンプトを細かく制御できるからこそできる事だと思いますが、実務の現場でもAIエージェントは欠かせない存在になってきているのではないでしょうか。 そんな中で大きなトピックは、新しいRubyのAOTコンパイラ「 Spinel 」の発表です。RubyコードをC言語に変換してからコンパイルすることで、ネイティブバイナリを生成するという試みで、すぐに業務レベルで役立てられるものではなさそうですが、インタプリタ言語×AOTコンパイルという発想と開発過程が興味深かったです。(Rubyは動的機能が多いので、機械語にできる=高速化ではないことに注意してください) まとめ RubyKaigiに行って良かったと思う1番のポイントは、Rubyやプログラミングが大好きな人達の「熱」を感じられたことでした。普段「仕事」としてプログラミングをしていると商業的な観点で価値を測り測かられる癖がついてしまって、楽しいとか知的探求という側面を忘れていたなぁと思いました。 また、現地での他社のエンジニアとの交流も刺激的でした。今まで自分がマイナビ色に染まっている自覚はありませんでしたが、他社のエンジニアと交流すると明らかに各社個性というか雰囲気があって、たまには視野を広げるためにも交流して新しい知見を持ち帰ってくる必要があると感じました。
Developer Engagementブロックの @ikkou です。2026年4月22日から24日の3日間にわたり北海道は函館市の 函館サーモン・まるなまアリーナ で「 RubyKaigi 2026 」が開催されました。 日本Rubyの会「RubyKaigi 2026」特別ライトアップ 今回の函館開催にあわせ、通常の白色のみの五稜郭タワーのライトアップが、Rubyをイメージした特別色のレッドにライトアップされていました。 ZOZOは今年もプラチナスポンサーとして協賛し、スポンサーブースを出展しました。 technote.zozo.com 本記事では、前半はWEARのバックエンドエンジニアが気になったセッションを紹介します。後半では、ZOZOの協賛ブースの様子と各社のブースにおけるコーディネートを写真中心に報告します。 ZOZOとWEARとRubyKaigi WEARのバックエンドエンジニアが気になったセッション A Faster FFI そもそもFFIとは? 現状のFFIの課題 なぜFFIは遅いのか? 改善1 改善2 FFXの仕組み 最終的なパフォーマンス ruby.wasm can also enable JavaScript to call Ruby libraries. The Less-Told Story of Socket Timeouts なぜopen_timeoutが必要だったのか net/httpのtimeoutライブラリ依存問題 resolv_timeout + connect_timeoutでは代替できない open_timeoutのAPI仕様 試してみる Ruby 3.4: fast_fallback ON Ruby 3.4: fast_fallback OFF Ruby 4.0: open_timeout fast_fallback ON + open_timeout fast_fallback OFF + open_timeout 同時指定はエラー 全体の対比 おわりに Autoresearching Ruby Performance with LLMs Autoresearchとは なぜ「ループ」が重要なのか 4つのループパターン Ralphループ Autoresearchループ Factoryループ Sidekiqでの実証実験 実験の背景 "マージされないコードを生成することの意味" PRレビューとは何か? 4つのレッスン Lesson 1: 「自動調査」であって「自動変更」ではない Lesson 2: 自分がオーナーで Architect でないものに Autoresearch を適用しない Lesson 3: ループはビター・レッスンを実践する Lesson 4: 人間のゲートをソフトウェアゲートに変換する Software Factory とその課題 まとめ おわりに Exploring RuboCop with MCP The Journey of Box Building Ruby Boxとは何か Ruby Boxの仕組み Ruby Boxが生まれた歴史 おわりに ZOZOブースの紹介 協賛企業ブースのコーディネートまとめ RubyKaigi 2026 アフターイベント〜初参加LT・スポンサー4社のパネル〜を開催します おわりに ZOZOとWEARとRubyKaigi 私たちが運営する ファッションコーディネートアプリ「WEAR by ZOZO」 のバックエンドはRuby on Railsで開発されています。2013年にVBScriptで構築されたシステムでしたが、2020年頃からコードフリーズし、Rubyへのリプレイスを開始しました。現在もリプレイスを進めながら、新規の機能もRubyで開発しています。また、Matzさんを技術顧問としてお迎えし、毎月Matz MTGと称したオンラインミーティングを実施しています。 ZOZOとRubyKaigiの関係は、ZOZOの前身であるVASILY時代の RubyKaigi 2017 に遡ります。コロナ禍を経て再開した RubyKaigi 2022 からはWEARのバックエンド開発を担うチームが中心となって協賛とスポンサーブースの出展を続けています。 RubyKaigi2017参加レポート(全日分)とスライドまとめ RubyKaigi2018参加レポート RubyKaigi 2019参加レポート〜sonots登壇セッション & エンジニア8名による厳選セッション RubyKaigi 2022参加レポート 〜エンジニアによるセッション紹介〜 RubyKaigi 2023参加レポート 〜エンジニアによるセッション紹介〜 RubyKaigi 2024 参加レポート RubyKaigi 2025 協賛&参加レポート WEARのバックエンドエンジニアが気になったセッション 今年はWEARチームから6名のバックエンドエンジニアがRubyKaigiに参加しました。本パートでは各エンジニアが特に気になったセッションを個々の視点で紹介します。 A Faster FFI chika です。私からはAaron Patterson( @tenderlove )氏の「 A Faster FFI 」を紹介します。 このセッションでは、「RubyはC言語より速くなるか?」という問いからスタートし、具体的にはRubyのFFIを高速化し、ネイティブのC言語(C拡張)よりもRubyを速く実行できるか? というのがメインの議題でした。 余談ですが、Aaron氏はRubyKaigi 2026の外国人登壇者の中でおそらく唯一日本語でスピーチする方です。流暢な日本語に加えて時折ジョークも交え、大変ユニークなセッションであり毎年楽しみに拝聴しています(最初の挨拶では「外国人みたいな名前ですが、外国人です」と日本語でジョークを言っていて面白かったです)。 そもそもFFIとは? FFIとはForeign Function Interfaceの略称で、一般的にはRubyのような高水準言語からC言語やRust, Zigなどで書かれた外部の関数を呼び出すための「概念」のことを指します(FFI自体は、あるプログラミング言語から他のプログラミング言語で定義された関数などを利用するための機構)。 Rubyでは主にlibffiというライブラリやfiddle gemを介して利用され、プラットフォームごとの呼び出し規則の違いを吸収してくれます。 例:FFIを使ったhello world #include <stdio.h> void hello ( void ) { printf ( "Hello, World from C! \n " ); } require ' ffi ' module Hello extend FFI :: Library ffi_lib File .expand_path( ' libhello.dylib ' , __dir__ ) attach_function :hello , [], :void end Hello .hello cc -shared -o libhello.dylib hello.c && ruby hello.rb #=> Hello, World from C! FFIはネイティブC拡張に比べ、CRuby, JRuby, TruffleRubyといった異なるRuby実装でも、ほぼそのまま動くというポータビリティの観点でもメリットがあります。 現状のFFIの課題 一見するとRubyからC言語の関数が直接呼び出せるのはパフォーマンスや移植性などの観点から便利そうに見えますが、実際のところFFIは滅多に使われておらず、その理由としてパフォーマンスが悪いという点が挙げられていました。 ベンチマーク比較では、既存のC拡張とFFIを比較すると、ネイティブC拡張の方が約2.4倍も高速に動作しているとのことでした(FF愛(FFI)してない…)。 なぜFFIは遅いのか? FFIが遅い主な原因として、以下の三点を挙げていました。 余分なフレームプッシュ Rubyから目的のC言語の関数を最終的に呼び出すまでに、呼び出し規則の変換などを行う「中間的な関数」がFFIやVM内部で何層も呼び出される。そのたびにコールスタックに余分なフレームが積まれる(プッシュされる)ため、関数呼び出しそのもののオーバーヘッドが大きくなってしまう。 値の渡し方の違い Rubyはスタックを使って値を渡すが、C言語は(x86_64やARM64などの環境において)CPUのレジスタを使用して値を受け取る。そのため、C言語の呼び出し規約(ABI)に合わせて、スタックからレジスタへ値をコピーして詰め直す操作が発生する。 タイプ変換 RubyのオブジェクトをC言語が理解できる型に変換し、Cからの戻り値を再びRubyオブジェクトに変換するオーバーヘッドが発生する。 改善1 この遅い原因を解消するために、JITコンパイラを用いてそれらのコストを削減するアプローチを試みていました。そして、Rubyで書かれたFFIのためのJITコンパイラ「FJIT」が誕生しました。これはfiddle gemなど複数のgemの機能を活用して直接マシンコードを生成することで、既存のFFI実装に比べて約2倍ほど高速化することが可能となりました。 改善2 さらにもう1つの解決案として提案されたのが、CRuby内蔵のJITコンパイラであるZJITと、新しいトランスレータである FFX でした。 FFXの仕組み FFXは、Rubyで書かれたFFI拡張のコードを読み込み、自動的にC拡張のコードに変換します。この時、生成されるCコードの中にはZJIT向けのヒント(具体的には、生成されるCコードの中に「この関数の引数はこういう型である」といったメタデータをZJITが読み取れる形で配置する)が意図的に埋め込まれます。ZJITはこのヒントから関数の引数や戻り値の型情報を認識することで、最適化された高速なマシンコードを動的に生成することが可能となります。 最終的なパフォーマンス これらの改善を導入しベンチマークを測定したところ、なんとC拡張よりも約1.4倍速く動作するという結果になりました。結果的に、「RubyはCよりも速くなる?」という問いに対しては、「(JITとFFXの力を借りれば)イエス」となり、つまり「RubyはCよりも速い」という結論で締め括られていました(笑) FFIの「遅いから使わない」という常識が覆りつつあり、ZJITやFFXの成熟によってRubyからCライブラリを気軽に・高速に呼べる未来が近づいていると感じました。 ruby.wasm can also enable JavaScript to call Ruby libraries. 小島( @KojimaNaoyuki )です。私からはShigeru Nakajima( @ledsun )さんの「 ruby.wasm can also enable JavaScript to call Ruby libraries. 」を紹介します。 www.docswell.com このセッションでは、ruby.wasmのこれまで積み重ねられてきた改善を振り返りつつ、なぜまだ実務での採用事例が少ないのかの分析とそれを解消するために「JSからRubyを呼び出す」機能を強化していく方針が話されていました。 ruby.wasmとは、ブラウザ環境でRuby実行を可能にするものです。JavaScriptと相互で連携しながらRubyをブラウザ環境で実行できます。 ruby.wasmはこれまでRubyからJavaScriptを呼び出す改善を多く実施しています。しかし、現状、本番環境で利用されている例はあまりないそうです。その原因としてこのセッションでは以下の2点について言及されていました。 ruby.wasmバイナリが大きすぎる JSからRubyを呼ぶサポートが弱い ruby.wasmバイナリが大きすぎることについては https://cdn.jsdelivr.net/npm/@ruby/head-wasm-wasi@2.9.3-2.9.4/dist/ruby+stdlib.wasm を例に出して話されていました。こちらのファイルは非圧縮で31.80MiB、圧縮すると8.84MiBになるそうです。そして、BtoCのブラウザアプリケーションではダウンロードサイズが重要ですが、BtoBの場合はダウンロードサイズは大きな問題ではない可能性があるとセッションでは話されていました。 JSからRubyを呼び出すサポートが弱いことについては、RubyからJSを呼び出す場合とJSからRubyを呼び出す場合を比較して扱いやすさの違いを言及していました。以下がセッションで使用されていた表です。 Feature R → J J → R eval Yes Yes Method Call Yes call Property Access Yes N/A Object Conversion Yes string Load Ruby Scripts browser No 出典: ruby.wasm can also enable JavaScript to call Ruby libraries. | ドクセル (最終閲覧日:2026/05/07) こちらを見ると確かにJSからRubyを呼び出す場合にサポートされていないものが多い印象ですね。現状JSからRubyを呼び出すにはevalメソッドを利用して実行できますが、rubyの実行結果の戻り値を扱う適切なAPIが不足していたりと課題があるようです。 これらの現状を受けて、本番環境で利用されている例が少ないのは「JSからRubyを呼ぶ」機能が不十分であるからではないかと分析されていました。そして、もしJSからRubyを呼ぶことが簡単にできたなら、ロジックの二重実装を防ぐことができるため有用ではないかとおっしゃっていました。ロジックをRubyに統一できれば修正も容易になり保守性も向上しそうですね! 「JSからRubyを呼ぶ」機能を強化していくためにはRubyオブジェクトをラップするRbValueを拡張する必要があり、このプロジェクトを「Ruby’s Blanket」と呼ぶと発表がありました。ちなみに、「Ruby’s Blanket」という命名は函館出身の人気バンドGLAYの楽曲に由来しているともおっしゃっていました。 セッションでは、目指していきたい理想の姿も紹介されており、以下のような使用感を想定しているようです。 vm . evalFile ( "dog.rb" ) ; const dog = vm .eval ( “Dog . new” ) ; dog . vow () ; dog . vow () ; const count = dog . count_of_vows . toInt () ; 出典: ruby.wasm can also enable JavaScript to call Ruby libraries. | ドクセル (最終閲覧日:2026/05/07) JSネイティブなプロパティアクセスやメソッド呼び出しでRubyプログラムを扱っているところを示しています。すごく直感的にJSからRubyコードを呼び出せていますね! セッションの最後にはコーディングエージェントの活用についても語られていて、以前よりも難しい課題に取り組むことができていると語られていました。「Ruby Committers and the World」や「Matz Keynote」でもAI活用が議題に上がっており、多くのコミッターがAIを駆使してRubyの改善に当たっているそうです。 個人的には、言語実装のような「世界的にサンプルが少なそうな低レイヤーの領域」はAIが不得意な分野だと思い込んでいたため、これほど多くのコミッターが活用している事実に驚かされました。 Ruby本体や周辺ライブラリの開発が、AI活用によって今後さらに加速していくのが非常に楽しみです! このセッションを通して「JSからRubyを呼ぶ」機能が充実することで、Rubyの活用の幅が広がる未来を感じました。実際のプロダクトのブラウザ上でRubyが動いているところを目にすることを期待しています! The Less-Told Story of Socket Timeouts WEARのバックエンド開発を担当しているaao4seyです。私からは、 @coe401_ さんの「 The Less-Told Story of Socket Timeouts 」を紹介します。 このセッションでは、Ruby 4.0で Socket.tcp / TCPSocket.new に新しく加わった open_timeout の導入経緯と、その背後にあるsocketライブラリのタイムアウトの歴史が語られていました。本記事では open_timeout が必要になった経緯を発表内容を踏まえて整理し、続いて各Rubyバージョンで実際にタイムアウトの挙動がどう変わるのかを手元で観察した結果を紹介しようと思います。 なぜopen_timeoutが必要だったのか net/httpのtimeoutライブラリ依存問題 発表者のもとに「net/httpのtimeoutライブラリ依存を外したい」という相談が届いたことが、 open_timeout 導入のきっかけでした。スライド内でも紹介されていますが、Ruby 2.7の開発時代にもnet/httpのような標準ライブラリが、標準ではないライブラリに依存しないほうが良いのではという趣旨の提案がなされていました。 加えて、timeoutライブラリは内部で Timeout::State::GLOBAL_STATE という共有状態を持っているため、non-main Ractorからこれにアクセスすると Ractor::IsolationError が発生してしまう状況でした。 resolv_timeout + connect_timeoutでは代替できない Ruby 4.0開発時点で、 Socket.tcp / TCPSocket.new には名前解決のタイムアウトを指定する resolv_timeout と、接続確立のタイムアウトを指定する connect_timeout がすでに用意されていました。これらを組み合わせればtimeoutライブラリの代替になりそうにも思えますが、実際にはメソッド全体の絶対上限時間を制御できません。 ここで前提として1つ補足しておくと、Ruby 3.4で Socket.tcp / TCPSocket.new にはHappy Eyeballs Version 2 (HEv2 / RFC 8305)が fast_fallback という名前でデフォルト有効として導入されました。HEv2は複数候補のIPアドレスに対して名前解決と接続試行を並行実行するアルゴリズムです(HEv2 自体の解説は同僚が「 RubyKaigi 2025 協賛&参加レポート 」で書いているので、本記事ではそちらに委譲します)。なお、Ruby 3.4までは fast_fallback: false 経路の resolv_timeout がAPI onlyで実装されておらず、本記事の対象であるRuby 4.0で両経路で機能するように修正されています。 その上で、たとえばfast_fallback有効で resolv_timeout: 2000ms , connect_timeout: 1000ms を指定した場合、IPv6が先に解決されてIPv4が解決待ちのまま接続試行が始まると、 connect_timeout の1000msを過ぎても resolv_timeout の期限まで待機が続きます。結果、全体タイムアウトは2つの合計でも connect_timeout の値でもなく、 resolv_timeout の値である2000msに支配されてしまいます。 HEv2のConnection Attempt Delay (250ms)が絡むケースでは「 connect_timeout: 1000ms を指定しても全体は1250ms待つ」、fast_fallback無効では「IP数 × connect_timeout 」と、いずれもユーザーが指定した値とは別の値で全体時間が決まってしまいます。 open_timeoutのAPI仕様 これらの背景を受けて、発表者ご自身が open_timeout を Socket.tcp / TCPSocket.new に追加する提案をされました。 open_timeout はtimeoutライブラリと同じく「名前解決〜接続確立の全体」を1つの期限で管理するオプションです。 Socket .tcp( " ruby-lang.org " , 80 , open_timeout : 1 ) 設計上の論点として、 open_timeout を resolv_timeout / connect_timeout と併用された場合にどう振る舞うかを整理する必要がありましたが、複雑になりすぎてしまうという課題があり、最終的に open_timeout は connect_timeout / resolv_timeout との同時指定を禁止し、同時指定時は ArgumentError を投げる仕様となったそうです。 発表内では実装の細かい部分が fast_fallback ありなしごとに説明されていました! スライドのp.224以降に記載されています。 試してみる ここまで見てきた経緯を踏まえて、 open_timeout がRuby 4.0で動作するのかの検証をしてみます。また、私自身はRuby 3.4で導入された fast_fallback 機能もこの発表内で知ったので、こちらの挙動も合わせて確認してみることにしました。 実験のため /etc/hosts に応答しない3つのIPを割り当てた test-multi-local.wear.jp を用意しました(記載のIPはdummyです)。 192.168.xx.1 test-multi-local.wear.jp 192.168.xx.2 test-multi-local.wear.jp 192.168.xx.3 test-multi-local.wear.jp 接続先には応答しないポート(50000)を指定し、SYNを送っても応答が返ってこない状況を作ります。 Ruby 3.4: fast_fallback ON この場合、HEv2のアルゴリズムに従い以下のような挙動になるはずです。 約250ms間隔(Connection Attempt Delay)で次のIPへ並行に試行を発射している 期待結果: (IP数-1) × 250ms + connect_timeout = 1.5 秒 で全体タイムアウト 検証スクリプト require " socket " t = Time .now begin Socket .tcp( " test-multi-local.wear.jp " , 50000 , connect_timeout : 1 , fast_fallback : true ) { |s| s.close } rescue => e puts "#{ e.class } : #{ e.message }" ensure puts " elapsed= #{ Time .now - t } s " end 実行結果(Ruby 3.4.9) Errno::ETIMEDOUT: Operation timed out \- user specified timeout elapsed=1.51281s tcpdumpの結果抜粋 10:41:18.580 SYN → .1 ← IP1 開始 (t=0) 10:41:18.836 SYN → .2 ← IP2 開始 (t≒0.256) 10:41:19.086 SYN → .3 ← IP3 開始 (t≒0.506) 想定どおり、約1.5秒でtimeoutエラーになることが観測できました! また、tcpdumpでSYNパケットをざっくり観測した結果、約250msごとに異なるIPへSYNパケットが飛んでいることも見て取れました。 Ruby 3.4: fast_fallback OFF fast_fallback: false を渡すと、Ruby 3.3以前と同じ直列フォールバックの挙動に戻ります。以下のような挙動になるはずです。 各IPに対して connect_timeout: 1 で1秒ずつ順番に試行 期待結果: IP数 × connect_timeout = 3 秒 で全体タイムアウト 検証スクリプト 前述のスクリプトの Socket.tcp 呼び出し行を以下のように変更します。 - Socket.tcp("test-multi-local.wear.jp", 50000, connect\_timeout: 1, fast\_fallback: true) { |s| s.close } + Socket.tcp("test-multi-local.wear.jp", 50000, connect\_timeout: 1, fast\_fallback: false) { |s| s.close } 実行結果(Ruby 3.4.9) Errno::ETIMEDOUT: Operation timed out \- user specified timeout elapsed=3.013505s tcpdumpの結果抜粋 10:40:20.753 SYN → .1 ← IP1 開始 (t=0) 10:40:21.760 SYN → .2 ← IP2 開始 (t≒1.0) 10:40:22.764 SYN → .3 ← IP3 開始 (t≒2.0) 想定通り、fast_fallbackを無効にするとIP1 → IP2 → IP3と1秒ずつ順番に試行され、合計約3秒で全体タイムアウトすることが観測できました。 Ruby 4.0: open_timeout ここまでの結果から、 connect_timeout だけではメソッド全体の上限時間を制御できないことが見えました。Ruby 4.0で導入された open_timeout を使うと挙動がどう変わるかを見ていきます。 fast_fallback ON + open_timeout fast_fallback ONで open_timeout が機能するか試します。 期待結果:open_timeoutに指定した1秒でタイムアウト 検証スクリプト 前述のスクリプトの Socket.tcp 呼び出し行を以下のように変更します。 \- Socket.tcp("test-multi-local.wear.jp", 50000, connect\_timeout: 1, fast\_fallback: true) { |s| s.close } \+ Socket.tcp("test-multi-local.wear.jp", 50000, fast\_fallback: true, open\_timeout: 1) { |s| s.close } 実行結果(Ruby 4.0.3) IO::TimeoutError: user specified timeout for test-multi-local.wear.jp:50000 elapsed=1.004653s 想定通り、約1.0秒で IO::TimeoutError が発生し、 open_timeout: 1 で指定した時間とほぼ同じ時間でタイムアウトすることが観測できました! fast_fallback OFF + open_timeout fast_fallback OFFで open_timeout が機能するか試します。 期待結果:open_timeoutに指定した1秒でタイムアウト 検証スクリプト 前述のスクリプトの Socket.tcp 呼び出し行を以下のように変更します。 \- Socket.tcp("test-multi-local.wear.jp", 50000, connect\_timeout: 1, fast\_fallback: true) { |s| s.close } \+ Socket.tcp("test-multi-local.wear.jp", 50000, fast\_fallback: false, open\_timeout: 1\) { |s| s.close } 実行結果(Ruby 4.0.3) Errno::ETIMEDOUT: Operation timed out \- user specified timeout for 192.168.xx.1:50000 elapsed=1.007804s こちらも想定通り、約1.0秒で user specified timeout が発生し、 open_timeout: 1 で指定した時間とほぼ同じ時間でタイムアウトすることが観測できました! fast_fallback の設定有無に関わらず open_timeout が機能していることがわかりました。 同時指定はエラー open_timeout は connect_timeout / resolv_timeout との同時指定が禁止されており、 Socket.tcp の入口で ArgumentError が即座に発火します。 Socket .tcp( " test-multi-local.wear.jp " , 50000 , connect_timeout : 1 , open_timeout : 1 ) { |s| s.close } # => ArgumentError: Cannot specify open_timeout along with connect_timeout or resolv_timeout 全体の対比 ここまで観測した結果を並べると以下のようになります。 Ruby 設定 elapsed 3.4.9 fast_fallback: false , connect_timeout: 1 約 3.01 秒 3.4.9 fast_fallback: true , connect_timeout: 1 約 1.51 秒 4.0.3 fast_fallback: false , open_timeout: 1 約 1.01 秒 4.0.3 fast_fallback: true , open_timeout: 1 約 1.00 秒 connect_timeout だけを指定したケースでは、ユーザーの指定値とは別の値で全体時間が決まっていたのに対し、 open_timeout を指定すれば 指定値ぴったりで打ち切られる ようになりました。 おわりに 本パートではセッションの内容である open_timeout の導入経緯と、実際に fast_fallback や open_timeout の挙動を観測した結果を紹介しました! open_timeout の実装により、接続試行全体のタイムアウトが管理しやすくなったと感じます。実際のセッションではRuby 2.0の開発時代から今に至るまでのsocketライブラリのタイムアウト導入の歴史も詳細に説明されており、各時代ごとにどんな課題がありどう解決してきたのかを知ることができとても興味深かったです。また、このセッションに限らずですが、RubyKaigi全体を通して発表者の方のモチベーションを垣間見ることができたのも自分にとって良い刺激となりました。 Autoresearching Ruby Performance with LLMs 小山 です。私からはNate Berkopec( @nateberkopec )さんの「 Autoresearching Ruby Performance with LLMs 」を紹介します。 Berkopec氏は、Speedshop代表でありPumaメンテナー、Railsパフォーマンスコンサルタントとして広く知られています。本セッションは Autoresearch という先行ツールを参考に、AIエージェントを使って、Rubyのパフォーマンス問題を自動で調査する取り組みとそれにより得られた示唆の発表でした。 発表資料は以下のリポジトリで公開してくださっているためご参照ください。 github.com Autoresearchとは Autoresearchは、2026年3月にAndrej Karpathy( @karpathy )氏が公開したLLM実験自動化ツールです。仕組みはシンプルです。 AIエージェントがコードへの変更を提案・適用する 一定時間ベンチマークを実行する ベースラインより良ければコミット、悪ければリバート 1に戻る これを無限に繰り返します。PyTorchと単一のメインファイル以外に依存関係がなく、1時間あたり約12実験、一晩で約100実験を回せます。Berkopec氏がこのコンセプトをRubyのパフォーマンス改善に応用できるかを探ったのが今回の発表です。 なぜ「ループ」が重要なのか セッションで繰り返し強調されたのが ループ という概念です。 AIエージェントは本質的にはループにすぎないと、Berkopec氏は次のコード例で説明しています。 messages = [user_prompt] loop do reply = llm.call(messages, tools : TOOLS ) break puts(reply.text) unless reply.tool_call? result = run_tool(reply.tool_name, reply.arguments) messages << reply messages << tool_result(result) end このループに どのようなゲート(通過条件)を置くか が、ループの性格を決めます。それがセッション全体の一貫したテーマでした。 4つのループパターン Berkopec氏は、ゲートの種類によってループを4種類に分類しました。 ループ ゲート シグナル 成果物 Agents LLM 自己停止 離散 最終的な返答 Ralph ビルド+テスト 離散 グリーンなコミット Autoresearch ベンチマーク差分 連続 改善した diff Factory 多数のチェック 多変数 マージ可能な PR Ralphループ while :; do cat PROMPT.md | claude-code ./build \_ and \_ test || continue git add \- A git commit \- m " ralph: passing build " git push done ビルドとテストをゲートにしたループです。テストが通った変更だけをコミットし続けます。バグへの対処に向いています。 Autoresearchループ best \= benchmark loop do change \= agent.propose\_optimization apply(change) score \= benchmark if score \> best git\_commit(change.summary) best \= score else git\_revert end end ゲートがブール値(pass/fail)ではなく 連続値(ベンチマークスコアの改善量) である点がRalphとの違いです。 Factoryループ backlog.each do |spec| loop do code \= agent.implement(spec) gates \= scenarios.map { |s| s.run(code) } break if gates.all?(& :pass? ) end ship(code) end スペックからコードを生成し、複数のゲートを全て通過したら出荷するパターンです。StrongDM社の「 Software Factory 」が参照されていました。 Sidekiqでの実証実験 セッション中に紹介された実験の1つが、 pi-autoresearch を用いた、Sidekiqを対象にした自動最適化です。 実験の背景 Sidekiqの Processor::Counter はアトミックなカウンターで、16行程度のシンプルな実装です。Autoresearchエージェントはここにストライプドロック(各スレッドが自前の状態を持ち、書き込みを安くする仕組み)を適用する変更を提案しました。 また、別のPRでは Time.now.to_i を Process.clock_gettime に置き換えることも提案されました。 Comparison: Process.clock_gettime: 17294486.8 i/s Time.now.to_i: 12698329.6 i/s - 1.36x slower Timeオブジェクトを生成せずに整数を直接返すことで1.36倍高速化できます。 "マージされないコードを生成することの意味" これらの変更についてBerkopec氏は「OSSメンテナーは実際にはこれをマージしないだろう」と正直に述べていました。その上で、なぜマージできないコードを生成するのか、という問いを投げかけました。 "Why generate code that you can't merge?" この問いに対して、セッションは PR レビューの目的を再定義するパート へと展開しました。 PRレビューとは何か? 変更を却下する理由として、Berkopec氏は以下を挙げました。 バグがある → LLMはある程度得意 トレードオフがある →「十分に速い」とはどのレベルか? 複雑すぎる → Flog/Flayスコア、ABCスコア、LOC リスクが高すぎる → キャッシュ無効化など(Autoresearchはキャッシュが大好きで、しかもバグりやすい) テストを通るが... → Autoresearchは良いテストがある前提で動く GPL 違反など → GitHub外の法的・コンプライアンスチェック 要するに 「マージできるか」を判断する能力こそがArchitectである という主張です。 4つのレッスン セッションを通じて提示された4つのレッスンをまとめます。 Lesson 1: 「自動調査」であって「自動変更」ではない Stan Lo氏(Shopify)の取り組みが紹介されました。Ruby-lspのCI時間を33%削減、rubydexのインデックス速度を10%〜50%改善するといった成果を、AIの提案を自身がレビューした上で実現しています。AIの出力を人間が検証・理解して取り込む姿勢が重要だという例です。 Lesson 2: 自分がオーナーで Architect でないものに Autoresearch を適用しない 理解・検証・修正できるコードにのみ適用すべきです。 Lesson 3: ループはビター・レッスンを実践する Richard Suttonさんの「苦い教訓(Bitter Lesson)」(人間の知識を組み込むより、計算のスケールによって探索・学習する方が長期的に優れるという教訓)をループは体現しています。ループは人間のゲートを担保するバージョンに過ぎず、ゲートをソフトウェアに置き換えることで探索をスケールさせられます。 Lesson 4: 人間のゲートをソフトウェアゲートに変換する 「遅い」という感覚を赤/緑の状態・バグチケットに変換するプロセスがパフォーマンス改善の本質です。たとえAutoresearchが失敗しても、ゲートを定義する過程でソフトウェアの品質自体が上がります。 Software Factory とその課題 セッション後半では、 Software Factory (StrongDM社の提唱するコンセプト)が紹介されました。 "Code must not be written by humans. Code must not be reviewed by humans." — StrongDM's "Software Factory" これは人間のレビューを完全に排除し、多数の自動ゲートを通過したコードのみを出荷する考え方です。セッションではこれを「dark factory」と表現し、Berkopec氏は多くの未解決の問いを並べて紹介しました。 形式手法・プロパティベーステストは現実的にスケールするか? 既存の大規模コードベース(ブラウンフィールド)に適用できるか? LOCの肥大化をどう防ぐか? LLMがトレーニングデータの著名な最適化を「再発明」するだけにならないか? 決定論的なゲートとLLMジャッジをどう組み合わせるか? Berkopec氏はこれらに対して現時点で答えはないとしながらも、「ゲート設計こそが重要になる」という方向性を示しました。 まとめ Berkopec氏のセッションを一言でまとめると、 「AI に自律的なループを回させることはできるが、それを意味のある成果に変えるにはゲート設計という人間のスキルが必要」 というメッセージでした。 発表で推奨されていた実践ステップは以下の通りです。 自分が十分に理解・レビューできるコードにのみ適用する(Stan Lo氏のアプローチ) 成熟した小さなモジュールから始める ゲートが最小で済む場所からスタートする ゲートを増やせば人間のレビューを減らせる Ralph(離散)・Autoresearch(連続)・Factory(多ゲート)のいずれかのループを試してみる おわりに 今年のRubyKaigiはAIに関するセッションの多さが印象的でした。普段の業務で自身もAIを活用しています。本セッションで紹介されていたゲートの設計・導入に注力して、パフォーマンス改善はもちろんのこと、それ以外のあらゆる業務課題の自動化をより推進していきたいと思いました! Exploring RuboCop with MCP 伊藤です。今年もRubyKaigiに参加してきました! 私からは @koic さんの「 Exploring RuboCop with MCP 」をご紹介します。 speakerdeck.com 本セッションは「I. MCP Ruby SDK」「II. RuboCop x MCP」の2部構成で、前半では @koic さんがコミッターを務める公式の MCP Ruby SDK ( mcp gemとして公開)の設計や、Streamable HTTPでのセッション管理・Sampling・Elicitationまでが解説されました。後半は、RuboCop 1.85.0で実験的に追加された組み込みMCPサーバーの紹介でした。 ここでは特に後半「RuboCop x MCP」を題材に、WEARのバックエンドエンジニアとして実際に手元で動かしてみた感想を中心にご紹介します。 rubocop --mcp は、RuboCop 1.85.0から導入されたサブコマンドで、AIエージェント(Claude CodeなどのMCPクライアント)からRuboCopを呼び出せるようにする組み込みMCPサーバーを起動します。stdio transportで通信し、以下の2つのツールを公開します。 rubocop_inspection :オフェンス検査(読み取り専用) rubocop_autocorrection :オートコレクト適用(破壊的) なお、RuboCopではコーディング規約違反のことを「オフェンス(offense)」と呼びます。以降本記事でもこの用語を使います。 rubocop --lsp の内部で使われている診断機構( RuboCop::LSP::Runtime )をそのまま再利用しているのが特徴で、出力されるオフェンスはLSPの Diagnostic 形式(JSON)です。Clang形式のテキストよりも構造化されているため、AIエージェントがそのままパースして扱いやすい形になっています。 ここからは実際に手元で動かしてみます。 rubocop --mcp を使うには、RuboCop本体に加えて mcp gemが必要です(RuboCopは mcp をruntime dependencyとして持っていないため)。既存のGemfileのdevelopmentグループに以下を追加します。 group :development do gem ' mcp ' end 次にClaude Codeに .mcp.json でRuboCopのMCPサーバーを登録します(Claude Codeが設定を読み、起動時に bundle exec rubocop --mcp をサブプロセスとして立ち上げてくれます)。 { " mcpServers ": { " rubocop ": { " command ": " bundle ", " args ": [ " exec ", " rubocop ", " --mcp " ] } } } Claude Code上で /mcp を叩くと、登録した rubocop サーバーから2つのツールが認識されているのが確認できます。 ❯ /mcp ──────────────────────────────────────────────────── Tools for rubocop 2 tools ❯ 1\. RuboCop's inspection read-only 2\. RuboCop's autocorrection destructive それでは実際に rubocop_inspection を呼んでみます。オフェンスを意図的に仕込んだ以下のサンプルファイル sample.rb を用意して、検査を呼んでもらいます。 # frozen_string_literal: true def greet ( name ) message = " Hello, " +name+ " ! " puts message end greet( " WEAR " ) 「 sample.rb をRuboCopで検査して」とお願いすると、Claude Codeが rubocop_inspection ツールを呼び、結果を以下のように整形して提示してくれました。 ❯ sample.rb をRuboCopで検査して Called rubocop (ctrl+o to expand) ⏺ sample.rb の検査結果: 9 件の offense が検出されました(すべて自動修正可能)。 ┌─────┬─────────────────────────────┬─────────────────────────────┐ │ 行 │ Cop │ 内容 │ ├─────┼─────────────────────────────┼─────────────────────────────┤ │ 2 │ Layout/SpaceInsideParens │ 括弧内の余計なスペース(2 │ │ │ │ 箇所) │ ├─────┼─────────────────────────────┼─────────────────────────────┤ │ 3 │ Style/StringConcatenation │ \+ 連結より文字列補間を推奨 │ ├─────┼─────────────────────────────┼─────────────────────────────┤ │ 3 │ Style/StringLiterals │ "Hello, " → │ │ │ │ シングルクォート推奨 │ ├─────┼─────────────────────────────┼─────────────────────────────┤ │ 3 │ Style/StringLiterals │ "\!" → シングルクォート推奨 │ ├─────┼─────────────────────────────┼─────────────────────────────┤ │ 3 │ Layout/SpaceAroundOperators │ \+ 演算子の前後にスペース無 │ │ │ │ し(2 箇所) │ ├─────┼─────────────────────────────┼─────────────────────────────┤ │ 4 │ Layout/IndentationConsisten │ インデントが不揃い │ │ │ cy │ │ ├─────┼─────────────────────────────┼─────────────────────────────┤ │ 7 │ Style/StringLiterals │ "WEAR" → │ │ │ │ シングルクォート推奨 │ └─────┴─────────────────────────────┴─────────────────────────────┘ すべて rubocop \-A で自動修正できます。自動修正を実行しますか? 「自動修正を実行しますか?」というフォローアップ付きで、続けて rubocop_autocorrection ツールへの切り替えまで自然に提案してくれているのが分かります(行番号がLSPの0-indexed値のまま表示されているのは、RuboCopが返すLSP Diagnosticの値をClaude Code側で1-indexedに変換せずに表示しているためです)。 このとき裏で rubocop_inspection から返ってきている生のJSONは、紙面の都合で code_actions を省略し、検出された9件のうち代表2件のみ抜粋すると以下のようになっています。 { " files ": [ { " path ": " sample.rb ", " offenses ": [ { " range ": { " start ": { " line ": 2 , " character ": 10 } , " end ": { " line ": 2 , " character ": 11 } } , " severity ": 3 , " code ": " Layout/SpaceInsideParens ", " codeDescription ": { " href ": " https://docs.rubocop.org/rubocop/cops_layout.html#layoutspaceinsideparens " } , " source ": " RuboCop ", " message ": " Layout/SpaceInsideParens: Space inside parentheses detected. ", " data ": { " correctable ": true , " code_actions ": [ /* 省略 */ ] } } , { " range ": { " start ": { " line ": 3 , " character ": 12 } , " end ": { " line ": 3 , " character ": 30 } } , " severity ": 3 , " code ": " Style/StringConcatenation ", " codeDescription ": { " href ": " https://docs.rubocop.org/rubocop/cops_style.html#stylestringconcatenation " } , " source ": " RuboCop ", " message ": " Style/StringConcatenation: Prefer string interpolation to string concatenation. ", " data ": { " correctable ": true , " code_actions ": [ /* 省略 */ ] } } ] } ] , " summary ": { " target_file_count ": 1 , " offense_count ": 9 } } LSPの診断と同じ形式なので、各オフェンスにcop名・該当範囲・cop個別のドキュメントURL・自動修正可否( correctable )まで構造化された形で含まれます。さらに省略した code_actions には実際のautocorrectで挿入する文字列(例: Style/StringConcatenation なら "Hello, #{name}!" への置換)や、 # rubocop:disable を入れて該当行で無効化する案もLSPの WorkspaceEdit 形式で含まれており、エージェントから機械的に扱えるようになっています。 rubocop_autocorrection ツールに切り替えれば、そのまま破壊的に修正をかけることもできます。 WEARのバックエンドではすでにRuboCop+各種pluginを大規模に運用しているので、生成AIによる開発を取り入れる際にも「RuboCopを通っている」という決定的なゲートはそのまま使い続けたい、と日頃から思っていました。本セッションは、まさにその「決定的なツール(RuboCop)」と「確率的なLLM」を rubocop --mcp というかたちで素直に橋渡ししてくれる発表で、聞きながら自分の開発フローに組み込む絵が具体的に浮かびました。 セッション後半で @koic さんが投げかけられていた "What happens when combined?"(決定的なツールと確率的なLLMを組み合わせると何が起きるか)という問いも印象的でした。例えば、現状の .rubocop.yml で無効化しているcopをLLMに一時的に有効化させ、レビュー観点として「いま無効になっているcopをあえて見たらどう見えるか」を提示してもらう、といった使い方は、ツール単体だと出てこない発想です。Streamable HTTPでのSamplingやElicitationまで含めて、まだ「探索が始まったばかり」という締めくくり通り、これからRuboCop×MCPでどんな試みが出てくるかとても楽しみです。 The Journey of Box Building 坂元( @sakam0cchan )です。私からはSatoshi Tagomori( @tagomoris )さんの「 The Journey of Box Building 」というセッションを紹介します。 speakerdeck.com このセッションは、Ruby 4.0で実験的に導入される新機能「Ruby Box」をテーマにしたKeynoteです。Boxの基本概念から、実行中のRubyプログラムの中でBoxを特定する際の難しさ、そしてその実装に至るまでの背景などが語られていました。 私自身が初めてRubyKaigiに参加し、そして初めて聞いたKeynoteがこの内容だったこともあり、これからの3日間がどんなものになるかとてもわくわくしました。そんな思いも込めて、内容を共有します。 Ruby Boxとは何か Ruby Box(RubyKaigi 2025までは「Namespace」と呼ばれていた機能)は、Ruby 4.0で導入された実験的な機能です。 Ruby Boxは、これまでRubyのプロセス全体で共有されていたクラスやモジュール、モンキーパッチなどを、別の空間(Box)として分離し読み込み・実行できる革新的な機能となっています。たとえば、 something.rb に書かれたクラスを、メイン環境とは独立したBoxの中に読み込むイメージです。 # something.rb class Something def hello = " hello from Something " end # main.rb box = Ruby :: Box .new box.require( ' something ' ) s = box:: Something .new p s.hello # => "hello from Something" Boxの中で読み込んだ Something は box::Something としてだけアクセスでき、Boxの外からは見えません。これにより、ライブラリやモンキーパッチをプロセス全体へ漏らさずに使えるのがBoxのコアアイデアです。セッションの中では、以下のような用途がBoxの利用シーンとして挙げられていました。 一部のコードにて有効なモンキーパッチの適用 テスト内でのモック処理 異なるバージョンのライブラリに依存する複数のアプリケーションを、同じプロセス内に同居させる どちらにも同じリクエストを送ることで、異なるバージョンのライブラリでの挙動を比較する このように、Boxは「同じプロセス内での隔離された環境」を提供することで、これまでRubyで難しかったことを可能にする機能として期待されています。 Ruby Boxの仕組み 本セッションでは、Ruby Boxを実現する上で必要不可欠となる「現在、実行中のコードがどのBoxに属しているか」を判定する仕組みが詳細に解説されていました。 Ruby Boxでは、「1つのファイルを、別のBoxで同時に読み込む」といったことが可能です。つまり、ファイル自体は特定のBoxに縛られていないため、プログラムが実行されるたびに、Ruby自身が「自分は今、どのBoxの中で動いているのか?」を動的に特定し続けなければなりません。 Boxの特定・決定のプロセスは、以下のようなステップで行われているとのことでした。 現在実行中のフレームを特定する まず、自分がいま実行しているControl Frameを見つける LOCALな変数スコープを探す そこから、ブロックや例外処理といった非ローカルなものを無視し、ファイル全体、クラス、メソッドといった「LOCAL(局所的)」なスコープを持つフレームまでさかのぼる Boxの情報を引っ張り出す そこで、見つけたLOCALなフレームの中に保存されている情報をたどり、そこに紐付いている「Box情報」を取得する ここで一番興味深かったのは、Control Frameとローカル変数などを管理しているENVとの関係性です。それぞれはRubyを実行する上で、現時点でどのコードが動いているのかを把握するためのものです。Rubyのメモリ上では、Control FrameとENVの2つが両端から内側に向かって伸びていく特殊な構造となっているそうです。 この二つが衝突すると、Stack Overflowが発生するという話をこのセッションで初めて知り、今まであまり意識していなかったRubyの内部構造について、もっとより知って勉強したいと改めて感じました。 Ruby Boxが生まれた歴史 さらに、印象に残っているもう1つのトピックとして、Ruby Boxが生まれた歴史についての話があります。開発者のTagomoriさんがRuby Boxの開発を始めたきっかけは、RubyKaigi 2023で発表された 「Multiverse Ruby」のセッション でした。その内容が、過去に自身が抱えていた課題と重なると感じたことで、一気にモチベーションが高まったそうです。 さらに、その会期中に発表者の方と直接話す機会があり、当初は「Hako」という命名にして函館のRubyKaigiで発表しよう、という話もあったそうで…。そして実際に今、こうして発表に至っている、なんと胸が熱くなる展開でしょうか。 こんなエピソードを通して、「みなさんにも、ふとモチベーションが湧き上がる瞬間があるかもしれない。あなたにとっての『Multiverse Ruby』があるかもしれない。そんなプロセスも含めてRubyKaigiを楽しんでほしい」という言葉が印象的でした。最初のセッションでその一言を聞いたとき、これから始まる3日間のRubyKaigiにとてもわくわくしたあの瞬間を、今でも覚えています。そんな開発の裏エピソードも含め、とても印象的なセッションでした。 おわりに 本セッションは「新機能の紹介」というよりも、どんなふうに実現されているのか、さらにその機能が生まれた瞬間を聴ける発表で、聴いた後の余韻が長く残るキーノートでした。 WEARのバックエンドはRuby on Railsで開発・リプレイスを進めており、monkey patchが絡む箇所のスコープ管理に苦労する場面が今後出てくるかもしれません。Boxのように「実行時の境界」を表現できるようになれば、たとえばライブラリの差し込みやテスト環境固有の上書きを、より見通しよく扱えるようになる可能性を感じました。Ruby Boxはまだ実験的な機能ではありますが、今後も引き続き注目していきたいです! ZOZOブースの紹介 こんにちは。Developer Engagementブロックの wiroha です。ここからはZOZOや各社の協賛ブースの様子を紹介します。 今回は出発当日の朝、管制トラブルの影響で飛行機の運航に乱れが出ている状況からのスタートとなりました。搭乗予定の便も遅延か欠航かの見通しが立たなかったため、早々にキャンセルして新幹線へ切り替えたことが功を奏し、4〜5時間かかったものの比較的早めの時間に函館入りできました。また直前の地震の影響で北海道では配送の遅延が起きていたようですが、幸いブースの荷物は無事に届いており、予定どおり運営できそうでほっとしました。 今回のブースはファッション×AIの2つの新しい体験をご用意しました。ひとつは「 Apps in ChatGPT 」、もうひとつはWEARアプリ内の「 着回し提案 」機能です。 ファッション×AIの新機能を体験しよう! 「 Apps in ChatGPT 」の体験では、技術カンファレンスに合うコーディネートを相談する方や、自分の写真を撮って「もっとおしゃれにするにはどうしたらいい?」と質問する方など、それぞれ自由な対話を楽しんでいる様子が見られました。 「Apps in ChatGPT」を体験中の様子 「 着回し提案 」は特定のアイテムを選択すると、ファッションに特化したAIがユーザーの好みに合わせた着回しを提案します。「知らなかった、便利そう!」という感想を多くいただきました。 「着回し提案」を体験中の様子 いずれかの体験をしていただいた方に、ZOZOらしいノベルティの「 洗濯ネット 」をプレゼントしました。「ちょうど今日洗濯しようと思ってました」「洗濯ネットが欲しくて体験しに来ました」といった声があり、日常的に使えるアイテムとして多くの方に喜んでいただけました! ZOZOらしいノベルティの洗濯ネット 協賛企業ブースのコーディネートまとめ assu_ です。函館へはすんなり辿り着く──はずでした。伊丹空港で突如届く欠航の知らせ、満席の函館便、迫られる決断。滑り込んだ新千歳行き、やけくそで高い寿司を買い、特急北斗で函館へ。家を出てから実に13時間、極寒の函館に到着──虚無の心を救ったのは、函館駅前のハセガワストア。波乱の移動を経て辿り着いた会場には、同じように大変な思いをした人も少なくありませんでした。 そんな会場から、前回の RubyKaigi 2025 同様、素敵なコーディネートの協賛企業の皆様を撮影させていただきました! デザインスポンサーのGaji-Laboさん Committer限定のMA-1は、希少性と刺繍の2026デザインがとてもクール。 北海道らしいイラストをあしらったフーディーの note さん。 パンダをモノクロにすると北海道らしくなったという GMO Flatt Security さん。 CM でおなじみのキャラクターにより個性的なデザインの ファインディ さん。 シンプル胸元とカタカナ背中の対比が良い、ネットプロテクションズさん。 襟シャツに絞り付きの裾、男性も女性も着こなし上手だったギフティさん。 ロゴのラインが今っぽい、視認性が高くセンスの良いWEDさん。 誇り高きデザインにより、長い Ruby歴 を表しているKOMOJUさん。 桜が満開の函館に一番ぴったりなカラーと、法被で華やかなwithさん。 Wellness Sponsorらしいブラックで “ W ”orkoutしやすそうなhacomonoさん。 北海道・函館らしいデザインや、変化する気温に対応しやすそうな形が目立った回でしたね。お忙しい中ご協力いただいたブースの皆さん、本当にありがとうございました! RubyKaigi 2026 アフターイベント〜初参加LT・スポンサー4社のパネル〜を開催します RubyKaigi 2026 アフターイベント〜初参加LT・スポンサー4社のパネル〜 5月13日(水)にRubyKaigi 2026スポンサー企業の株式会社ZOZO、株式会社リブセンス、株式会社TOKIUM、株式会社マイベストでRubyKaigi 2026のアフターイベント「 RubyKaigi 2026 アフターイベント〜初参加LT・スポンサー4社のパネル〜 」を開催します。 RubyKaigi 2026に参加した方も、参加できなかった方も、ぜひお気軽にご参加ください! mybest.connpass.com おわりに 世界中のRubyistが函館の地に集まりました! ZOZOは毎年RubyKaigiに協賛し、ブースを出展しており、今年も多くの方々との交流を通じて有意義な時間を過ごすことができました。実行委員会の皆様、そして温かく迎えてくださった函館市の皆様に感謝申し上げます。来年も再び素晴らしい時間を共有できることを楽しみにしております! 技術カンファレンスでは恒例のスポンサーパネルへのサイン! RubyKaigi 2027 #rubykaigi 🗓️ 14..16 Apr 2027 🥭 Miyazaki, Japan — RubyKaigi (@rubykaigi) 2026年4月24日 そして、次回の開催地は私たちZOZOの宮崎オフィスがある宮崎県です。宮崎在住エンジニアも数名在籍しているので、地元を生かしたイベントなどができると良いですね。それではまた来年、RubyKaigi 2027でお会いしましょう。現場からは以上です! ZOZOでは、来年のRubyKaigi 2027を一緒に盛り上げるエンジニアを募集しています。ご興味のある方は以下のリンクからご応募ください。 corp.zozo.com


















