Ruby - TECH PLAY - TECH PLAY

TECH PLAY

Ruby

イベント

マガジン

技術ブログ

本記事は 2026 年 5 月 21 日に公開された Mike George 氏の “ Test Driven Development (TDD) with Kiro: this is how it should feel ” を翻訳したものです。 私のキャリアの初期に、所属していた組織は、コード品質の向上と自信を持ってリファクタリングできる体制づくりのために、本格的にユニットテストを導入するという正しい判断を下しました。私たちは テスト駆動開発(TDD) の導入を試みました。その利点は理解していたものの、TDD を実践する作業自体が負担に感じられ、エンジニアたちにこの手法を一貫して適用してもらえませんでした。私自身、TDD というアイデアは大好きでしたが、実際の作業は嫌いでした。本記事では、Kiro を使って TDD を実践する方法を紹介し、red-green-refactor サイクルに沿ったテストを手作業で書くという苦痛を伴うことなく、TDD の恩恵を受ける方法をお見せします。 テスト駆動開発(TDD)の概要 テスト駆動開発の基本的な考え方は次のとおりです。 構築する新機能ごとに、その新機能の振る舞いを特定します。 新しい振る舞いそれぞれに対して、もしその振る舞いが存在すればパスするはずのテストを書きます。 テストを実行します。振る舞いがまだ存在しないため、すべてのテストは失敗するはずです。 新しいテストをパスさせるもっともシンプルなコードを書きます。 すべてのテストがパスし続けることを確認しながら、必要に応じてリファクタリングします。 これが red-green-refactor サイクルと呼ばれるものです。書いたテストが失敗し(red フェーズ)、テストをパスさせるためにコードを書き(green フェーズ)、必要に応じてリファクタリングを続ける(refactor サイクル)という流れです。TDD は高品質なコードを生み出すのに役立ちますが、時間がかかると見られがちで、プロセスを一貫して守るためにかなりの規律が求められます。 私は TDD のアイデアは大好きでしたが、テストコードと実装コードの間を行き来するためのコンテキストスイッチが嫌いでした。どのテストを書いたかを管理することも好きではなく、率直に言って、TDD をきちんと実践するために必要なすべてのテストを書く単調さも嫌でした。TDD の経験について他の人と話すと、自分だけがそう感じているわけではないことがよくわかります。 Kiro のようなエージェント型開発ツールは、TDD のこうした欠点をうまく扱ってくれます。Kiro は仕様駆動開発をサポートしています。これは、Kiro が正しいものを正しい方法で正しいアーキテクチャに沿って構築できるよう導く、3 つのフェーズからなる開発プロセスです。各 spec は次の 3 つのフェーズで構成されます。要件フェーズでは、Kiro がユーザーストーリーと受け入れ基準の定義と承認を支援します。設計フェーズでは、技術アーキテクチャと実装アプローチを定義します。タスクフェーズでは、元の要件を満たすコードを生み出すための実行可能な実装タスクを定義します。 Kiro は仕様駆動開発に加えて、 hooks もサポートしています。hooks は、IDE で特定のイベントが発生したときに自動的に実行される自動化ツールです。たとえば、ファイルが保存されるたびにコードを lint する hook を作成したい場合などです。要するに、仕様駆動開発が全体的なエンジニアリングのアプローチを構造化するのに役立つ一方、hooks は TDD のような特定のプラクティスの徹底を可能にします。 私の場合は、TDD の取り組みを支援するために Kiro hook を作成しました。 本番環境での利用におけるセキュリティ上の考慮事項 このチュートリアルは、教育目的で例示プロンプトを使用して Kiro の TDD 機能を紹介しています。本番システム向けにこのアプローチを適用する場合は、次の点に留意してください。 デプロイ前に、 AI が生成したコードのセキュリティ脆弱性を必ずレビューしてください 。とくに、認証、認可、入力検証、データ処理ロジックには注意が必要です。コードが正しく動作することを確認するため、さまざまなシナリオでテストしてください。 この例では簡潔さのために認証なしのオープンな API を使っていますが、 本番環境のデプロイには包括的なセキュリティ対策が必要です 。具体的には、認証と認可、HTTPS/TLS 暗号化、保存時の暗号化、入力検証、レート制限、包括的なロギング、認証情報のシークレット管理などです。 AI 開発ツールを採用する組織は、アクセス制御、コード品質モニタリング、検証プロセスに関する ガバナンスポリシーを確立する 必要もあります。 包括的なセキュリティガイダンスについては、所属組織のセキュリティチームに相談し、 AWS のセキュリティのベストプラクティスに関するドキュメント を参照してください。 Kiro と TDD の統合 Kiro 内で hook を作成するには、Kiro タブに移動し、 agent hooks ウィンドウの「+」ボタンをクリックして manually create a hook を選択します。表示されたウィンドウで、以下を入力します。 Title : TDD: Test First Description : 本番コードを書く前に、失敗するテストを書くよう促すことでテスト駆動開発を徹底します。red/green/refactor サイクルに従います。すなわち、失敗するテストを書き(red)、それをパスさせる最小限のコードを書き(green)、その後リファクタリングします。 Event : Pre Tool Use Tool name : write Action : Ask Kiro Instructions for Kiro agent (以下は和訳です。原文は英語で記述されています): 待ってください! あなたは今コードを書こうとしています。TDD に従い、まずテストを書いてください。 必要なアクション: 1. これがテストファイル(*test*.py、*.spec.* など)の場合 — 続行する 2. これが例外(設定ファイル、ドキュメント、.kiro/)の場合 — 続行する 3. 対応するテストファイルが存在し、かつ実行した結果すべてのテストが失敗(RED フェーズ)していることが確認できている場合 — 続行する 4. それ以外の場合 — 中断し、まずテストファイルを書く: a. test_<filename>.py(または同等のもの)を作成する b. (単なる import ではなく)実際のアサーションで失敗するテストを書く c. テストを実行し、すべてがアサーションエラーで失敗することを確認する d. その後、戻ってこの実装を書く TDD サイクル: - RED: まず失敗するテストを書く(新しいテストはすべてアサーションで失敗しなければならない) - GREEN: テストをパスさせる最小限のコードを書く - REFACTOR: テストが green のままになるようにしつつコードを整理する RED フェーズの要件: - すべてのテストは AssertionError またはテスト失敗で失敗しなければならない - RED フェーズでいずれかのテストがパスする場合、そのテストは弱い/中身がない - RED フェーズでテストがパスするのは、空のループ、アサーションの欠落、テスト内のロジックエラーを意味する 正しい RED フェーズ: すべてのテストが AssertionError で失敗している 不正な RED フェーズ: 一部のテストがパスする、ImportError、ModuleNotFoundError、構文エラー 例外(テストなしで続行してよいもの): - テストファイル: *test*.py、*.spec.*、*.test.*、test_*.py、*_test.go - 設定: package.json、tsconfig.json、.eslintrc、webpack.config.js、vite.config.ts、jest.config.js、pytest.ini、setup.py、pyproject.toml、requirements.txt、Dockerfile、docker-compose.yml、.env、.gitignore - .kiro/ ディレクトリ内のファイル - 型定義: .d.ts ファイル、Protocol クラス - ドキュメント: .md、.txt、.rst、LICENSE、README、CHANGELOG - データファイル: .json、.yaml、.yml、.xml、.csv、.sql、.html、.css、.scss(ロジックを含まないもの) - ビルド成果物: dist/、build/、__pycache__/、*.pyc、node_modules/ 先にテストを必要とするもの: - 関数/クラスを含む Python ファイル(テストパターンに一致しない .py) - ロジックを含む JavaScript/TypeScript(テストパターンに一致しない .js、.ts、.jsx、.tsx) - 実行可能なロジックを含むすべてのソースコード(Go、Java、C#、Ruby など) 重要な確認: テストを実行し、すべてのテストが失敗したことを確認しましたか? スタブ実装でいずれかのテストがパスした場合は、まずテストを修正してください。すべてのテストが失敗する適切な RED フェーズが得られたときにのみ続行してください。 画面の一番下までスクロールし、 Create Hook をクリックします。 この hook は、Kiro がファイルを保存しようとするたびに実行されます。コードを書いている過程で red-green-refactor サイクルが守られているかをチェックします。 簡単なテストとして、 モンティ・ホール問題 を示すプログラムを書くよう Kiro に依頼してみました。Kiro はユニットテストを書き、それらは失敗します。次に Kiro は、対応するモジュールがまだ存在しないためにテストが失敗していることに気づきます。hook によれば、テストはアサーションの失敗によって失敗しなければなりません。そこで Kiro は基本的なモジュールを作成し、テストがアサーションエラーで失敗することを確認します。その後、テストをパスさせるための最小限のコードを書きます。 このシンプルな例で hook が機能していることがわかりますが、もう少し現実的なもの、つまり本番環境向けの REST ベースの API の構築でどう動くか見てみましょう。 REST ベースの API を構築する 実際のコードを書くため、私は Kiro の spec セッション を開始し、タスク管理システム用の REST ベースの API を構築するための要件を入力しました。最終的な要件ドキュメント(以下は和訳です。原文は英語で記述されています)は以下のとおりです。 # 要件ドキュメント ## はじめに タスク管理システム用の REST API です。この API は、永続化のための リレーショナルデータベースまたは組み込みデータベース(PostgreSQL や SQLite など)を備えた Python の Web フレームワーク(Flask や FastAPI など)を使って構築します。アプリケーションは Docker コンテナ としてパッケージ化されるため、コンテナをサポートする任意の プラットフォームにデプロイできます。この API はオープン(認証・認可 なし)です。タスクに対する完全な CRUD 操作をサポートし、クライアントが タスクを作成・読み取り・更新・削除できるようにします。フロントエンドは ありません。これは API のみのサービスです。 ## 用語集 - **API_Server**: HTTP リクエストを受け取り、適切なリクエスト ハンドラーへルーティングする Python の Web フレームワーク アプリケーション(Flask や FastAPI など) - **Task_Service**: タスク関連のビジネスロジックの処理を担う、 コンテナ内で動作するアプリケーションロジック - **Task**: 一意の識別子、タイトル、説明、ステータス、 タイムスタンプといったプロパティを持つ作業単位 - **Task_Store**: タスクが格納・取得されるリレーショナル データベースまたは組み込みデータベース(PostgreSQL や SQLite など) - **Client**: API に HTTP リクエストを送信する外部システム またはユーザー - **Dockerfile**: アプリケーションとその依存関係をポータブルな Docker イメージにパッケージ化する、コンテナのビルド定義 - **Container**: API_Server と Task_Service をホストする、 Docker イメージの実行中インスタンス - **Standard_Error_Format**: すべての API エラーレスポンスで 使われる一貫した JSON エラーレスポンス構造。 `{"error": "<説明的なメッセージ>"}` の形式 ## 要件 ### 要件 1: タスクの作成 **ユーザーストーリー:** クライアントとして、API を通じて 新しいタスクを作成したい。作業項目を追跡できるようにするためである。 #### 受け入れ基準 1. WHEN クライアントが title と description の両方を含む有効な リクエストボディとともに `/tasks` へ POST リクエストを送信する、 THE Task_Service SHALL Task_Store に新しい Task を作成し、 一意の識別子を持つ作成済みの Task を HTTP ステータス 201 とともに返す 2. WHEN クライアントが必須フィールド(title または description)が 欠落したリクエストボディとともに `/tasks` へ POST リクエストを 送信する、THE Task_Service SHALL Standard_Error_Format の エラーメッセージとともに HTTP 400 レスポンスを返す 3. THE Task_Service SHALL 新しく作成された各 Task に、一意の識別子、 `createdAt` タイムスタンプ、`updatedAt` タイムスタンプを割り当てる 4. THE Task_Service SHALL 新しい Task の初期ステータスを "pending" に設定する ### 要件 2: 単一タスクの取得 **ユーザーストーリー:** クライアントとして、識別子を指定して 特定のタスクを取得したい。その詳細を閲覧できるようにするためである。 #### 受け入れ基準 1. WHEN クライアントが有効なタスク識別子とともに `/tasks/{taskId}` へ GET リクエストを送信する、 THE Task_Service SHALL 一致する Task を HTTP ステータス 200 とともに返す 2. WHEN クライアントが Task_Store に存在しないタスク識別子とともに `/tasks/{taskId}` へ GET リクエストを送信する、 THE Task_Service SHALL Standard_Error_Format の エラーメッセージとともに HTTP 404 レスポンスを返す ### 要件 3: 全タスクの一覧取得 **ユーザーストーリー:** クライアントとして、すべてのタスクを 一覧表示したい。追跡中の全作業項目の概要を確認できるように するためである。 #### 受け入れ基準 1. WHEN クライアントが `/tasks` へ GET リクエストを送信する、 THE Task_Service SHALL Task_Store のすべての Task のリストを HTTP ステータス 200 とともに返す 2. WHEN Task_Store に Task が 1 つも存在しない、 THE Task_Service SHALL 空のリストを HTTP ステータス 200 とともに返す ### 要件 4: タスクの更新 **ユーザーストーリー:** クライアントとして、全体を置き換える形で 既存のタスクを更新したい。タイトル、説明、ステータスを変更できるように するためである。 #### 受け入れ基準 1. WHEN クライアントが有効なタスク識別子と、すべての必須フィールド (title、description、status)を含むリクエストボディとともに `/tasks/{taskId}` へ PUT リクエストを送信する、 THE Task_Service SHALL Task_Store 内の一致する Task を完全に 置き換え、更新後の Task を HTTP ステータス 200 とともに返す 2. WHEN クライアントが Task_Store に存在しないタスク識別子とともに `/tasks/{taskId}` へ PUT リクエストを送信する、 THE Task_Service SHALL Standard_Error_Format の エラーメッセージとともに HTTP 404 レスポンスを返す 3. WHEN クライアントが必須フィールド(title、description、status) のいずれかが欠落したリクエストボディとともに `/tasks/{taskId}` へ PUT リクエストを送信する、THE Task_Service SHALL Standard_Error_Format のエラーメッセージとともに HTTP 400 レスポンスを返す 4. THE Task_Service SHALL Task が変更されるたびに `updatedAt` タイムスタンプを更新する ### 要件 5: タスクの削除 **ユーザーストーリー:** クライアントとして、タスクを削除したい。 不要になった作業項目を完全に除去できるようにするためである。 #### 受け入れ基準 1. WHEN クライアントが有効なタスク識別子とともに `/tasks/{taskId}` へ DELETE リクエストを送信する、 THE Task_Service SHALL Task_Store から Task を完全に削除し、 HTTP ステータス 204 を返す 2. WHEN クライアントが Task_Store に存在しないタスク識別子とともに `/tasks/{taskId}` へ DELETE リクエストを送信する、 THE Task_Service SHALL Standard_Error_Format の エラーメッセージとともに HTTP 404 レスポンスを返す ### 要件 6: リクエストの検証 **ユーザーストーリー:** クライアントとして、不正なリクエストに 対して明確なエラーレスポンスがほしい。API 呼び出しの問題を 修正できるようにするためである。 #### 受け入れ基準 1. WHEN クライアントが無効な JSON ボディを含むリクエストを送信する、 THE API_Server SHALL Standard_Error_Format の エラーメッセージとともに HTTP 400 レスポンスを返す 2. WHEN クライアントが未定義のルートへリクエストを送信する、 THE API_Server SHALL HTTP 404 レスポンスを返す 3. WHEN クライアントが定義済みのルートに対してサポートされていない HTTP メソッドを使ってリクエストを送信する、THE API_Server SHALL HTTP 405 レスポンスを返す ### 要件 7: 入力長の検証 **ユーザーストーリー:** クライアントとして、API に妥当な入力長を 強制してほしい。過度に大きな入力が拒否されるようにするためである。 #### 受け入れ基準 1. WHEN クライアントが 100 文字を超える `title` フィールドを含む リクエストを送信する、THE Task_Service SHALL title が長すぎる ことを示す Standard_Error_Format のエラーメッセージとともに HTTP 400 レスポンスを返す 2. WHEN クライアントが 1000 文字を超える `description` フィールドを 含むリクエストを送信する、THE Task_Service SHALL description が 長すぎることを示す Standard_Error_Format のエラーメッセージと ともに HTTP 400 レスポンスを返す 3. THE Task_Service SHALL 作成(POST)操作と更新(PUT)操作の 両方で入力フィールドの長さを検証する ### 要件 8: タスクのデータモデル **ユーザーストーリー:** クライアントとして、一貫したタスクの データ構造がほしい。API レスポンスを確実にパースできるように するためである。 #### 受け入れ基準 1. THE Task_Service SHALL 各 Task を次のフィールドを持つ JSON オブジェクトとして表現する: `id`(string)、`title`(string)、 `description`(string)、`status`(string)、 `createdAt`(ISO 8601 タイムスタンプ)、 `updatedAt`(ISO 8601 タイムスタンプ) 2. THE Task_Service SHALL Task の status フィールドとして次の値を 受け入れる: "pending"、"in-progress"、"completed" 3. IF クライアントが受け入れ可能な集合に含まれない status 値を 指定する、THEN THE Task_Service SHALL Standard_Error_Format の エラーメッセージとともに HTTP 400 レスポンスを返す ### 要件 9: JSON シリアライズのラウンドトリップ **ユーザーストーリー:** クライアントとして、シリアライズと デシリアライズを通じて API がタスクデータを忠実に保持してほしい。 データが失われたり破損したりしないようにするためである。 #### 受け入れ基準 1. FOR ALL 有効な Task オブジェクトについて、Task を JSON へ シリアライズし、その JSON を再び Task へデシリアライズすると、 同等の Task オブジェクトが得られる SHALL(ラウンドトリップ特性) 2. THE Task_Service SHALL すべてのリクエストボディとレスポンス ボディの唯一のデータ交換形式として JSON を使用する 3. THE Task_Service SHALL すべてのレスポンスに `Content-Type: application/json` ヘッダーを含める ### 要件 10: 標準エラーレスポンス形式 **ユーザーストーリー:** クライアントとして、すべてのエラー レスポンスが一貫した JSON 構造に従ってほしい。エラーを確実に パースして処理できるようにするためである。 #### 受け入れ基準 1. THE Task_Service SHALL すべてのエラーレスポンスを Standard_Error_Format で返す: `{"error": "<説明的なメッセージ>"}` 2. THE Task_Service SHALL すべてのエラーレスポンスに `Content-Type: application/json` ヘッダーを含める 3. THE Task_Service SHALL 各エラーレスポンスの `error` フィールドに 人間が読めるエラーの説明を提供する ### 要件 11: コンテナの構成 **ユーザーストーリー:** 開発者として、妥当なデフォルト値を備えた Docker コンテナとしてアプリケーションをパッケージ化したい。 システムがポータブルで、任意のプラットフォームに簡単にデプロイ できるようにするためである。 #### 受け入れ基準 1. THE Dockerfile SHALL API_Server と Task_Service を、必要なすべての 依存関係とともに単一の Docker イメージにパッケージ化する 2. THE Dockerfile SHALL イメージサイズを最小化するため軽量な Python ベースイメージを使用する 3. THE Container SHALL API_Server が HTTP リクエストを受け付けるための 設定可能なポート(デフォルトは 8000)を公開する 4. THE Container SHALL データベース接続設定とサーバーポートについて、 環境変数による構成をサポートする 5. THE Dockerfile SHALL API_Server が稼働しているときに HTTP ステータス 200 を返すヘルスチェックエンドポイントを `/health` に含める ### 要件 12: データベースの移植性 **ユーザーストーリー:** 開発者として、アプリケーションに標準的な リレーショナルデータベースを使ってほしい。デプロイ環境に合った データベースバックエンドを選べるようにするためである。 #### 受け入れ基準 1. THE Task_Service SHALL SQL ベースのデータベースアクセスを使用し、 Task_Store を SQLite、PostgreSQL、その他の SQL 互換データベースで 支えられるようにする 2. THE Task_Service SHALL 外部データベース接続が構成されていない場合、 Task_Store としてデフォルトで SQLite を使用する 3. WHEN 環境変数を通じて外部データベース接続文字列が提供される、 THE Task_Service SHALL Task_Store として指定されたデータベースに 接続する 4. THE Task_Service SHALL 起動時にテーブルが存在しない場合、必要な データベーステーブルを自動的に作成する 要件が揃ったら、Kiro はそれらを設計ドキュメントに、最終的にはタスクリストへとまとめ上げてくれます。各タスクを実行するたびに、TDD hook が Kiro に red-green-refactor サイクルでの作業を強制します。 例として、API の GET /tasks/{task_id} のルートハンドラーを構築するタスクを Kiro に実行させてみました。Kiro は、この機能をサポートするコードを書く前に、まず失敗するテストを書かなければならないと判断します。 Kiro はテストを構築し、それらが失敗することを確認します。これは red フェーズが正常に完了したことを意味します。次にコードを修正してテストを再実行し、テストがパスすることを確認します。これにより green フェーズが正常に完了したことが確認できます。新しいテストがパスしたら、Kiro は既存のすべてのテストを再実行し、それらが引き続き正しく動作することを検証します。 まとめ 長年の間、私は TDD と格闘してきました。コード品質の向上、バグの減少、リファクタリングへの自信といった利点を信じてはいましたが、現実は疲れるものでした。絶え間ないコンテキストスイッチ、red-green-refactor サイクルを守り続けるために必要な規律、そして実装の前にテストを書くことの苦痛が、TDD を有益なプラクティスというより負担に感じさせていました。 Kiro はこの状況を完全に変えてくれます。シンプルな hook を使うことで、TDD に必要な規律を自動化できました。hook があれば、Kiro はステップを飛ばしたり近道をしたりすることを許しません。私が意識しなくてもサイクルを守らせてくれます。コンテキストスイッチの負担、プロセスを守り続けるための規律、良いテストを書くことの単調さといった重荷を背負うことなく、TDD の恩恵を受けられるのです。本記事で示したように、モンティ・ホール問題のような単純なデモであっても、数十もの要件を持つ本番環境向けの REST API であっても、hook が TDD のプロセスを徹底してくれます。 私にとって、これこそが TDD のあるべき姿です。Kiro は、負担なしですべての恩恵を与えてくれます。 このアプローチをご自身で試してみたい方は、本記事の hook 設定をコピーして、ご自身の Kiro プロジェクトで使ってみてください。小さなプロジェクトから始めて、感触を確かめ、自分のやり方に合うように hook の指示を調整してみてください。これまで TDD で苦労してきた方や、コード品質を向上させる方法を探している方は、Kiro の hook を使った TDD をぜひ試してみてください。
こんにちは!DevRelの重田( @Shige0096 )です。 メドレーでは夏企画として『MEDLEY Summer Tech Blog Relay』と題して、ブログリレーを開催します! 7/13(月)〜8/21(金)まで毎日異なるメンバーが技術やエンジニアリング、個人開発など幅広いテーマでテックブログを公開していきます! 本記事にて毎日ブログを追記更新していくので、ぜひお楽しみください✨ ※土日祝を除く 👇よろしければ昨年の記事もぜひチェックしてみてください! MEDLEY Summer Tech Blog Relay | MEDLEY Developer Portal こんにちは!メドレーでDevRelをしている重田です。 今年も暑い日が続いていますがいかがお過ごしですか? メドレーでは夏企画として『MEDLEY Summer Tech Blog Relay』と題して、ブログリレーを開催します! 8/2... developer.medley.jp 🌻 ブログリレーカレンダー 🗓️第1週(7/13~7/17) Day1:(仮)🍄(玉井) Day2:Fivetranについて(本多) Day3:TBD(森川) Day4:BigQueryのコスト削減周り(林田) Day5:TBD(宋) 🗓️第2週(7/21~7/24) Day6:FY25 CLINICS大規模障害対応で認証基盤の刷新をした時に得た学びについて(山田) Day7:ローカルLLMの個人での活用について(斎藤) Day8:自作ハーネスとローカルLLMをゴリゴリ(福島) Day9:TBD(稲村) 🗓️第3週(7/27~7/31) Day10:Rubyで組み込み・ゲーム・AIのどれか!(藤原) Day11:TBD(平林) Day12:TBD(高橋) Day13:(仮) AI API 叩くとき考えること(山下) Day14:Jetson Orin Nano Super によるローカルMLLM活用について(山本) 🗓️第4週(8/3~8/7) Day15:Maestro E2E(菅原) Day16:TBD(奥澤) Day17:TBD(佐藤) Day18:TBD(牧) Day19:TBD(久保) 🗓️第5週(8/10~8/14) Day20:TBD(柏木) Day21:TBD(山本) Day22:TBD(村上) Day23:TBD(竹本) 🗓️第6週(8/17~8/21) Day24:TBD(山河) Day25:TBD(亀澤) Day26:TBD(清水) Day27:メドレーにおける0→1開発のリアルな話(エージェント業務システム)(德永) Day28:TBD(倉林) 🍉 We’re hiring! メドレーでは、「医療ヘルスケアの未来をつくる」仲間を大募集しています! 少しでも興味をお持ちいただけましたら、ぜひ、カジュアル面談にお越しください🙌 ご応募お待ちしております!! メドレーで働く|株式会社メドレー メドレーでの働き方や人事制度、求人情報など、採用に関する情報をご紹介します。 www.medley.jp
はじめに 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やプログラミングが大好きな人達の「熱」を感じられたことでした。普段「仕事」としてプログラミングをしていると商業的な観点で価値を測り測かられる癖がついてしまって、楽しいとか知的探求という側面を忘れていたなぁと思いました。 また、現地での他社のエンジニアとの交流も刺激的でした。今まで自分がマイナビ色に染まっている自覚はありませんでしたが、他社のエンジニアと交流すると明らかに各社個性というか雰囲気があって、たまには視野を広げるためにも交流して新しい知見を持ち帰ってくる必要があると感じました。

動画

該当するコンテンツが見つかりませんでした

書籍