Findyの爆速開発を支える生成AI活用 ~MCPサーバー作成編~

こんにちは。

ファインディ株式会社 で Tech Lead をやらせてもらってる戸田です。

現在のソフトウェア開発の世界は、生成AIの登場により大きな転換点を迎えています。

GitHub Copilotやチャットベースの開発支援ツールなど、生成AIを活用した開発支援ツールが次々と登場し、開発者の日常的なワークフローに組み込まれつつあります。

そのような状況の中で、MCPというプロトコルが話題となっていることは読者の皆さんもご存知かと思います。

そこで今回は、弊社の開発組織でのMCPサーバーの導入と実装、そして実績について紹介します。

それでは見ていきましょう!

MCPとは

MCP(Model Context Protocol)は、アプリケーションが大規模言語モデル(LLM)に情報やツールへのアクセス方法を提供する、新しいオープンプロトコルです。

USB-Cが様々なデバイスを標準的な方法で接続するように、MCPはAIモデルを多様なデータソースやツールへつなぐための、標準化された方法を提供します。

これがこれまでのやり方、特に従来のAPIとどう違うのかというと、AI連携の開発を大幅に簡素化し、より動的な機能を提供する点にあります。従来は、各サービスに対して個別のコードや設定が必要で、「それぞれのドアに個別の鍵が必要」な状況に似ていました。しかし、MCPは単一の標準化されたコネクタとして機能するため、一度の統合で複数のツールやサービスにアクセスできる可能性があります。

詳しくは次の公式ドキュメントをご覧ください。

modelcontextprotocol.io

このMCPの規格に則って作成されたサーバーをMCPサーバーと呼びます。

導入

弊社でも例に漏れず、MCPサーバーの利用を開始しました。

Model Context Protocol servers で紹介されている公式MCPサーバーを開発リポジトリに追加するだけで、すぐに利用開始できます。

github.com

GitHubのMCPサーバーを使って、Pull requestやIssueの情報を取得して、そのままLLMに渡すことができます。

github.com

SentryのMCPサーバーを使って、不具合の詳細を取得して、そのままLLMに渡すことが可能です。その情報をそのままCopilotやAgentなどに解析してもらい、原因を特定した後に自動的に実装コードを修正してもらうことが出来ます。

github.com

AWSのDocumentのMCPサーバーを使うことで、CopilotなどからAWSについての質問を投げて、ドキュメントの内容を検索して返してもらうことができます。その内容はそのままLLMに渡すことも可能です。

github.com

利用方法はとても簡単です。各種AIエージェントの設定ファイルに追記して、MCPサーバーを起動するだけです。

例えばVSCodeでGitHubのMCPサーバーを利用する場合、次のような記述を追加します。

{
  "servers": {
    "github": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "GITHUB_PERSONAL_ACCESS_TOKEN",
        "ghcr.io/github/github-mcp-server"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_ACCESS_TOKEN>"
      }
    }
  }
}

このように、既に多くのプロダクトやサービスでMCPサーバーは公開されており、開発者やプロダクト開発の生産性を向上させるための強力なツールとなっています。

実装

各社から提供されているMCPサーバーを活用していると、組み合わせ次第で色々なことが出来ることに気づきます。

そこで弊社の開発組織で活用できるようなMCPサーバーを自分たちで作ることにしました。

公式から開発用のSDKが提供されているのですが、今回は弊社が普段から使っているTypeScriptのSDKを使って実装しています。

github.com

公式のSDKを使うことで、MCPサーバー内部の実装に集中することが出来るので、利用しない手はありません。

次のコードは、与えられた2つの数値に対して計算を行い、その結果を返すMCPサーバーの実装例です。

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const mcpServer = new McpServer({
  name: "Sample MCP Server",
  version: "0.0.1"
});

mcpServer.tool(
  "addition",
  "足し算をする",
  { a: z.number(), b: z.number() },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }]
  })
);

mcpServer.tool(
  "multiplication",
  "掛け算をする",
  { a: z.number(), b: z.number() },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a * b) }]
  })
);

try {
  const transport = new StdioServerTransport();
  await mcpServer.connect(transport);
} catch (error) {
  console.error('Error starting server:', error);
  process.exit(1);
}

mcpServer.tool でtoolを追加します。

1つ目の引数でtoolの名前、2つ目の引数でtoolの説明、3つ目の引数でtoolのパラメータ、4つ目の引数でtoolの実装を定義しています。

また、用途や内容によってはMCPサーバーそのものを分けたいケースが出てきます。ですが、そのたびにリポジトリを切ってしまうと、同じような処理のコードを分散管理することになってしまいます。

そのため弊社では、以前から活用していたNxを利用して、MCPサーバーの実装をモノレポで管理するようにしました。

tech.findy.co.jp

モノレポにすることで、複数のMCPサーバーから利用できる共通関数などを作成できます。外部APIを実行するClientなどが良い例です。このように弊社ではMCPサーバーの社内エコシステムを実現しています。

更にNxのgenerator機能を使うことで、モノレポとMCPサーバーの初期設定はコマンドを数回実行するだけで完了するようにしています。

Nxのgenerator機能については以前の記事で紹介しておりますので、併せてご覧ください。

tech.findy.co.jp

このような整備を行うことで、社内のエンジニアがMCPサーバーを作るハードルを下げることができました。

実績

社内エコシステムの整備を行ったことにより、数名のエンジニアで、3日間で10個のMCPサーバーと30個のtoolを実装して、社内のエンジニアに配布することを実現できました。

今回はその中の一部を紹介します。

動的にプロンプトのテキストを作成して返す

パラメータを受け取り、その内容に応じて動的にプロンプトのテキストを生成して返すMCPサーバーを実装しました。

開発チーム内で共通で利用したいプロンプトがあり、内容を動的に切り替えたい場合などに便利です。

例えば、他のMCPサーバーからデータを取得して、そのデータをこのMCPサーバーに渡すことで、外部データのテキストを利用してプロンプトを生成するといったことが実現できます。

このように、プロンプトのテキストを返し、それをそのままLLMで実行するような使い方がMCPサーバーでは可能です。

Devinと連携する

完全自律型AIエンジニアのDevinは、α版ではありますがAPIを公開しています。

docs.devin.ai

そのAPIをMCPサーバーから実行することにより、Devinに関する情報を取得したり、セッションを作成、停止できるようにしました。

これによりエンジニア自身の作業中に、SlackやDevinの画面を直接確認せずともDevinの管理ができるようになりました。

このように、外部APIを実行して情報を取得したりデータ登録、削除を行うことがMCPサーバーでは可能です。

Figmaデータのlintを行う

デザインプラットフォームのFigmaはAPIを公開しています。

www.figma.com

そのAPIをMCPサーバーから実行して、対象のFigmaのデータを取得して、データの中身をチェックして、デザインルールに則っているかどうかlintのようなチェックを行うMCPサーバーを実装しました。

例えば、指定したフォント以外を使っている場合や、利用できるpx数ではない場合など、デザインルールに則っていない場合は警告を返すようなことが可能です。

このように、外部APIからデータを取得して、内容を解析したり加工して返すようなことがMCPサーバーには可能です。

セキュリティ面の考慮

外部APIを実行する場合、アクセストークンが必要になるケースが多いと思います。

このようなケースの場合、MCPサーバーを実行する際に環境変数を使ってアクセストークンを渡すことができます。

利用するアクセストークンの権限を絞り、環境変数でアクセストークンを隠蔽して実行することで、安全にMCPサーバーを管理、実装することが可能になります。

また、外部の公式以外のMCPサーバー、いわゆる野良MCPサーバーを利用する場合は注意が必要です。

悪意のあるMCPサーバーに対してアクセストークンなどを渡してしまうと、意図しない情報漏洩やデータの改ざんなどのリスクがあります。

そのため、外部の野良MCPサーバーを利用する場合は、内部実装を確認して実際に実行されている処理を確認することをオススメします。

まとめ

いかがでしたでしょうか?

MCPサーバーを実装するハードルとコストは非常に低く、それに対するリターンは非常に大きいため、コストパフォーマンスが非常に高いです。

是非一度、公式ドキュメントを参考にして簡単なMCPサーバーを実装してみてください。

現在、ファインディでは一緒に働くメンバーを募集中です。

興味がある方はこちらから ↓ herp.careers

【ソフトウェア開発現代史】 DevOpsを形づくった人々 〜Kent Beck氏、Gene Kim氏の来日に寄せて〜

このブログの内容をポッドキャストでも配信中!


https://cdn-ak.f.st-hatena.com/images/fotolife/T/Taka_bow/20250512/20250512172219_original.png

ソフトウェア開発現代史年表 Ver2.07

はじめに

こんにちは。ソフトウェアプロセス改善コーチ兼アジャイルコーチ兼エンジニア兼Findy Tech Blog編集長の高橋(@Taka-bow)です。

30年以上前、エンジニアとしての第一歩を踏み出したばかりの私に、ある先輩が教えてくれた言葉があります。

「動いているコードには触れるな」

この言葉は、当時の日本のIT現場における「安定性最優先」の価値観を象徴しています。一度リリースされたシステムは「完成品」として扱われ、必要最低限の変更以外は避けるべきという考え方が主流でした。変更は常にリスクをはらんでおり、「動いているものを変えれば壊れる可能性がある」という恐れが、技術的な進化よりも優先されていたのです。

しかし、DevOpsの登場によって、この価値観は大きく変わりました。「変更を恐れる」文化から「変更を受け入れる」文化へ。「安定性のために変更を避ける」から「頻繁な小さな変更によって安定性を高める」へと変化しました。

つまり「動いているコードには触れるな」から「常に改善し続けるコード」へと進化したのです。

この価値観の転換は、単なる技術的な進化ではなく、ソフトウェア開発に対する根本的な考え方の変革です。

世界的には当たり前となりつつあるこの変革ですが、日本の状況はどうでしょうか。興味深いことに、日本では一部のWeb系企業やスタートアップを除き、DevOpsの波はまだ十分に広がっていません。

なぜでしょう?

この疑問を探る前に、まずDevOpsがどのように生まれ、発展してきたのかを理解する必要があります。ソフトウェア開発現代史の視点から、この変革をもたらした先駆者たちの物語を紹介していきたいと思います。

下図の年表に示すように、DevOpsの歴史は2009年頃から始まります。

DevOps誕生以前(〜2000年代前半)

2000年代前半までのソフトウェア開発では、開発と運用は完全に分断され、しばしば対立関係にありました。開発チームの目標は「新機能を素早くリリース」、運用チームの目標は「システムの安定性維持」であり、この2つの目標はしばしば衝突していました。

  • 開発チームは「とにかく早くリリースしたい」 → 変更を加えたがる
  • 運用チームは「安定稼働が最優先」 → 変更を嫌う
  • 結果として、開発は「リリース遅延」に苛まれ、運用は「障害対応」に追われる

変更のリスクを最小限にするために、厳格なリリースプロセスが敷かれ、開発者は運用チームから「勝手にデプロイするな」と釘を刺されていました。その結果、リリースサイクルは長期化し、開発の俊敏性が犠牲になっていったのです。

特に、大規模なリリースでは手作業によるデプロイが行われることが多く、設定ミスや環境差異が原因で障害が頻発していました。こうした問題を解決し、開発と運用のギャップを埋める新たなアプローチが強く求められていました。

2009年:DevOpsのはじまり

Flickrの伝説的講演「10+ Deploys Per Day」

こうした状況に一石を投じたのが、2009年のO'Reilly Velocity 09 Conferenceで行われた、Flickrのエンジニアによる伝説的な講演です。当時Flickrのエンジニアだったジョン・オールスパウ(John Allspaw)とポール・ハモンド(Paul Hammond)が「10+ Deploys Per Day: Dev and Ops Cooperation at Flickr」のタイトルで発表し、業界に衝撃を与えました。

この講演で彼らは、当時としては驚異的な「1日に10回以上のデプロイ」を実現している事実を明らかにしました。多くの企業が月に1回や四半期に1回のリリースサイクルに苦しんでいた時代に、これは革命的な実践だったのです。

彼らが強調したのは次のポイントです。

  1. 開発と運用の協力関係 - 両チームが共通の目標(ユーザー価値の提供)に向かって協力する
  2. 小さな変更の頻繁なデプロイ - 大きな変更を一度にリリースするのではなく、小さな変更を頻繁にリリースする
  3. 自動化の徹底 - テスト、デプロイ、モニタリングなど、あらゆる工程を自動化する
  4. 「失敗は避けられない」前提 - 失敗を防ぐのではなく、素早く検知して回復する能力を高める

この講演により「高頻度デプロイは可能」との認識が広まり、多くの企業がFlickrの実践に触発されて自社のデプロイプロセスを見直すきっかけとなりました。この講演は、YouTubeで「10+ Deploys Per Day: Dev and Ops Cooperation at Flickr」として今でも視聴可能であり、DevOpsを学ぶ人々にとって必見の資料となっています。

このとき日本では:アジャイル導入の緩やかな歩み 2001年に発表された「アジャイルソフトウェア開発宣言(アジャイルマニフェスト)」は、「プロセスやツールよりも個人と対話を」「包括的なドキュメントよりも動くソフトウェアを」といった価値観を掲げ、後のDevOps誕生の重要な思想的背景となりました。しかし、2000年代前半のアジャイル導入期において、日本企業の多くはこの本質である「変化への適応」や「顧客との協働」といった考え方を十分に受け入れることができませんでした。日本の階層的な組織構造や詳細な計画を重視する文化が、アジャイルの柔軟性と相容れなかったのです。DevOpsはアジャイルの延長線上に生まれた考え方であり、この最初のアジャイル導入のチャンスを逃したことが、後のDevOps導入の遅れにも直接的な影響を与えることになりました。

パトリック・ドボアと「DevOps」という言葉の誕生

Flickrの講演と同じ2009年、「DevOps」という言葉が誕生しました。この言葉の背後には、ベルギーのITコンサルタントパトリック・ドボア(Patrick Debois)の興味深い物語があります。*1

実は、ドボアの旅は2007年に始まっていました。ベルギー政府のデータセンター移行プロジェクトに携わっていた彼は、開発者とシステム管理者の間の対立に悩まされ、解決策を模索していました。

転機となったのは2008年8月、トロントで開催されたAgileカンファレンスでした。ソフトウェア開発者のアンドリュー・シェーファー(Andrew Shafer)が「Agile Infrastructure(アジャイルインフラストラクチャ)」というセッションを開催しましたが、参加者はたった一人、パトリック・ドボアだけでした。二人はこの出会いをきっかけに、開発と運用の間の壁を取り払うアイデアについて議論を始めました。

そして2009年、先述のFlickrの講演「10+ Deploys Per Day」に触発されたドボアは、エンジニアのナスラット・ナサー(Nasrat Nassar)からの「ベルギーで独自のVelocityイベントを開催してはどうか」というツイートをきっかけに行動を起こします。彼は「DevOpsDays」というイベントを開催することを決意しました。

この名前は、「development(開発)」と「operations(運用)」の最初の3文字を取り、「days(日)」という単語を追加したものでした。2009年10月30日に開催されたこのカンファレンスには、開発者、システム管理者、ツール開発者など多様な参加者が集まりました。

カンファレンス終了後、議論はTwitterに移り、より覚えやすいハッシュタグとして、ドボアは名前を「#DevOps」に短縮しました。これが「DevOps(Development + Operations)」という言葉の誕生であり、それ以来、このムーブメントはDevOpsとして知られるようになりました。

興味深いことに、スペルについては今でも意見の相違があります。主流の使用法は「DevOps」ですが、創始者のドボアを含む少数派は「Devops」を主張し、一部の人々は大文字を完全に排除して「devops」とすることを主張しています。

ドボアのX(当時はTwitter)アカウント(@patrickdebois)は初期のDevOpsムーブメントの中心的な情報源となり、#devopsというハッシュタグを通じて、世界中の実践者がアイデアを共有する場となっていきました。

このとき日本では:クラウド導入の機会損失 2006年から2010年にかけてのクラウド黎明期において、AWSをはじめとするクラウドサービスの登場は、単なるインフラの選択肢ではなく、DevOpsの実践を加速させる技術的基盤でした。「Infrastructure as Code」や「自動化」といったDevOpsの核心的な実践を可能にする環境が整いつつあったのです。しかし、日本企業の多くは「セキュリティ懸念」「既存システムへの投資保護」「クラウドの信頼性への疑問」などを理由に慎重な姿勢を取り続けました。この時期に積極的なクラウド活用を進めていれば、DevOpsへの移行も自然な流れとして実現できたはずです。この「待ちの姿勢」が、後のDevOps導入の遅れにも繋がることになりました。

なお、パトリック・ドボアは昨年2024年1月に行われたDevOpsDays Tokyo 2024の基調講演のため来日しています。

私は現地でお話を聞きましたが、このときのドボアはAIにとても興奮しており、特にRAGについてのお話でした。

当時の私は、ドボア氏が何に興奮していたのか、何も分かっていませんでした……でも、今なら分かります。

改めてビデオを見返して、気になった発言をピックアップしました。

私がとても気に入っている概念のひとつに、こういう考え方があります。
エージェントの性能が向上すれば、私たちは直接「もの」を作るのをやめ、
「ものを作るもの」を作るようになります。
つまり、自動化の次の段階に進むのです。


多くの人が私にこう言いました。
「DevOpsでは乗り遅れた。でも今度のAIの波では遅れたくない。」
成熟したDevOps文化を持つ人々こそ、GenAIのアーリーアダプターになりつつあるのです。


私たちエンジニアは、常にやりすぎる傾向があります。
でも、それでいいのです。
今もっとも難しい新しいプログラミング言語?
それはもしかすると「英語」か「日本語」かもしれませんね。


もっと、たくさん痺れる事を仰っているので、ぜひ本編YouTubeをごらんください。

2010年代前半:ジェズ・ハンブルと継続的インテグレーション(CI)から継続的デリバリー(CD)への発展

さて、話を戻しましょう。ここで登場するのが、DevOpsの歴史において重要な役割を果たした人物、ジェズ・ハンブル(Jez Humble)です。彼はソフトウェアデリバリーの自動化と効率化に関する先駆的な研究と実践で知られ、継続的デリバリー(CD)の概念を体系化した第一人者として世界中のソフトウェア開発に多大な影響を与えました。

DevOpsの言葉が広まり始めた2010年代前半、まず理解しておくべき重要な概念が「継続的インテグレーション(Continuous Integration, CI)」です。CIは実はDevOpsより前から存在していた概念で、2000年代初頭にアジャイル開発の文脈で広まりました。

特にマーティン・ファウラー(Martin Fowler)ケント・ベック(Kent Beck)がその重要性を提唱しています。

継続的インテグレーション(CI)とは何か

継続的インテグレーションの核心は、「開発者が頻繁にコードを統合し、自動テストによって問題を早期に発見する」という実践です。具体的には次のような特徴があります。

  • 頻繁なコミット - 開発者は少なくとも1日1回、コードをメインブランチに統合する
  • 自動ビルド - コードがコミットされるたびに自動的にビルドが実行される
  • 自動テスト - ビルド後に自動テストが実行され、問題があれば即座にフィードバックが得られる
  • 問題の早期発見 - 統合の問題を早期に発見することで、修正コストを低減する

CIの実践により、「統合地獄(Integration Hell)」と呼ばれる、長期間分岐したコードを統合する際の困難を避けることができます。CIを実現するツールとしては、Jenkins(旧Hudson)、Travis CICircleCIなどが広く使われるようになりました。

なお、CI(継続的インテグレーション)と密接に関係する実践として、TDD(Test-Driven Development:テスト駆動開発)があります。TDDは「テストを先に書き、そのテストをパスする最小限の実装を行う」という開発手法で、Kent Beckがエクストリーム・プログラミング(XP)の中で体系化し、広く普及させました。

TDDによって作成される自動テスト群は、CIパイプラインにおける重要な構成要素となり、コードの品質を継続的に保証する基盤となります。TDDが主にコードレベルの品質を担保するのに対し、CIはチーム全体での統合品質を確保する役割を果たします。両者はいずれも「早期フィードバック」と「問題の早期発見」を重視しており、DevOps実践の基盤として不可欠な考え方です。

継続的デリバリー(CD)への発展

CIが「コードの統合と検証の自動化」に焦点を当てているのに対し、継続的デリバリー(CD)はその先の「デプロイメントまでの自動化」にまで範囲を広げた概念です。この概念を体系化したのが、ジェズ・ハンブルとデビッド・ファーリー(David Farley)による「Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation」(2010年、日本語訳は2012年)の書籍でした。

この書籍では、「継続的デリバリー(Continuous Delivery, CD)」の概念が体系的に説明され、その核心は「ソフトウェアをいつでも本番環境にリリース可能な状態にする」ことにありました。

ジェズ・ハンブルとデビッド・ファーリーは、この書籍で継続的デリバリーを実現するために次のような実践を提唱しています。

  • デプロイメントパイプライン - コードの変更が自動的にビルド、テスト、デプロイされる一連のプロセス
  • 自動化されたテスト - 単体テスト、統合テスト、受け入れテストなど、あらゆるレベルのテストを自動化
  • 本番環境と一致したステージング環境 - 「開発環境では動くのに本番では動かない」問題を解消
  • ブルーグリーンデプロイメント - ダウンタイムなしでのデプロイを可能にする手法

これらの実践は現代のCI/CDパイプラインの基盤となり、ジェズ・ハンブルとデビッド・ファーリーの貢献によって「継続的インテグレーション(CI)」から「継続的デリバリー(CD)」へと、自動化の範囲が大きく拡大していきました。

このとき日本では:継続的デリバリー(CD)とゲートキーパー文化の相克 2010年代前半には、継続的デリバリー(CD)の波が世界中に広がりました。海外では多くの企業がデプロイパイプラインを構築し、頻繁なリリースを実現していました。一方、日本企業の多くは「手動リリース」や「長いリリースサイクル」、「厳格な承認プロセス」を維持し続ける傾向がありました。これは日本特有の品質管理への強いこだわりや、変更に対する慎重な姿勢が影響していると考えられます。「リリース=大イベント」「変更=リスク」という認識が根強く、DevOpsの核心である「小さな変更を頻繁に行う」文化への移行が進みにくい土壌があったのです。特に、多くの組織では品質保証部門が「ゲートキーパー」として機能し、リリース前の最終承認権を持つ構造が確立されていました。このゲートキーパー型の品質保証プロセスは、継続的デリバリーが目指す「自動化されたテストによる品質担保」という考え方と根本的に相容れず、プロセス重視の文化から脱却することが難しかったのです。

2010年代中盤:ジーン・キムとDevOpsの理論体系化

2010年代中盤、DevOpsムーブメントの中心人物として台頭したのがジーン・キム(Gene Kim)です。彼は優れた技術者であると同時に、複雑な概念を分かりやすく伝える稀有な才能を持ち合わせていました。それまで現場レベルの実践知として広がっていたDevOpsの考え方を、組織全体で取り組むべき体系的な方法論として確立した功績は計り知れません。彼の著作は技術書の枠を超え、多くの組織でDevOps変革の指南書となっています。

2013年、ジーン・キムは、ケビン・ベア(Kevin Behr)、ジョージ・スパッフォード(George Spafford)と共に「The Phoenix Project: A Novel about IT, DevOps, and Helping Your Business Win(邦題:The DevOps 逆転だ!)」を出版しました。

この小説形式の書籍は、架空の自動車部品メーカー「Parts Unlimited」のIT部門が、DevOpsの原則を採用することで危機を乗り越える物語を描いています。物語形式でありながら、DevOpsの本質的な考え方を分かりやすく伝え、多くの読者に影響を与えました。

続いて2016年には、ジェズ・ハンブル、パトリック・ドボア、ジョン・ウィリス(John Willis)と共に「The DevOps Handbook: How to Create World-Class Agility, Reliability, and Security in Technology Organizations」を出版し、DevOpsの実践をより体系的にまとめあげました。

この書籍では、DevOpsの三つの原則(Three Ways) が提唱されています:

  1. フロー(Flow) - 開発からデプロイまでの流れを最適化する
  2. フィードバック(Feedback) - 問題を早期に発見し、素早く修正する
  3. 継続的学習と実験(Continual Learning and Experimentation) - 失敗から学び、継続的に改善する

ジーン・キムの貢献により、DevOpsは単なる技術的プラクティスではなく、組織文化の変革として広く認識されるようになりました。

このとき日本では:DevOpsの理論と技術基盤の受容の遅れ ジーン・キムらがDevOpsの理論を体系化していた2013年以降、世界ではマイクロサービスアーキテクチャが注目され、Dockerの登場によりコンテナ技術が普及し始めました。これらの技術は、DevOpsの三つの原則(フロー、フィードバック、継続的学習)を実践するための技術的基盤でしたが、日本企業の多くはモノリシックなアーキテクチャと従来のデプロイモデルを維持し続けました。マイクロサービスへの移行には技術的な挑戦だけでなく、組織構造の変革(「2ピザチーム」など小規模で自律的なチーム編成)も必要でしたが、日本の階層的な組織文化はこうした変革に適応しにくい面がありました。「The Phoenix Project」や「The DevOps Handbook」といった書籍は日本語にも翻訳されましたが、その思想を実践に移す組織はWeb系企業の一部に限られていました。

2010年代後半〜現在:DevOpsの普及と進化

2010年代後半になると、DevOpsの考え方は業界全体に広がり、様々な形で進化していきました。

Googleによる「SRE(Site Reliability Engineering)」の提唱

2016年、Googleは「Site Reliability Engineering: How Google Runs Production Systems」の書籍を出版し、「SRE(Site Reliability Engineering)」の概念を広めました。

SREはDevOpsの思想を具現化し、システム運用と開発の連携を深めるための実践的なアプローチです。具体的には、ソフトウェアエンジニアリングの考え方をインフラや運用に適用し、システムの信頼性を向上させることを目的としています。

「You build it, you run it(作ったものは自分で運用する)」文化の定着

Amazonのエンジニアリング文化から生まれた「You build it, you run it(作ったものは自分で運用する)」の考え方もこの時期に広く普及しました。これは開発者が自ら作ったシステムの運用にも責任を持つ考え方で、開発と運用の境界をさらに曖昧にするものです。この文化の下では、開発者はコードを書くだけでなく、デプロイ、モニタリング、障害対応など、システムのライフサイクル全体に関わることが求められるようになりました。

DORA(DevOps Research and Assessment)の設立と影響

Dr. Nicole Forsgren (開発生産性Conference 2024にて)

2016年、ニコール・フォースグレン(Nicole Forsgren)ジェズ・ハンブル(Jez Humble)らによって「DORA(DevOps Research and Assessment)」が設立されました。DORAはDevOpsの実践と組織のパフォーマンスの関係を科学的に調査・分析する組織として、毎年「State of DevOps Report」を発行し、世界中の何千もの組織からデータを収集・分析することで、DevOpsの実践がビジネス成果にどのように影響するかを明らかにしてきました。

特に、「デプロイ頻度」「変更のリードタイム」「変更失敗率」「サービス復旧時間」 の4つの主要指標(Four Key Metrics)を確立し、これらがビジネスパフォーマンスと強い相関関係にあることを示しています。

2018年には、ニコール・フォースグレン、ジェズ・ハンブル、そしてジーン・キムによって書籍「Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations(邦題:LeanとDevOpsの科学)」が出版され、DORAの研究成果が体系的にまとめられました。この書籍によってDevOpsの実践は「科学」として確立される重要な一歩を踏み出しました。

DORAの10年にわたる研究と進化については、2025年4月に開催されたDevOpsDays Tokyo 2025にて私がお話した資料で詳しく解説していますので、そちらもぜひご参照ください。

DORAの研究は、DevOpsの実践が単なるトレンドではなく、科学的に裏付けられた効果的なアプローチであることを証明する上で、非常に重要な役割を果たしています。

クラウドネイティブ技術とDevOpsの融合

AWS、Google Cloudなどのクラウドサービスの活用が拡大し、「クラウドネイティブ」な開発が注目され始めました。これを支える技術として、コンテナ技術(Docker)やコンテナオーケストレーション(Kubernetes)が普及し、インフラとアプリケーションの管理がより柔軟かつ自動化されるようになりました。これらの技術はDevOpsの実践をさらに加速させる役割を果たしています。特に「Infrastructure as Code(IaC)」の考え方が広まり、インフラストラクチャの構成もコードとして管理されるようになりました。これによりインフラの変更も開発プロセスの一部として扱われるようになり、開発と運用の統合がさらに進みました。現代ではTerraform、AWS CloudFormation、Ansible、Puppet、ChefなどのIaCツールが広く使われるようになっています。

DevOpsの文化的側面の重視

技術的な進化と並行して、DevOpsの文化的側面も重視されるようになりました。成功したDevOps組織では次のような文化的特徴が見られます。

  • 心理的安全性 - 失敗を非難せず、学びの機会として捉える文化
  • 実験と学習の奨励 - 小さな実験を繰り返し、継続的に改善する姿勢
  • 部門間の協力 - 開発、運用、セキュリティなど、従来は分断されていた部門間の壁を取り払う
  • 共有責任 - 「これは私の担当ではない」考え方ではなく、全員がプロダクトの成功に責任を持つ

これらの文化的側面がなければ、いくら優れたツールを導入しても、DevOpsの真の価値を実現できないという認識が広まっています。

DevOpsの本質と今後の展望

2009年に始まったDevOpsの旅は、今や業界標準となる考え方を生み出しました。現在、多くの開発チームがCI/CDを導入し、自動化されたデプロイパイプラインを運用しています。開発者がコードをコミットすると、すぐにテストが走り、数分後には本番環境へデプロイされるようになりました。リリースのたびに夜を徹して作業したり、「障害対応のために今すぐオペレーションチームを呼べ」と叫んだりする文化は、徐々に過去のものになりつつあります。

しかし、DevOpsの本質は単なる「自動化」ではありません。開発と運用の対立を超え、継続的に改善し続ける組織文化を作ることこそがDevOpsの本質なのです。パトリック・ドボア、ジェズ・ハンブル、ジーン・キムをはじめとする先駆者たちが伝えたかったのは、ツールや技術の導入ではなく、根本的な組織文化の変革でした。

これからの開発者は、ツールやプロセスだけでなく、組織全体の在り方に目を向ける必要があります。 そして、彼らが築いたこの文化をさらに進化させていくことが求められているのです。

日本におけるDevOps:まだ「一部の人たちのモノ」状態

DevOpsは世界的に広がりを見せていますが、日本においては普及のペースが異なる面もあります。

例えば、AgileやScrumのイベントと比べるとDevOpsDays Tokyoの参加規模や盛り上がりが海外の同様のイベントと比較して小さいことからも、日本特有の課題があることがうかがえます。

また、2018年10月には「ソフト高速開発のDevOps推進協議会が2年余で解散」というニュースも報じられました。

日本でDevOpsの導入が遅れている理由を構造的・文化的・歴史的背景から分析すると、次のような要因が浮かび上がってきます。

要因 説明
「開発」と「運用」の切り分けの曖昧さ 日本の多くの企業では、「システム部門」が開発・運用・保守のすべてを抱える体制が一般的でした。このため、DevOpsが解決しようとした「開発と運用の断絶」問題が顕在化していなかったのです。しかし、これは必ずしも良い状況ではありませんでした。役割は分かれていなくても、責任やナレッジの分担があいまいで属人的になりやすいという弊害がありました。つまり、「DevとOpsが分かれていない」状態が「連携している」状態を意味するわけではなかったのです。
受託開発構造と多重下請けによる分業文化 日本では、ユーザー企業 → SIer(元請)→ サブ → 孫 という多重下請け構造が見受けられます。この構造だと、開発はA社、運用はB社、保守はC社という分断が起きやすく、DevOpsの前提である「共通の目標と責任」が成立しにくい環境にあります。DevとOpsが物理的にも契約上も分断されているため、DevOps導入が困難な状況が生まれているのです。
製造業モデルの異なる解釈:工程主義・完璧主義 日本のITは製造業の品質管理思想の影響を強く受けていますが、その解釈に課題がありました。本来、トヨタ生産方式は「継続的改善(カイゼン)」や「小さな改善の積み重ね」を重視し、これはDevOpsの思想と共通しています(実際、DevOpsの「リーン」の考え方はトヨタ生産方式が起源です)。しかし、多くの日本のIT現場では、製造業の手法を「工程の厳格な管理」「完璧な品質の追求」という側面だけを取り入れ、「リリース=出荷」「不具合=欠陥品」という感覚が強くなりました。このため、頻繁なリリース(Continuous Delivery)や早期フィードバックといったDevOpsの実践が「リスク」だと認識される傾向があるのです。皮肉なことに、トヨタ生産方式の本質を正しく理解していれば、DevOpsへの親和性はむしろ高かったはずなのです。
インフラのアウトソーシング文化 データセンターやネットワーク運用を外部委託(SIer、DC事業者など)するのが主流で、自社にインフラ運用の知見が蓄積しにくい環境があります。クラウドネイティブな文化が根付く前は、Opsの自動化やSRE的観点がなじみにくかったのです。これにより、DevOpsの「Infrastructure as Code」「SRE」文化が根付きにくい土壌が形成されていました。
ITが"事業の中核"ではないという認識 海外(特に米国)では、2000年代以降「ITは競争力の源泉」と位置づけられていましたが、日本ではITが依然として「業務支援」「コストセンター」と見なされ、IT部門が戦略の中心になりにくい状況がありました。このため、DevOpsが本来持つ「ビジネス成果を最大化するための仕組み」としての意義が伝わりにくかったのです。

しかし、近年日本企業においてもDevOpsへの関心が高まっています。その背景には次のような変化があります。

  • クラウドネイティブの普及:AWS、Google Cloudなどのクラウドサービスの活用が拡大し、インフラのコード化や自動化の基盤が整いつつある
  • 内製回帰の動き:特にメガベンチャーやSaaS企業を中心に、システム開発の内製化が進み、DevOpsの実践がしやすい環境が生まれている
  • 開発生産性の重視:Developer Productivity向上への注目が高まり、CI/CDパイプラインの構築など、開発効率を高める取り組みが増えている
  • AI革命の波:ChatGPTをはじめとする生成AIの登場により、ソフトウェア開発および運用の在り方が根本から変化しつつある。コードの生成、テストの自動化、運用時のログ分析や障害対応といった領域でAIが実用段階に入り、従来の手作業中心のプロセスは再構築を迫られている。AIは単なる補助ツールではなく、開発サイクル全体に組み込まれる存在となりつつあり、その文脈においてDevOpsも再定義され始めている。

この変化は一様ではありません。新興企業やIT専業企業ではDevOpsの実践が進む一方、大企業・公共系・受託構造の現場では依然として課題が残っています。

DevOpsとAI導入の共通障壁

冒頭のパトリック・ドボアは、1年前のDevOpsDays Tokyo 2024に於いて「成熟したDevOps文化を持つ人々こそ、GenAIのアーリーアダプターになりつつある」と仰っており、事実そうなりました。

つまり、AI革命の波が押し寄せる中、DevOpsを受け入れられなかった組織は、同じ理由でAIの波にも乗り遅れる危険性があります。

日本でDevOpsを根付かせるためには、単なる「技術導入」ではなく「組織文化改革」として捉える必要があります。「DevOpsを導入する」=「ツールを導入する」ではなく、「職能や役割を超えてチームが協働する文化を育む」ことであり、この文化的転換が日本では特に重要です。

そのため、日本の組織構造を考慮した上で、DevOpsの本質を理解し、自分たちの組織に合った形で取り入れていくためには、次のようなアプローチが有効と思われます。

  1. 変化を恐れない文化の醸成

    • 「失敗は学びの機会」という認識を組織全体で共有する
    • 小さな変更から始め、成功体験を積み重ねることで自信をつける
  2. クロスファンクショナルチームの形成

    • 部門の壁を越えた協働を促進し、共通の目標に向かって取り組む環境をつくる
    • 開発・運用・ビジネス部門が一体となって価値を生み出す体制を構築する
  3. 長期的視点での投資判断

    • 短期的ROIだけでなく、競争力維持の観点から技術投資を判断する
    • 継続的な学習と実験の文化を育み、組織全体の適応力を高める

DevOpsの歴史は、技術の進化だけでなく、人々の協力関係や組織文化の変革の歴史でもあります。その本質を理解し、自分たちの組織に合った形で取り入れていくことが、現代のソフトウェア開発者に求められています。そして、日本の文化や組織構造に合ったDevOpsの形を模索していくことも、私たち日本のエンジニアの重要な課題なのです。


◆ AI時代の適応 ◆
AI時代は、変化に適応できる組織とそうでない組織の差がさらに拡大します。DevOpsの本質である「変化を恐れず、継続的に改善し続ける文化」は、この時代において競争力の源泉となります。
AIの導入に伴い、一部では大規模言語モデルのコストやセキュリティの観点から自社データセンターでのAI基盤構築が選択されています。しかし、これは単なるオンプレミスへの回帰ではなく、クラウドで培われたDevOps手法(IaC、コンテナ技術)を活用した新たな形態です。インフラの場所に関わらず、「自動化」「協働」「継続的改善」というDevOpsの価値はAI時代においてより重要になっています。
過去にDevOpsの波に乗り遅れた組織も、AI時代という転換点を変革の契機として活かすことができるのではないでしょうか。

【告知】ソフトウェア開発の伝説的パイオニアたちが来日!

2025年7月3日(木)・4日(金)9:30〜19:00、弊社主催「開発生産性Conference2025」にて、ソフトウェア開発の歴史を塗り替えた二人の巨匠が来日します。

アジャイルとDevOpsの世界的権威が同時来日する歴史的瞬間に、ぜひ立ち会ってください。

  • ケント・ベック(Kent Beck) — アジャイル開発の父、XPの創始者、TDDの提唱者。
  • ジーン・キム(Gene Kim) — DevOps革命の立役者、『The Phoenix Project』著者。

本ブログで紹介した「ソフトウェア開発の開拓者たち」が、目の前で語る貴重な機会です。彼らの思想と実践が世界のソフトウェア開発をどう変えたのか、そして日本のエンジニアが今後どう進むべきか—直接その声を聞ける、まさに一生に一度のチャンスです。

DevOpsの本質を理解し、AI時代を勝ち抜くための知恵を、開発現場の革命家たちから直接学びませんか?

参加登録はこちら開発生産性Conference2025公式サイト


ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。

*1: Edwards, D. (2014, May 16). The incredible true story of how DevOps got its name. New Relic. https://newrelic.com/blog/nerd-life/devops-name

RubyKaigi 2025レポート:Rubyで作成したスクリプトをバイナリにして簡単に配布できるか試してみた

こんにちは!Findy Team+開発チームでEMをしているhamです。

今年もRubyKaigi 2025に参加してきました。 私はコロナ後に三重県で開催されたときから4年連続で参加しているのですが、今年も興味深いセッションがたくさんあり、Rubyが着実に進化していることを感じることができました!

本記事では、その中の1つである「The Ruby One-Binary Tool, Enhanced with Kompo」で紹介された「Kompo」について、実際に試してみた結果と所感を紹介します。

Kompoとは

Kompoとは、READMEで「A tool to pack Ruby and Ruby scripts in one binary.」と紹介されている通り、Rubyスクリプトをバイナリにして配布できるツールです。 Rubyのスクリプトをバイナリにすることで、配布が容易になったり、実行環境にRubyのインストールが不要になるため配布先での実行が容易になります。

Kompoは2024年のRubyKaigiでも「It's about time to pack Ruby and Ruby scripts in one binary」のセッションで紹介されており、興味を持っていました。

2024年の時点ではRailsなどの大きなGemは実行できていないとのことだったのですが、2025年の発表ではRailsが動いており進化を感じました!

'Hello, world!'を返すスクリプト

今回のセッションでRailsが動作するようになったと発表されていたので、Webサーバーが動作するバイナリを作ってみることにしました。 とはいえ、最初から大きなものを作ろうとすると詰まる可能性が高いです。何事もスモールスタートが良いですね。

入門といえば 'Hello, world!'ということで、まずは'Hello, world!'を返却するRubyスクリプトでトライしました。

なお、ここからの内容は執筆時点(2025年4月)の情報なので最新版では変更されている可能性があります。

kompo-vfs

Kompoは内部で仮想ファイルシステムを使っているとのことです。

当初は既存のライブラリで実現できないか検討したとのことですが、Kompoのやりたいことにマッチするものがなかったとのことで「kompo-vfs」を自作したそうです。 リポジトリを見ていただければわかりますが、こちらはRustで書かれていました。

Kompoを使うにはまずkompo-vfsをbuildしておく必要があるとのことです。 READMEに沿って作業します。

READMEには次のように記載されています。(※手順はKompoのREADMEに記載されています)

## prerequisites
Install [kompo-vfs](https://github.com/ahogappa/kompo-vfs).

#### Homebrew

$ brew tap ahogappa/kompo-vfs https://github.com/ahogappa/kompo-vfs.git
$ brew install ahogappa/kompo-vfs/kompo-vfs

### Building
To build komp-vfs, you need to have cargo installation.

$ git clone https://github.com/ahogappa/kompo-vfs.git
$ cd kompo-vfs
$ cargo build --release

Set environment variables.

$ KOMPO_CLI=/path/to/kompo-vfs/target/release/kompo-cli
$ LIB_KOMPO_DIR=/path/to/kompo-vfs/target/release

MacBookを使っているのでbrewの手順を試しましたがうまくいかなかったので、リポジトリをcloneする方法で実施しました。

mainブランチで試してみましたが、buildがエラーになりました。

% cargo build --release
...
error: could not compile `kompo_storage` (lib) due to 2 previous errors; 1 warning emitted

こちら色々解析したところ、MacBookには対応してなさそうだとわかりました。 そこでDockerを立ち上げてその中でbuildすることにしました。 Kompoの実行もDockerで実施した方が良さそうだったので、Rubyイメージから作成しました。

# Dockerfile
FROM ruby:3.4.3

# Install dependencies
RUN apt-get update && apt-get install -y \
  git \
  build-essential \
  libssl-dev \
  zlib1g-dev \
  libyaml-dev \
  libgmp-dev \
  libreadline-dev \
  pkg-config \
  autoconf \
  bison \
  curl \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

# Install latest Rust using rustup
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

# Set working directory
WORKDIR /app

# Copy project files
COPY . /app/

# Install bundler and dependencies
RUN gem install bundler && bundle install

CMD ["tail", "-f", "/dev/null"]

buildしてbashでコンテナに入り、改めてbuildしたら成功しました🙌

% docker build -t hello-world .
% docker run -it --rm -v .:/app hello-world bash
root:/app# cd kompo-vfs/
root:/app/kompo-vfs# cargo build --release
...
Finished `release` profile [optimized] target(s) in 5.40s

Kompo

次に'Hello, world!'を返すRubyスクリプトを作成します。

# hello.rb
p 'Hello, world!'

Kompoはgemが公開されていないのでリポジトリをcloneしてローカルで生成します。 色々試行錯誤したのちに気づいたのですが、登壇スライドの26ページによるとkompoはfeature/use_rtld_nextの方が最新と思われるのでそちらを利用します。

Demo

Dockerコンテナ内でgem buildを実施して、無事kompo-0.2.0.gemが生成できました。

root:/app/kompo# gem build kompo.gemspec
WARNING:  licenses is empty, but is recommended. Use an license identifier from
https://spdx.org/licenses or 'Nonstandard' for a nonstandard license,
or set it to nil if you don't want to specify a license.
WARNING:  open-ended dependency on mini_portile2 (>= 0) is not recommended
  use a bounded requirement, such as "~> x.y"
WARNING:  open-ended dependency on async (>= 0) is not recommended
  use a bounded requirement, such as "~> x.y"
WARNING:  You have specified the uri:
  https://github.com/ahogappa0613/kompo
for all of the following keys:
  homepage_uri
  changelog_uri
  source_code_uri
Only the first one will be shown on rubygems.org
WARNING:  See https://guides.rubygems.org/specification-reference/ for help
  Successfully built RubyGem
  Name: kompo
  Version: 0.2.0
  File: kompo-0.2.0.gem

次に、Kompoをインストールします。

root:/app# gem install kompo/kompo-0.2.0.gem
...
Successfully installed kompo-0.2.0
10 gems installed

時は来た!あとは梱包(Kompo)するだけです!実行には数分かかるので待ちます。

root:/app# kompo --help
Usage: kompo [options]
    -e, --entrypoint=VAL             File path to use for entry point. (default: './main.rb')
    -g, --use-group=VAL              Group name to use with 'bundle install'. (default: 'default')
        --[no-]gemfile               Use gem in Gemfile. (default: automatically true if Gemfile is present)
        --local-kompo-fs-dir=VAL
        --verbose                    Verbose mode.
        --dest-dir=VAL               Output directry path. (default: current dir)
        --bundle-cache=VAL           Specify the directory created by 'bundle install --standalone'.
        --ruby-version=VAL           Specify Ruby version. (default: current Ruby version)
        --rebuild
        --repack
root:/app# kompo -e hello.rb --local-kompo-fs-dir=kompo-vfs
...
/usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.WD+0x0): multiple definition of `WD'; /tmp/ccNwyWlB.o:(.rodata+0x0): first defined here
/usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.PATHS_SIZE+0x0): multiple definition of `PATHS_SIZE'; /tmp/ccNwyWlB.o:(.rodata+0x28): first defined here
/usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.PATHS+0x0): multiple definition of `PATHS'; /tmp/ccNwyWlB.o:(.rodata+0x30): first defined here
/usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.FILES_SIZE+0x0): multiple definition of `FILES_SIZE'; /tmp/ccNwyWlB.o:(.rodata+0x192c8): first defined here
/usr/bin/ld: /app/kompo-vfs/target/release/libkompo_fs.a(529179467e613863-dummy_fs.o):(.rodata.FILES+0x0): multiple definition of `FILES'; /tmp/ccNwyWlB.o:(.rodata+0x192d0): first defined here
collect2: error: ld returned 1 exit status
/usr/local/bundle/gems/kompo-0.2.0/lib/kompo.rb:193:in 'Kernel#system': Command failed with exit 1: gcc (RuntimeError)

エラーが発生しました。 kompo-vfsで、WDPATHSなどの定義が重複しているようです。

Rustなんもわからんので雰囲気ですが、kompo-vfs/kompo_fs/dummy_fs.cの中でWDPATHSが定義されているので怒られた定義をコメントアウトして再buildしてやり直してみました。

// kompo-vfs/kompo_fs/dummy_fs.c

// const char FILES[] = {};
// const int FILES_SIZE = 0;
// const char PATHS[] = {};
// const int PATHS_SIZE = 0;
// const char WD[] = {47,119,111,114,107,115,112,97,99,101,115,47,114,117,98,121,95,112,97,99,107,97,103,101,114,47, 0};
const char START_FILE_PATH[] = {46,47,109,97,105,110,46,114,98, 0};

再度Kompoを実行。今回は正常終了し、バイナリ(app)が生成されました🎉

root:/app# kompo -e hello.rb --local-kompo-fs-dir=kompo-vfs
...
info: Finish kompo!
root:/app# ls -l app
-rwxr-xr-x 1 root root 76437456 Apr 24 02:15 app
root:/app# ./app
"Hello, world!"

最後に実行します。せっかくなのでRubyが入っていない環境で実行します。

Dockerfileはこちらを使いました。

FROM ubuntu:latest

# Set working directory
WORKDIR /app

# Copy project files
COPY . /app/

CMD ["tail", "-f", "/dev/null"]

コンテナを立ち上げて、バイナリを実行します。 Rubyが入っていない環境で実行できました🎉

% docker build -t ubuntu-app .
% docker run -it --rm -v .:/app ubuntu-app bash
root@62df4e7aa257:/app# ./app
"Hello, world!"

Rails

簡単なRubyスクリプトでは実行できることがわかったので、次はRailsに挑戦です!

ただ、結論は「色々試行錯誤したものの起動できず」でした...

Kompoするところでエラーになったり、Kompoはできたが起動できなかったり、これ以上の解析は難しいので今回は諦めました🙏

最後に

今回は簡単なRubyスクリプトしかできませんでしたが、ワンバイナリで配布してすぐに実行できることはとても便利だと感じました。今後の更なる進化に期待です!!


5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、ピクシブ株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2025の振り返りを行います。

LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!!

https://pixiv.connpass.com/event/352852/pixiv.connpass.com

また、ファインディではこれからも新しいものを取り入れつつ、Rubyを積極的に活用してRubyとともに成長していければと考えております。

一緒に働くメンバーも絶賛募集中なので、興味がある方はぜひこちらから ↓

herp.careers

RubyKaigi 2025レポート:FindyのRailsプロジェクトでSorbetの型チェックを試してみた

こんにちは。ファインディでソフトウェアエンジニアをしているnipe0324です。

先日、愛媛県松山市で開催されていたRubyKaigi 2025 に参加してきました。

様々なセッションに参加し、他社のエンジニアと話す中で多くの刺激をうけました。特に印象深かったのは、Sansanさん、TwoGateさん、Timeeさんなどの企業がRubyの型を導入して運用していたことです。

本記事では、N番煎じですが、Findy転職のRailsプロジェクトにSorbetを入れて型を試してみました。

Rubyの型に興味を持っているけどなかなか試せていないという方の参考になれば嬉しいです。

Sorbetとは?

Sorbet(https://github.com/sorbet/sorbet) はRubyのコードに型注釈をつけて、型エラーを検出できるツールです。

StripeやShopifyなどの企業で利用されています。

Sorbetを利用している企業例

Sorbetのメリット・デメリット

Sorbetの主なメリットとデメリットは次のとおりです。

メリット

  • 型エラーを事前に検出できる
  • コードの可読性と保守性の向上
  • IDEのコード補完がより有効に使える
  • リファクタリングが安全に行える
  • ドキュメントとしての役割

デメリット

  • 導入コストがかかる(コードの修正、型定義の作成)
  • Rubyの動的な特性が一部制限される
  • チーム全体に学習コストが発生する
  • すべてのgemやライブラリが型に対応しているわけではない

Sorbetを使ったRubyのコード例

Sorbetを使うと、型をインラインで定義できます。

このコード例では、sigを使って、greetメソッドがString型の引数を受け取ることを宣言しています。そのため、数値(Integer)を渡すと型エラーが検出されます。

# typed: true

class User
  extend T::Sig

  sig {params(name: String).void}
  def greet(name)
    "Hello #{name}"
  end
end

User.new.greet("Tom")  # OK - 文字列を渡している
User.new.greet(3)      # 型エラー - 数値を渡している
補足:Rubyの型チェッカー

現時点でRubyの主要な型チェッカーとして「Sorbet」と「Steep」があります。また、型定義の書き方として「RBI(Ruby Interface)」と「RBS」があります。
現時点の組み合わせとして、「SorbetとRBI」、「SteepとRBS」というように型チェッカーと型定義を組み合わせ使います。

型導入のモチベーション

Findy転職のRailsアプリケーションでは、「GraphQL API」、「Interactor」、「Model」といったレイヤーで実装をしています。

Findy転職のRailsアプリケーションのレイヤー抜粋

Interactor層では、collectiveidea/interactor という gemを使ってビジネスロジックを実現しています。

1つの操作(ユーザー登録やデータ検索など)を独立した「インタラクション」として実装でき、責務を分割しやすい特徴があります。

課題:Interactorの入出力が不明確

Interactor gemの内部ではOpenStructを使ってデータの受け渡しをしています。つまり、どんな値でも自由に設定できる柔軟性がある反面、入出力として何があるのか不明確になりやすいです。

Interactorのサンプルコードで問題点を確認してみます。

# イメージ実装

# Interactorの実装
class CreateJobDescriptionInteractor
  include Interactor
  
  def call
    job_description = JobDescription.new(create_params)

    if job_description.save
      # contextには何でも入れられるため
      # Interactorの返り値を知るには実装を読む必要がある
      context.job_description = job_description
    else
      context.fail!(error_messages: job_description.errors.full_messages)
    end
  end

  private

  def create_params
    # contextにどんな値が入るかは呼び出し元を見る必要がある
    context.params.slice(:name, :description, :job_type)
  end
end

# 呼び出し元
# Interactorの中身を読まないと何が返されるか分からない
result = CreateJobDescriptionInteractor.call(params: { 
  title: '求人票', 
  description: '求人票の内容', 
  job_type: 'バックエンドエンジニア' 
})

if result.success?
  result.job_description  # job_descriptionが返るのは実装を読まないと分からない
else
  result.error_messages   # error_messagesが返るのも実装を読まないと分からない
end

この問題に対して、型を導入することで入出力を明確にし、コードの可読性と保守性を向上させることを目指しました。

検証した結果

Sorbetの公式ドキュメントを見ながら、GraphQL、Interactor、Modelに対して型を定義してみました。

結果としては、次のとおりです。

  • ActiveRecordのモデルGraphQLのAPItapiocaを使うことで型定義(RBIファイル)をある程度自動生成できるため導入が簡単
  • Interactorは、gemの特性上、型との相性が悪く、PORO(Plain Old Ruby Object)などの設計の変更の実施が必要そう

検証の実施内容

Sorbetのセットアップ

SorbetのGetting Started (https://sorbet.org/docs/adopting) を見ながらセットアップをしました。

まず、Gemfileに必要なgemを追加してbundle installを実行します。

# Gemfile

gem 'sorbet', group: :development
gem 'sorbet-runtime'
gem 'tapioca', require: false, group: [:development, :test]

次に、Tapiocaを使ってSorbetを初期化します。このコマンドでsorbetディレクトリが作成され、プロジェクト内のGemに対して自動的に型定義(RBIファイル)が生成されます。

$ bundle exec tapioca init

Sorbetによる型チェックを実行します。初回は多くの型エラーが出るので、修正していきます。

$ bundle exec srb tc

型エラーを修正して、型チェックが成功したら初期セットアップは完了です 👏

$ bundle exec srb tc
No errors! Great job.

ActiveRecordのモデルに型を追加

tapioca dslコマンドを使うことで、ActiveRecordモデルやGraphQL-RubyのDSL(Domain Specific Language、特定領域向け言語)から自動的に型定義ファイル(RBIファイル)が作成できます。

$ bundle exec tapioca dsl
Loading DSL extension classes... Done
Loading Rails application...

      create  sorbet/rbi/dsl/skill.rbi
      create  sorbet/rbi/dsl/user.rbi
      create  sorbet/rbi/dsl/job_description.rbi
      create  sorbet/rbi/dsl/xxxx.rbi
      ...

例えば、JobDescriptionモデルがある場合、次のようなRBIファイルが自動的に作成されます。これによりモデルのプロパティに型情報が付与されます。

class JobDescription

     # ...

    sig { returns(::String) }
    def title; end

    sig { params(value: ::String).returns(::String) }
    def title=(value); end

    sig { returns(T::Boolean) }
    def title?; end

    sig { returns(T.nilable(::String)) }
    def title_before_last_save; end

では、実際に型チェックが機能するか検証してみます。

# typed: trueをファイルの上部に追加し、型チェックの対象にします。さらに、型エラーの動作確認のためわざと不正な値を設定してみます。

# frozen_string_literal: true
# typed: true

class JobDescription < ApplicationRecord
  def type_check_error_method
    self.title = 1 # String型のプロパティにInteger型を設定(エラーになるはず)
  end
end

Sorbetを実行すると、予想通り型エラーが検出されました。👏

$ bundle exec srb tc
app/models/job_description.rb:6: Assigning a value to value that does not match expected type String https://srb.help/7002
     6 |    self.title = 1
                        ^
  Got Integer(1) originating from:
    app/models/job_description.rb:6:
     6 |    self.title = 1
                        ^
Errors: 1

GraphQLに型を追加

graphql-ruby を利用しているプロジェクトでは、GraphQLの型定義もtapioca dslコマンドで自動生成されます。

# 自動生成されるRBIファイルのイメージ
class CreateJobDescriptionMutation
  sig { params(title: ::String, company_id: ::Integer).returns(T.untyped) }
  def resolve(title:, company_id:); end
end

ActiveRecordモデルと同様に、型定義に違反した実装をすると型エラーが発生します。これによりGraphQLのリゾルバーやミューテーションにも型安全性を導入できます。

ただし、自動生成されたRBIファイルでは戻り値がT.untyped(型のない状態)になることがあるため、具体的な型を指定していく必要があると感じました。

Interactorに型を追加

Interactorでは、型定義をうまく行うことができませんでした。

Interactor gem で定義されているcontextが、OpenStructを使っておりデータが柔軟に設定できるがゆえに型定義がうまくできませんでした。

# frozen_string_literal: true
# typed: true

class CreateFooInteractor
  extend T::Sig

  include Interactor

  # contextのアクセスのための型定義
  # 上手く定義できず、`T.untyped`(型のない状態)になっている
  sig { returns(T.untyped) }
  attr_reader :context

  def call
    foo = Foo.new(create_params)

    if foo.save
      context.foo = foo
    else
      context.fail!(error_messages: foo.errors.full_messages)
    end
  end

  private

  def create_params
    context.params.slice(:title, :company_id)
  end
end

改善案としては、PORO (Plain Old Ruby Object) による実装に変えて、入出力の型を明確にすることで、型定義を記載するという方法が考えられます。

また、https://github.com/maxveldink/sorbet-result にあるような RustのResult型に似た実装を導入するのも効果的です。Result型は「成功」か「失敗」のどちらかの状態を表現するもので、型安全な方法で結果を扱えるようになります。

型でガチガチにするとRubyの良さが失われてしまう懸念もあるため、段階的に導入しつつバランスを見ていく必要があります。

型を試してみた所感

今回既存のRailsプロジェクトでSorbetによる型を試しに導入しました。

感想としては、検討事項は他にもありますが、前向きに型導入を進めていこうと思いました。

  • ActiveRecordやGraphQLにほぼ自動的に型定義を追加できたり、段階的に型チェックを有効化できるので小さく始めやすい
  • ローカル開発やCIで型チェック、GraphQLやテーブルスキーマ変更時の型更新のフローを整備する必要がある
  • SorbetとSteepのどちらが良いかは好みによるので検討は必要がある
  • など

最後に

ファインディでは、一緒にRubyやRailsの開発をしてくれる仲間を募集しています。 興味のある方は、ぜひこちらからチェックしてみてください! herp.careers

また、2025/5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、3社合同でRubyKaigi 2025の振り返りを行います。

オンライン・オフラインどちらもありLTやパネルディスカッションなどコンテンツが盛りだくさんなのでぜひご参加ください!!

pixiv.connpass.com

RubyKaigi 2025で紹介されたruby.wasmのスライド作成ツールgibier2を使ってみた

はじめに

こんにちは!ファインディでFindy Team+を開発している中嶋(@nakayama__bird)です!

RubyKaigi 2025に参加してきました!

今回のRubyKaigiが初参加で楽しみ半分緊張半分だったのですが最高な3日間でした! Rubyを使う人、Rubyを作る人、そしてRubyで遊ぶ人などたくさんの出会いがあり、日頃の業務でRuby on Railsに触れるのとはまた違った視点でRubyを考えるきっかけとなりました。

複数のセッションに参加した中で特に関心を持ったruby.wasm、そしてruby.wasmを使ったスライド作成ツールgibier2を使ってみての感想をまとめていきたいと思います。

ruby.wasmとは?

ruby.wasmとは、WebAssemblyという技術を使用してRubyのコードをブラウザ上で実行できる仕組みのことです。 Rubyをブラウザ上で実行できることで、環境構築をせずにRubyのコードを試せる環境の提供やフロントエンド実装への可能性を広げるといったメリットがあります。

github.com

わかりやすい例だとruby.wasmのREADMEに掲載されている Try Rubyが挙げられます。ブラウザ上でRubyのコードが動くため、RubyをPCにインストールせずともどんな挙動になるのかを簡単に確認できます。

私自身、約1年半ほど前にコードを初めて書いた駆け出しエンジニアなのですが、ブラウザ上で動きが確認できるのは学習スタートのハードルが下がるという点でとても良いなと思いました。

また、ruby.wasmを活用した事例として、Writing Ruby Scripts with TypeProfのセッションで、TypeProfをブラウザ上で試すことができるTypeProf.wasmが紹介されていました。

気軽に試してみて、もしエラーなどあれば報告してくださいというようなお話をしており、ruby.wasmが新しい技術を手軽に試せる環境として機能している点も大きな魅力だなと感じました。

x.com

gibier2について

gibier2は、ruby.wasmを使ったスライド作成ツールです。 dRuby on Browser Again! でトークセッションをした youchanさんが開発したもので、マークダウンでスライドを作成できます。

x.com

github.com

gibier2の前身としてgibierがあるのですが、こちらはRubyをJavaScriptにコンパイルする Opal を使用しているためスライド作成ツールという点では共通しているものの中身は別物です。

これまで私自身、スライドを作成する際にCanvaやGoogle スライドを使いGUI上で操作して作成していました。しかしながら箇条書きで内容整理をしながら登壇資料が作成できて便利そうということで、マークダウンを使ったスライド作成ツールが気になっていたため早速使ってみました!

セットアップ

開発環境:ruby 3.3.6(RubyのWebAssemblyサポートは3.2.0以降1

READMEを参考に実行しました。具体的なコマンドは、次の通りです。 リポジトリをcloneしてbundle installします。

$ git clone https://github.com/youchan/gibier2
$ cd gibier2
$ bundle install

その後、wasmディレクトリでのセットアップを行います。 README通りにbundle exec rbwasm build -o dist/ruby.wasmをしたところ失敗したため、先にcd wasm && bundle installを実行しました。

$ cd wasm && bundle install

その後、wasmディレクトリで次のコマンドを実行します。

$ bundle exec rbwasm build -o ../dist/ruby.wasm
$ bundle exec rbwasm pack ../dist/ruby.wasm --dir ./src::/src -o ../dist/app.wasm

初回のbuildには時間がかかるのしばらく待ちます。

ルートディレクトリに戻りRackアプリケーションサーバーを立ち上げます。

$ cd ..
$ bundle exec rackup

http://localhost:9161を開くとサンプルのスライドが表示されます。

Ruby WebAssembly

実際に使ってみる

基本的にslide.mdにマークダウンで資料の内容を書いていきます。

<!-- slide.md -->
## RubyKaigi 2025行ってきました
- ruby.wasmを知ることができた
- gibier2を使ってみた
- スピーカーとお話しできた

スライドの背景画像はpublic/images配下におきます。 それをpublic/css/custom.cssで適宜設定します。

/* public/css/custom.css */
.page {
  background-image: url("/images/new_background.png");
  color: #222;
}

Ruby WebAssembly

特にカスタマイズしないと、#だとセンタリングされた状態、##だと見出しとして表示されます。

<!-- slide.md -->
# #が1つの場合

Ruby WebAssembly

<!-- slide.md -->
## #が2つの場合

Ruby WebAssembly

マークダウンのためコードブロックやリンクの追加も可能です。

<!-- slide.md -->
## コードブロックやリンクの挿入も可能

<200b>```ruby
def hello
  puts "Hello, World!"
end
<200b>```

[Findy Tech Blog](https://tech.findy.co.jp/)

Ruby WebAssembly

まとめ

RubyKaigiで関心を持ったruby.wasm、そしてruby.wasmを使ったスライド作成ツールgibier2を使ってみたという内容をまとめました。

発表を聞いてすぐにgibier2を試したのでぜひyouchanさんにお話を聞きたいなと思っていたところ、偶然にも弊社のDrink Upでお会いできました。セッション後、早速使ってみましたと伝えると喜んでもらえて私も嬉しい気持ちになりました。 このように、登壇者とコミュニケーションを取りやすいのもRubyKaigiの良いところです。

gibier2について、今回のRubyKaigiの登壇資料用として作ったため、汎用的なツールとしてはこれからだという話をしていました。 またGUI上でより細かなデザインをできるようにしたりPDFへ書き出せるようにしたりしたいなど、今後の展望についてもお話を聞くことができとても良い経験となりました。

またビルドの流れでREADMEに記載されている通りに実行したところうまく立ち上がらなかったため、Issueを作成してみました。

github.com

OSSへの貢献したいなと考えていたので、RubyKaigiでの出会いをきっかけに実際に行動に移せてよかったです。

最後に

5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、ピクシブ株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2025の振り返りを行います。

オンライン・オフラインどちらもありLTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!! 今回のブログで取り上げたgibier2を使って登壇資料を作成予定なので、ぜひ見にきていただけると嬉しいです!

pixiv.connpass.com

また、ファインディではこれからも新しいものを取り入れつつ、Rubyを積極的に活用してRubyとともに成長していければと考えております。

一緒に働くメンバーも絶賛募集中なので、興味がある方はぜひこちらから ↓

herp.careers

RubyKaigi 2025 レポート: 早速「RBS::Trace」でRailsプロジェクトの型情報を自動生成してみた!

こんにちは、Findy Freelanceの開発をしているエンジニアの@2boです。

先日、愛媛県で開催されたRubyKaigi 2025に参加してきました。ファインディのブースにお立ち寄りいただいた方、Rubyクイズに答えてくださった方、Drinkupに参加していただいた方、運営やSpeakerの皆様、ありがとうございました! おかげさまでとても楽しく過ごすことができ、興味深いセッションもたくさんありました。

本記事では、その中の1つである@sinsoku_listyさんの「Automatically generating types by running tests」で発表されていた「RBS::Trace」を早速、Findy FreelanceのRailsプロジェクトで試してみた結果と所感を紹介します。

RBS::Traceとは

RBS::Traceは、コード実行時にメソッドの引数と戻り値の型情報を収集し、自動的にInline RBSとしてコメントを挿入したり、RBSファイルを作成してくれるGemです。

実行手順

Findy FreelanceのRailsプロジェクトのテストでRBS::Traceを実行してみました。 執筆時点のバージョンは0.5.1です。 なお、今回は大枠の動作と結果を確認することが目的のため、対象はapp/models/配下に絞っています。

次の手順で実行しました。

1. Gemfileへの追加

gem "rbs-trace"

Gemfile追記後にbundle installを実行します。

$ bundle install

2. RSpecの設定

次に、RSpec用の設定ファイルを作成します。 RBS::Trace.newの引数pathsで対象のファイルを指定しています。

RBSファイルの格納先として、sig/trace/を指定しています。なお、この設定は任意です。

# spec/support/rbs_trace.rb

RSpec.configure do |config|
  # RBSの出力対象とするファイルを指定
  trace = RBS::Trace.new(paths: Dir.glob("#{Dir.pwd}/app/models/**/*"))

  config.before(:suite) { trace.enable }
  config.after(:suite) do
    trace.disable
    trace.save_comments
    # RBSファイルの格納先を指定
    trace.save_files(out_dir: "sig/trace/")
  end
end

3. テストの実行

テストを実行します

$ bundle exec rspec spec/models/

結果の確認

テストを実行すると、Inline RBSが対象のファイルのメソッド定義の上に追記され、RBSファイルが生成されます。 それぞれの結果を次に示します。 なお、掲載しているのは例示用のコードで、実際のFindy Freelanceのコードや処理とはまったく関係ありません。

Inline RBS

app/models/user.rbに次のようなInline RBSが生成されました。

class User < ApplicationRecord
  has_many :projects
  
  # @rbs () -> String
  def full_name
    format_name
  end

  # @rbs () -> Project?
  def main_project
    projects.find_by(active: true)
  end
  
  # @rbs (Date) -> Project::ActiveRecord_AssociationRelation
  def projects_after_date(date)
    projects.where('start_date >= ?', date)
  end
  
  private
  
  # @rbs () -> String
  def format_name
    "#{last_name} #{first_name}"
  end
end

RBSファイル

sig/trace/app/models/user.rbsに次のようなRBSファイルが生成されました。

class User
  def full_name: () -> String

  def main_project: () -> nil
                  | () -> Project

  def projects_after_date: (Date) -> Project::ActiveRecord_AssociationRelation

  def format_name: () -> String
end

参考: テストコード

RBS::TraceによるRBSの自動生成は、テストで実行された内容に依存するため、参考としてテストコードの例を掲載します。

RSpec.describe User do
  describe '#full_name' do
    subject { user.full_name }

    let(:user) { create(:user, first_name: '太郎', last_name: '山田') }

    it 'returns a string combining last_name and first_name' do
      expect(subject).to eq('山田 太郎')
    end
  end

  describe '#main_project' do
    subject { user.main_project }

    let(:user) { create(:user) }

    context 'when there is an active project' do
      let!(:active_project) { create(:project, user: user, active: true) }
      let!(:inactive_project) { create(:project, user: user, active: false) }

      it 'returns the active project' do
        expect(subject).to eq(active_project)
      end
    end

    context 'when there is no active project' do
      let!(:inactive_project) { create(:project, user: user, active: false) }

      it 'returns nil' do
        expect(subject).to be_nil
      end
    end
  end

  describe '#projects_after_date' do
    subject { user.projects_after_date(target_date) }

    let(:user) { create(:user) }
    let(:target_date) { Date.new(2025, 4, 1) }

    let!(:before_project) { create(:project, user: user, start_date: Date.new(2025, 3, 31)) }
    let!(:on_date_project) { create(:project, user: user, start_date: Date.new(2025, 4, 1)) }
    let!(:after_project) { create(:project, user: user, start_date: Date.new(2025, 4, 2)) }

    it 'returns only projects starting on or after the specified date' do
      expect(subject).to include(on_date_project, after_project)
      expect(subject).not_to include(before_project)
    end
  end
end

結果からわかったこと

実行した結果、次のようなことがわかりました。

  • 直接テストしていないが、テスト中に実行されるprivateメソッドの型情報も生成されている
  • ApplicationRecordのサブクラスを返すメソッドは、具体的なクラス名で型情報が生成されている
  • Railsのassociationやscopeメソッドには、型情報が生成されない
    • これらはメソッド定義ではないため当然の結果である
  • ActiveRecord::AssociationRelationのサブクラスのインスタンスを返すメソッドは、[具体クラス名]::ActiveRecord_AssociationRelationのように型情報が記載される
    • これはRailsの仕様によるもので、そのようなクラスを動的生成しているためである
  • 既に記載されているInline RBSは上書きされない
    • RBSファイルの内容はテストを実行するたびに更新される

Steepの導入

せっかくRBSファイルが生成されたので、型チェッカーであるSteepもセットアップして型のあるRailsプロジェクトを疑似体感してみました。 ただし、RBS::TraceだけでRailsプロジェクトの型チェックすべてパスさせることはできないため、VSCodeで型情報を確認できるようにすることを目的としています。

次の手順でセットアップしました。

1. Gemfileへの追加

# Gemfile
gem 'steep'

2. Bundle install

Gemfile追記後にbundle installを実行します。

$ bundle install

3. Steepの設定

設定ファイルとなるSteepfile作成します。

D = Steep::Diagnostic

target :app do
  # RBSファイルの格納先を指定
  signature "sig/trace"
  # Steepで型チェックする対象のファイルを指定
  check "app/models"
  # 型チェック結果を全て抑制(無音)する
  configure_code_diagnostics(D::Ruby.silent)
end

4. VSCodeのSteep拡張のインストール

steep-vscodeをVSCodeにインストールします。

5. RBSファイルのエラーをコメントアウト

RBSファイル内にエラーがあると、VSCodeのSteep拡張が動作しないため、エラーしている箇所をコメントアウトしました。本来は型定義を追加、修正するなどしてエラーを解消する必要がありますが、今回はVSCodeで型情報を確認できることが目的のため、このような対応をしました。

先のRBSファイルの例で言うと、RBS::Traceの実行だけではProject::ActiveRecord_AssociationRelationクラスの型情報が生成されないため、RBS::UnknownTypeNameエラーになります。これをコメントアウトすることでエラーを握りつぶしています。

class User
  def full_name: () -> String

  def main_project: () -> nil
                  | () -> Project

  # RBS::UnknownTypeName エラーになるためコメントアウト
  # def projects_after_date: (Date) -> Project::ActiveRecord_AssociationRelation

  def format_name: () -> String
end

VSCodeでの型情報の表示結果

メソッドの呼び出し箇所をホバーすると、型情報が表示されます。

VSCodeでメソッドをホバーすると型情報が表示される

また、入力の補完時にも型情報が表示されます。

VSCodeで補完時に型情報が表示される

所感

RBS::Trace導入のメリットと可能性

RBS::Traceは、RBSが全くないRailsプロジェクトにとって、型情報導入の最初の一歩として非常に有効だと感じました。テスト実行だけで型情報が自動生成されるため、手動で記載する手間が大幅に省けます。

Inline RBSだけの生成も可能なので、ドキュメント生成ツールとしても活用できそうです。これは人間にとって読みやすいだけでなく、GitHub Copilotなどの生成AIツールにも型情報を提供できるメリットがあります。生成AIがInline RBSから型情報を読み取れば、より正確なコード提案が得られるのではないかと期待しています。

将来的には、RBS::InlineRBS本体に組み込まれる計画もあるようで、Inline RBSだけで型チェックができる日も来るかもしれません。

活用における注意点

型情報の出力結果はテストの実行内容に依存します。例えばStringとnilを返すメソッドがあっても、nilを返すケースのテストがなければ、型情報はStringだけになってしまいます。つまり、RBS::Traceを活用するには、テストの品質確保が前提となります。

また、Railsプロジェクト全体にRBSやSteepを導入した場合、相応のメンテナンス工数がかかると感じました。RBS::Traceだけでは、Rails自体が生成するクラスやメソッドの型情報は提供されないため、rbs_railsなどの併用や、手動での型情報メンテナンスも必要になるでしょう。

RBSのプロジェクトへの導入について

今回の試行から、Findy FreelanceプロジェクトへのRBSやSteep導入を決定したわけではありませんが、これらのツールとエコシステムの現状を実感できました。RBS::Traceのおかげで試すハードルが下がったのは大きな収穫です。開発者の@sinsoku_listyさんには感謝しています。

最後に

RubyKaigi 2025では、Rubyの型に関するセッションがいくつかありました。 すべてを聞けてはいませんが、総じてRubyの型に関するエコシステムは今後もまだまだ進化していきそうだと感じました。 正直、私は今までちゃんとキャッチアップができていなかったのですが、RubyKaigiへの参加をきっかけに興味が強くなり、理解を深めるきっかけとなりました。 今後もRBS, Steep, Sorbetなどの型に関するエコシステムの進化をキャッチアップしつつ、プロジェクトに導入するかどうかを検討していきたいと思います。

5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、ピクシブ株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2025の振り返りを行います。 オンライン・オフラインどちらもありLTやパネルディスカッションなどコンテンツが盛りだくさんなのでぜひご参加ください!!

pixiv.connpass.com

ファインディでは、一緒にRubyやRailsの開発をしてくれる仲間を募集しています。 興味のある方は、ぜひこちらからチェックしてみてください! herp.careers

参考

RubyKaigi 2025 レポート: deprewriter-ruby で非推奨メソッドの置き換えを自動化!

こんにちは!ファインディ株式会社でエンジニアをしているみっきーです。 先日開催されたRubyKaigi 2025に参加しました。 去年はLTの登壇があり、準備で忙しかったので、今年はたくさんのRubyistと話したり、セッションを見られることをとても楽しみにしていました!! 今回は特に印象に残った「On-the-fly Suggestions of Rewriting Method Deprecations」というセッションについて紹介します。

自己紹介

私はFindy Team+でバックエンドエンジニアとして働いており、普段はRubyを使った開発をしています。 また、プライベートでは「omochi gem」というRuby gemを開発・メンテナンスしています。 そのため、今回のRubyKaigi 2025で「On-the-fly Suggestions of Rewriting Method Deprecations」というセッションを見つけたときは、すぐに興味を持ちました。非推奨メソッドの置き換えを自動化できるツールがあれば、ユーザーの移行をスムーズにサポートできると思ったからです。

deprewriter-ruby

deprewriter-ruby gemの紹介

「On-the-fly Suggestions of Rewriting Method Deprecations」セッションでは、ohbaryeさんが開発した「deprewriter-ruby」というgemが紹介されました。このgemは、非推奨になったメソッドの呼び出しを検出し、新しいメソッドへの置き換え方法を自動的に提案してくれるツールです。

deprewriter-rubyの特徴は次の通りです:

  1. 設定ファイルによる柔軟な定義: YAMLファイルで非推奨メソッドとその代替メソッドのマッピングを定義できます。
  2. インラインの提案: コードを実行すると、非推奨メソッドが使われている箇所で、代替メソッドへの置き換え方法が提案されます。
  3. 自動修正機能: 提案された変更を自動的に適用することも可能です。

このgemは、Rubyの標準的な警告メカニズムを拡張して、単に「このメソッドは非推奨です」と警告するだけでなく、「このメソッドは非推奨です。代わりにこのように書き換えてください」という具体的な提案をします。

speakerdeck.com

動かしてみた

セッション後、早速deprewriter-rubyを試してみました。

まず、次のようにインストールします

# Gemfile
gem "deprewriter", github: "ohbarye/deprewriter-ruby"

次に、ライブラリコードで非推奨メソッドとその代替メソッドを定義します

# Library code
require "deprewriter"

class Legacy
  def old_method(arg)
    puts "Using deprecated old_method with #{arg}"
  end

  def new_method(arg)
    puts "Using new_method with #{arg}"
  end

  extend Deprewriter
  deprewrite :old_method, to: 'new_method({{arguments}})'
end

そして、非推奨メソッドを含むコードを実行すると

# User code
legacy = Legacy.new
legacy.old_method("example argument")

環境変数を設定して実行することで、異なるモードで動作させることができます

# ログモード - 非推奨メソッドの警告と提案を表示
$ DEPREWRITER=log ruby your_script.rb

# 差分モード - 変更の差分を表示
$ DEPREWRITER=diff ruby your_script.rb

# 書き換えモード - 自動的にコードを書き換え(注意して使用してください)
$ DEPREWRITER=dangerously_rewrite ruby your_script.rb

ログモードでは次のような警告と提案が表示されます

$ DEPREWRITER=log bundle exec ruby user_code.rb
Calling deprecated method:
W, [2025-04-21T19:49:55.132998 #2137]  WARN -- : DEPREWRITER: Legacy#old_method usage at user_code.rb:9 is deprecated. You can apply the diff below to resolve the deprecation.
--- ./user_code.rb      2025-04-21 19:49:55.123740000 +0900
+++ ./user_code.rb      2025-04-21 19:49:55.123740000 +0900
@@ -6,7 +6,7 @@

 # Call the deprecated method
 puts "Calling deprecated method:"
-legacy.old_method("example argument")
+legacy.new_method("example argument")

 # The deprewriter will detect this call and show a warning
 # suggesting to use new_method instead

Using deprecated old_method with example argument

書き換えモードでは次のような警告と変更がおこなわれます。

$ DEPREWRITER=dangerously_rewrite bundle exec ruby user_code.rb
Calling deprecated method:
W, [2025-04-21T20:11:49.426183 #39447]  WARN -- : DEPREWRITER: Dangerously trying to rewrite. It will rewrite a file to apply the deprecation and load the file
Calling deprecated method:
Using new_method with example argument
Using deprecated old_method with example argument

$ git diff
diff --git a/user_code.rb b/user_code.rb
index d1a2a2b..bfcab60 100644
--- a/user_code.rb
+++ b/user_code.rb
@@ -6,4 +6,4 @@ legacy = Legacy.new

 # Call the deprecated method
 puts "Calling deprecated method:"
-legacy.old_method("example argument")
+legacy.new_method("example argument")

手元の環境でdemoを作成し、試してみました。 驚くほど簡単に非推奨メソッドの検出と提案が行われ、ユーザーがコードを更新する手間を大幅に削減できることを実感しました。

作者のohbaryeさんに聞いてみた

セッション後、ohbaryeさんに直接話を聞く機会がありました。セッションの内容だけでなく、ツール開発のきっかけや普段の情報収集の方法についても興味深いお話を伺うことができました。

ohbaryeさんはHacker Newsを定期的にチェックして、技術トレンドや面白いプロジェクトの情報を集めているそうです。 セッション内で紹介のあったPharoというプログラミング言語もHacker Newsで見かけたそうです。 また、PharoのDeprewriterに触れたきっかけで、deprewriter-rubyを作ったそうです。

「最近は日本語版もあるので、英語が苦手な方にもとっつきやすくなりました」と教えてくれました。さらに、情報収集の効率化について次のようなアドバイスもいただきました。

著名人や有名企業(Shopify、GitHubなど)のブログなど質の高い情報ソースはfeedlyというRSSリーダーを使って集めるようにしています。すべての未読記事を消化するのは大変なので、時間があるときに気になったものだけを読むようにしていますね。

このような効率的な情報収集の方法は、私も取り入れていきたいと思いました。 「必要だから作る」という実践的なアプローチにも共感し、自分のプロジェクトでも同じような姿勢で取り組んでいきたいと感じました。

ohbaryeさんに直接感想を伝えられたことで、オープンソースコミュニティの温かさも実感できた貴重な機会でした!

最後に

deprewriter-rubyは、Rubyの非推奨メソッドの置き換えを自動化するという、一見小さな問題に焦点を当てたgemですが、その影響は大きいと感じました。 非推奨メソッドの変更は避けられないものであり、ユーザーの移行をいかにスムーズにサポートするかは、ライブラリ開発者にとって重要な課題です。 day3のRuby Committers and the World内で、Matzも後方互換を大切にしている話をしていました。

x.com

現在のdeprewriter-rubyには、複雑なメソッド呼び出し(メタプログラミングなど)の書き換えができない、非推奨メソッドが呼び出されるたびに書き換え処理が実行されて効率が悪い、非標準のRubyライブラリに依存しているといった制限があります。 しかし、ohbaryeさんはこれらの課題に対して、より複雑なメソッド呼び出しへの対応、最初の呼び出し後の処理をスキップして最適化する、依存関係を減らしてスタンドアロン化するといった明確な改善計画を持っています。 このような継続的な改善への取り組みを見ると、今後さらに実用的なツールへと進化していくことが楽しみです!!

RubyKaigi 2025では他にも多くの興味深いセッションがありました。 Rubyコミュニティの活発な技術共有やライブラリやツール開発の文化に、改めて感謝の気持ちを抱いた3日間でした。

5/13(火)に、「After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜」として、ピクシブ株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2025の振り返りを行います。

オンライン・オフラインどちらもありLTやパネルディスカッションなどコンテンツが盛りだくさんなのでぜひご参加ください!!

ファインディでは、一緒に働く仲間を募集しています!! 興味を持っていただいた方はこちらのページからご応募お願いします。

herp.careers