メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックカンパニーのエンジニアブログです。読者に有用な情報発信ができるよう心がけたいので応援のほどよろしくお願いします。

#RubyKaigi 2026 で発表されたRubyのAOT Compiler Spinelで作ったものを自慢しよう!

こんにちは、メドピアのサーバーサイドエンジニアの草分( @Lni_T )です。

RubyKaigi 2026参加者の皆さん、お疲れ様でした。 今回は北海道は函館での開催ということで、名所にグルメに楽しいことが目白押しでしたが、皆さん満喫できましたでしょうか?

草分は函館だけでなく小樽や札幌まで足を伸ばしていました。北海道、また行きたいですね。

海の幸を満喫

Spinel

さて、今回の本題はSpinelです。RubyKaigi最終日、Matzさんのキーノートで発表され、話題となったツールですね。

github.com

SpinelはRubyのAOT Compilerで、Rubyコードをスタンドアロン実行可能なネイティブ実行ファイルにコンパイルすることが可能です。

処理の流れとしては以下のように説明されています。

Ruby ソースコード (.rb)
    ↓
spinel_parse:Prism を使用した解析
    ↓
AST テキストファイル
    ↓
spinel_codegen:型推論および Cコードの生成
    ↓
C ソースコード (.c)
    ↓
cc -O2 -Ilib -lm:標準の Cコンパイラ + ランタイムヘッダー
    ↓
ネイティブバイナリ:スタンドアロン(実行時の依存関係なし)

そう、RubyのインストールなしにRubyコードが動作するようになります。

とはいえ、もちろん制約はあり、evalやメタプログラミングなどはサポートされていない旨がREADMEに記載されています。 「Railsサーバーがそのまま動く!」といったソリューションではないようですね。

作ったものを自慢しよう!のコーナー

それでは、Spinelにはどこまでのパワーがあるのでしょうか? ちょっとひねったものを実装して確かめてみましょう。

1. ○×ゲーム(Tic Tac Toe)

実装

ソースコード

気づき

Ruby版/Spinel版で挙動が変わるケースがある

# array.rb
ary = Array.new(3, nil)
puts ary.inspect

上記のコードは、Rubyで実行した場合とSpinelでコンパイルした場合で結果が異なります。

$ ruby array.rb
[nil, nil, nil]
$ ./array
[0, 0, 0]

nilで初期化した配列ですが、Spinel版では0が出力されます。RubyにはNIlClassがありますが、C言語のnullにそのような概念はないせいか、どうも0として扱われてしまうようです。

この辺りの挙動はC言語に変換されている以上は致し方ない所でしょうか。適宜、Rubyコードとコンパイル済バイナリの実行結果を比較しつつ実装すると安全に開発できそうですね。

型の制約

Rubyでは型宣言などを書かず、やりたい放題プログラムを実装できますが、 Spinelを利用する場合、少々型の制約を受けてしまうようです。

# apple.rb
ary = Array.new(3, nil)
ary[0] = "apple"
puts ary.inspect

例えば、上記のコードはSpinelではコンパイルエラーになります。

$ ../spinel apple.rb
/tmp/spinel_out.XXXXXX.c:18:32: error: incompatible pointer to integer conversion passing 'char *' to parameter of type 'mrb_int' (aka 'long long') [-Wint-conversion]
    sp_IntArray_set(lv_ary, 0, (&("\xff" "apple")[1]));
                               ^~~~~~~~~~~~~~~~~~~~~~

0(nilだったはずのもの)で初期化したint配列に文字列を代入しようとしたため、エラーになってしまうようですね。

以下のように、型を合わせることでコンパイルを通すことができます。

ary = Array.new(3, "")
ary[0] = "apple"
puts ary.inspect

2. スネークゲーム

実装

ソースコード

気づき

system が使える

Rubyで外部コマンドを実行する Kernel.#system も動作します。 Spinel単体ではsleepが整数秒単位でしか動作しないなどの制限もあり、このデモコードでは外部コマンドに頼っています。

しかし、この方法は環境依存要因が増えてしまいますね。せっかくスタンドアロン実行可能形式にコンパイルしたのに、これでは本末転倒かもしれません。

おわりに

ちょっとしたツールを作って運用する場合「Goでビルドする」手段が強力です(最近ならRustも選択肢に)。また、さらに前の時代であれば「Perlでスクリプトを書く」というのも有力で、Rubyでツールを作るケースは少なかったように思います。

こうしたユースケースに対してRubyも参入できるのかも!と思うと、なかなか夢が広がるツールだなぁと感じています。今後の動向が楽しみですね!


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら hrmos.co

■エンジニア紹介ページはこちら engineer.medpeer.co.jp

■メドピア公式YouTube  www.youtube.com

■メドピア公式note
style.medpeer.co.jp

#RubyKaigi 2026 セッションレポート

皆様こんにちは、メドピアのサーバーサイドエンジニアの内藤(@naitoh)です。

RubyKaigi 2026 に参加されていた皆さん、お疲れ様でした。 開催直前の地震や羽田空港の管制システムトラブルで最初はどうなることかと思いましたが皆様無事に辿り着けましたでしょうか。 自分は飛行機が欠航しましたので、急遽、新幹線に振替を行い北海道新幹線のありがたさを噛み締めました。

RubyKaigi 2026

いろいろありましたが、RubyKaigi のセッションの中で特に印象に残ったセッションをご紹介させて頂きます。

タイムテーブルは下記から確認ください。

rubykaigi.org

Portable and Fast - How to implement a parallel test runner

最初に Red Data Tools での開発の取り組みで「テストを高速化できないか」という気づきが発端になったという Portable and Fast - How to implement a parallel test runner を紹介させて頂きます。

rubykaigi.org

Rails な皆さんはテスティングフレームワークとしてたいてい RSpec (もしくは Rails 標準の minitest)を使っていて、test-unit はご存知ないのではと思いますが、今回は gem として開発が続いている test-unit の並列化を並列実行させて早期にテスト完了させる取り組みになります。

参考: Rubyのテスティングフレームワークの歴史(2014年版)

ちなみに、並列テストランナーとして、RubyKaigi 2023 の The Resurrection of the Fast Parallel Test Runner が今回の話の参考になります。

その中で、ActiveSupoort::TestCase で Minitest を使用している場合はスレッドベースで並行テストが可能。 Rails 6 以降で ActiveSupoort::TestCase で Minitest を使用している場合はデフォルトでプロセスベース(fork)で並列テストが有効になります。 プロセス間の同期には drb を使用しています。 ただ皆さんがよく使われている RSpec では並列テストはサポートされていないのが課題ということでした。

そこで並列テストランナーの test-queue は、RSpec や Minitest、test-unit などの各種テスティングフレームワークに対して、キューを分割させる形でプロセスベースでUNIX domain Socket or TCP/IP Socket を用いてプロセス間の同期を行い並列で動作させる事で、テストを早期に完了させることができるという話でした。

test-queue を使えば、test-unit も並列動作可能なのですが、各種テスティングフレームワークの内部APIを使用しているので内部APIが変更になると動作しなくなるリスクがあるので、最終的には各種テスティングフレームワーク自身で並列テストできる事が望ましいと最後に述べられています。

というわけで、前置きが長くなりましたが、今回は test-unit 単体で並列テストを可能にする取り組みです。

slide.rabbit-shocker.org

Portable (互換性) と Fast (テスト全体を効率良く早く完了させる)事を念頭に開発されたそうです。

  • Portable : test-unit 自身がサポートしている Windows もサポートする必要がある。(fork ではなくWindows でもサポートされている spawn を使用する)
    • spawn は親プロセスの状態を引き継がないので、親プロセスとの通信の仕組みを作る。
    • プロセス間通信の仕組みで drb があるが、Ruby 3.3 以降で Bundled gem になっているため、外部ライブラリへの依存しない方針のため不採用。
    • pipe を使ってプロセス間通信を行う。
      • Unix: IO.pipe を用いた片方向通信のペアでの MainプロセスとWorkerプロセス間の個別の双方向通信。 (Marshal#dump/load でオブジェクトをやり取り)
      • Windows: TCP/IP Socket を使った通信。
  • Fast : 各Workerの処理状況を知っているのは各Worker自身なので、キューに並べられたテスト対象を、各Workerが自分でPullしテストを実行するため、遊んでいるWorkerが発生しない形にしている。

スレッドベースではなくプロセスベースにすることで、各Workerが効率良くテストを実行できる点や、テストを各Worker自身がPull する仕組みが効率的良さそうな点と、外部依存を極力排除しているので導入しやすさの点でよく考えられている印象でした。

ただ、Windows で TCP/IP Socket を使うのであれば、test-queue の様に UNIX domain Socket & TCP/IP Socket の組み合わせ方が処理的に似ているのでメンテナンスし易そうに思いました。というわけで、いろいろなプロセス間通信の特徴が理解できました。

Exploring RuboCop with MCP

次は MCPサーバー & クライアントの Ruby-SDK のお話です。

rubykaigi.org

speakerdeck.com

MCP は JSON-RPC を用いるのは知っていましたが、トランスポートの仕組みに stdio と Streamable HTTP (HTTP + SSE の拡張)という仕組みを用いるんですね。 ここで Rack 3 の streaming body を使って SSEに対応しているのが面白いですね。

Rubocop x MCP の取り組みの紹介で、cli での rubocop -A で自動修正 や、 cli の結果をAIエージェントが解釈すればいいのでは? 🤔 と思ったのですが、 Support built-in MCP server by koic · Pull Request #14911 · rubocop/rubocop · GitHub を確認するとエージェントはCLI出力を解析するのではなく、構造化された方法で(定義されたスキーマを使用して) RuboCopに問い合わせる事ができるので、より確実に処理する事が狙いだそうです。AI専用の応答を MCP経由で返すインターフェースですね。

弊社内でも管理画面にDB参照用のMCPサーバーを組み込むことで、AIエージェントからDBに対してSQLを発行*1し障害調査に使えるようになっており、今回の開発は大変ありがたいです。

Surviving Black Friday: 329 billion requests with Falcon!

続いてFalconを2025年のBFCM*2に本番投入したお話です。

rubykaigi.org

speakerdeck.com

Falcon はFiberベースで、I/Oバウンドなリクエストを効率良く処理するwebサーバーです。CPUバウンドなリクエストには効果はありません。*3

本番導入するにあたって、コードがFiber Safeである必要があります。

  • スレッドをブロックするC拡張(rdkafka, gRPC, etc)は、そのWorker上の全てのFiberをブロックする。

ただ、どのようなケースで問題が発生するか事前には不明だったため、スケールテストや、カナリアリリースで問題が発生時は即座にUnicorn に切り戻す形で検証を実施することで、上記の問題を検出 & 修正しアップストリームにフィードバックされたとのことなので、途中から Falcon に切り替える場合は同様の検証が必要になりそうです。

なお、最初はUnicornモード(Worker毎に1リクエスト)で実施したとこのこと。これは Unicorn から Puma への移行でも同じ感じですね。

最終的なスループットの改善はUnicorn と比較して1割程度と控えめですが、Shopify ならその削減は相当なものになるでしょうし、Shopify レベルの環境で Falcon の本番導入実績ができたのはすごいですね。

おわりに

3日間にわたる RubyKaigi 2026 が終了しました。 最後は AOT コンパイラの Spinel に話題を持って行かれた気がしましたが、Ruby にはまだまだ進化の余地がありそうなので楽しみですね!

次回のRubyKaigiは 2027年4月14日から4月16日、場所は宮崎県宮崎市です。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら hrmos.co

■エンジニア紹介ページはこちら engineer.medpeer.co.jp

■メドピア公式YouTube  www.youtube.com

■メドピア公式note
style.medpeer.co.jp

*1:GRANT で個人情報等は参照できないように制限しています。

*2:Black Friday, Cyber Monday

*3:複数コアを活用するなどCPUバウンドなリクエストに効果がありそうなのは Ractor です。

第二創業期のメドピアが定義する、次世代エンジニアの「三つの責任」

こんにちは。三村(@t_mimura39)です。

メドピアはいま、大きな変革の真っ只中にいます。 2024年の創業20周年や代表交代、そして2025年のMBO。こうした大きな節目を経て、自らを「第二創業期」の只中にあると定義しています。 そんな中で私は、プリンシパルエンジニアの一人として「プロダクト開発組織の未来図」を描き、その実現をリードしています。評価制度や開発フロー・ルールの刷新など、あらゆる変革の根底にあるのは、一つの極めてシンプルな問いです。

「AIが実装を担う未来において、エンジニアは何に責任を持つべきか?」

今回はその問いに対しての一つの答えを書き記したいと思います。

目次


1. はじめに「なぜ今、この責任を定義するのか」

生成AIの普及により、コードを「書く」という行為は急速に自動化されつつあります。これからのエンジニアの存在意義は、実装という作業そのものではなく、「技術によって、託されたプロダクトの品質を保証すること」に一本化されていきます。

事業の推進者がエンジニアを信頼し、プロダクトという事業の心臓部を託すための共通言語として、「プロダクト開発」を主眼に置くエンジニアが果たすべき「三つの責任」を定義します。


2. 三つの責任

① 構造の責任

「正しく、AIや人間が文脈を理解しやすい秩序を保つ責任」

  • 責任の本質
    • 変化に強い骨格を設計し、AIが生成する膨大なコードによってシステムの秩序が失われるのを防ぐ。状況に応じて最適な結合度を選択し、開発速度と保守性のバランスを制御する。
  • 必要なスキル例
    • ドメインモデリング: ビジネスの本質を、シンプルで強固なデータ構造に落とし込む力。
    • アーキテクチャ設計: 規模やフェーズに応じ、構成の分割単位と依存関係を定義する力。保守性と速度を天秤にかけ、チームの認知負荷に適した「最適な複雑さ」を選択する。
    • 技術的ガバナンス: 命名規則や設計思想の一貫性を守り、AIや人間が迷わず文脈を理解できる「ノイズのない場」を維持する力。
  • 具体的なアクション例
    1. 解決するべき複雑な事象を整理し、単なるデータの保存ではない「ビジネスルールを表現する」強固なデータ構造を定義することで、システムの整合性を担保する。
    2. 「疎結合」を目的化せず、チームの状況を考慮して、あえてシンプルさを優先するか、抽象化を導入して独立性を高めるかの損益分岐点を見極め、構成を決定する。
    3. 設計思想と実装の乖離を埋めるリファクタリングを主導し、AIや後続のエンジニアが「推測」なしに改修できる透明性の高いコードベースを維持し続ける。

② 価値成立の責任

「技術をプロダクトの価値へ翻訳し、確実な形として具現化する責任」

  • 責任の本質
    • 「仕様書通り」の遂行に留まらず、技術の力で事業の可能性を拡張する。ビジネスの意図を咀嚼した上で、プロダクトが進むべき理想の姿を自ら定義し、最短・最良の形で価値へと昇華させる。
  • 必要なスキル例
    • 技術要素の統合力: 自身の専門を軸としつつ、インフラからフロントエンドまでを地続きに捉え、ユーザー体験を成立させるために必要な要素を繋ぎ合わせる力。
    • プロダクト価値の定義: 要求の背景にある課題を技術的に解釈し、プロダクトとしての「あるべき品質や機能」を逆提案する力。
    • 仮説検証の設計力: 最初から完璧を目指すのではなく、ビジネスの仮説を最速で検証するための「価値の核」を特定し、素早いフィードバックループを回すための構成を提案する力。
  • 具体的なアクション例
    1. 自身の担当領域を仕上げた上で、APIの挙動や画面の操作感に矛盾がないかE2Eで点検し、必要であれば領域外の修正まで踏み込んで「体験」を完成させる。
    2. 不確実性の高い新機能において、フルスペックの開発に入る前に「価値の核」を確認するための最小実装(プロトタイプ)を提案・構築し、実戦での検証結果をもとに素早く軌道修正を行う。
    3. 実装段階で仕様の不備(あるべきプロダクト価値との乖離など)に気付いた際、専門家として代替案を提案・議論し、最適化する。

③ 継続性の責任

「プロダクトが社会的に生存し続け、信頼を維持するための責任」

  • 責任の本質
    • セキュリティや信頼性、そして経済的合理性を後回しにせず、プロダクトが持続するための絶対条件として維持する。
  • 必要なスキル例
    • 経済的最適化: インフラ構成やリソース消費をモニタリングし、パフォーマンスを維持しつつコストを最小化する力。
    • レジリエンスと可観測性: 異常の予兆を検知し、復旧プロセスをエンジニアリングで自動化する力。
    • セキュリティ・バイ・デザイン: 扱うデータの重要性やリスクを予見し、設計段階から堅牢な保護の仕組みを組み込む力。
    • ライフサイクル管理: 技術負債を適切に管理し、計画的なアップデートによってシステムの陳腐化を防ぐ力。
  • 具体的なアクション例
    1. リソースの利用効率を追求し、モデルの選定やキャッシュ戦略の最適化を通じて、事業利益を圧迫しない技術構成を実現する。
    2. SLO/SLIを定義し、「人が介入すべき真の異常」のみを抽出するアラート設計と、復旧手順の自動化を推進する。
    3. 認証基盤やデータ保護など、社会的な信頼に応えるためのセキュリティ基準を満たすアーキテクチャへの継続的な投資と改善を行う。

3. 実装者から「価値の設計者」へ

これらの責任は、生成AIが登場する以前からエンジニアリングの根幹を成すものでした。しかし、AIがコード生成の大部分を肩代わりする現在、その重要性はかつてないほど高まっています。

これからのエンジニアは、コードを書くという「手段」の習熟から解放され、エンジニアリングの「本質」に対してのみ注力する時代へと突入します。

  • 「作る」から「成立させる」へ

要件をコードへ翻訳するだけの段階は終わりました。これからのエンジニアリングとは、実装の先にある「構造の妥当性」や「経済的な持続性」を束ね、プロダクトを事業として成立させることそのものを指します。実装は責任を果たす過程で生まれる成果物であり、目的そのものではありません。

  • 「何を託すか」から「誰に託すか」へ

AIがコードを書ける時代だからこそ、事業推進者が抱く問いは「何ができるか」から「誰にこの事業の心臓部を託すべきか」へと変わります。三つの責任を引き受け、プロダクトの命運を担える「設計者」であること。それこそが、変化の時代において私たちが信頼され続けるための確かな指針です。


4. 専門性を起点とした、境界なきオーナーシップ

私たちは、個々のエンジニアが持つ深い専門性(モバイル、バックエンド、SREなど)を、三つの責任を果たすための強力な「武器」であると定義します。しかし、専門性を磨くことは、自らの貢献を特定の領域に閉じ込めることではありません。

  • 専門性は「目的を達成するための起点」

自身の得意とする領域において、卓越した品質で構造を保ち、価値を成立させ、継続性を守ることはエンジニアとしての「ベースライン」です。しかし、私たちの真の目的は技術の出力そのものではなく、プロダクト価値の完遂にあります。

  • 「価値の完遂」が行動の射程を決める

例えば「② 価値成立」の責任を果たす上で、自身のメイン領域の修正だけでなく、APIの仕様変更やデータベースの調整、あるいはインフラの構成変更が必要であれば、技術スタックの境界を自ら越えて課題解決に当たることを求めます。

  • 技術領域に安住しない

「ここから先は自分の領域ではない」という心理的な境界線を設けず、「プロダクトを成功させるために、今、解決すべき課題は何か」を最優先する姿勢が重視されます。専門性は、自身の貢献を規定するための枠組みではなく、より広範な課題を解決するための確固たる足場として機能させます。


5. 結びに「なぜこれらの責任が信託の基礎となるのか」

事業の推進者はエンジニアに「コード」を頼むのではありません。「技術を通じて、この事業を成功させてほしい」という願いを託します。

  • 構造があるから、明日も加速できる。
  • 価値成立があるから、ユーザーに届く。
  • 継続性があるから、安心して使い続けられ、事業が成り立つ。

この三つの責任を果たすこと。それこそが、私たちが専門家として信頼を受け、プロダクトに命を吹き込むための証明です。コードの先にある、事業の未来を共に創りましょう。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら hrmos.co

■エンジニア紹介ページはこちら engineer.medpeer.co.jp

■メドピア公式YouTube  www.youtube.com

■メドピア公式note
style.medpeer.co.jp

Rails8.0にアップグレードしたらinsert_allの振る舞いが変わった

こんにちは。メドピアのお手伝いをしている@willnetです。最近寒くなってきましたね。毎年この時期に風邪をひくので最近は手洗いうがいを怠らないようにしています。このたび久しぶりにテックブログに寄稿します。

insert_allの仕様変更に関するPR

メドピア内のとあるRailsアプリケーションのバージョンを7.2から8.0にアップグレードしたときにinsert_allの仕様変更に気づいた話をします。 insert_allの仕様変更に該当するのは次のPRです。

Fix active record insert values of type cast and serialize by OuYangJinTing · Pull Request #48139 · rails/rails

上記PRではsaveメソッドとinsert_all(及びinsertinsert_all!)メソッドで値の変換方法が一致していないのを修正しています。PRではString型のカラムにArrayやHashをアサインして変更内容を検証していますが、この変更でDateTime型やTime型のカラムの振る舞いも変わることがわかりました。

変更内容

前提として、次のようにRailsのタイムゾーンを"Tokyo"にしています。

class TestApp < Rails::Application  
  config.time_zone = "Tokyo"
end

そのうえで次のようにstart_timeカラム(Time型)、start_atカラム(DateTime型)に値をいれます。どちらも文字列を利用しているのがポイントです。

Post.insert_all([{ start_time: "12:00:00", start_at: "2025-10-29 12:00:00" }])

するとRails7.2では次のようなクエリが発行されます

INSERT INTO "posts" ("start_time","start_at") VALUES ('2000-01-01 12:00:00', '2025-10-29 12:00:00') ON CONFLICT  DO NOTHING RETURNING "id"

Rails8.0ではこうです。

INSERT INTO "posts" ("start_time","start_at") VALUES ('2000-01-01 03:00:00', '2025-10-29 03:00:00') ON CONFLICT  DO NOTHING RETURNING "id"

INSERTする時刻が9時間ズレてしまいました。

もともとRailsはsave時にタイムゾーン設定を考慮して、DBにはUTCに変換した時刻を入れるようになっています。しかしinsert_allを利用したときはsaveと振る舞いが違っていました。ActiveSupport::TimeWithZoneオブジェクトがアサインされたときはUTCへの変換が行われますが、文字列がアサインされたときにはタイムゾーン変換を行わずにクエリを発行しています。Rails8.0ではこの問題が解消されてinsert_allsaveが同じように振る舞います。

感想

これはバグフィックスとしては妥当な変更だと思いますが、結果として発行するクエリが変わるので、アップグレードガイドなどに変更する旨を記載するか設定で振る舞いを切り替えられるとより良かったように思えます。Rails8.0がリリースされる前にこの仕様変更に気づいてPRを出すべきだったので、やっぱり「定期的にRailsのedgeでCIを実行する」は実施すべきプラクティスだよな〜という気持ちを新たにしました。

この記事が今後Rails8.0にアップグレードする方の参考になれば幸いです。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら medpeer.co.jp

■エンジニア紹介ページはこちら engineer.medpeer.co.jp

■メドピア公式YouTube  www.youtube.com

■メドピア公式note
style.medpeer.co.jp

Vue Fes Japan 2025 に参加してきました!

こんにちは、エンジニアの野里です。 今回は 2025年10月25日に開催された Vue Fes Japan 2025 に参加してきたレポートになります。

Vue Fes Japan 2025 のパネル前での集合写真

会場の雰囲気

私が参加してきたイベントの中では参加者が一番多く、「盛り上がってるなー」と感じました! あと小物なんかがとてもオシャレ!

広い会場でしたが休憩スペースがあったり、飲み物も提供されていたのでとても快適に過ごせました。

ブース出展

今年も弊社はブース出展させていただきました。

メドピア出展ブース前での集合写真

Vue.js、React、Svelte に関するアンケートを実施。 たくさんの方にご回答いただけました!参加していただいた皆様ありがとうございます!

アンケートの写真

「その他・該当なし」では Astro や SolidJS の名を挙げる方もおり、話が盛り上がりました。

参加したセッション等の感想(一部)

オープニング

ムービーがカッコイイ…引き込まれました。

youtu.be

Vue Fes Japan のロゴが変更され、Vue だけでなく Vite などエコシステム全体を表現するデザインになったことなどお話しされていました。 そういったところでも進化しているんだなと感じました。

さらに日英同時翻訳が表示されていて、参加する人が言語関係無くお話し聞けるようになっていたのもすごかったです。

キーノート

Evan You 氏によるキーノートの様子

Evan You さんによるキーノート。

Vue.js の成長、新機能や VoidZero のお話の中でも、少し前に発表されたフロントエンドのツールチェーン統合の Vite+ の話がやはり印象に残りました。 Rolldown や Oxc は触ったことが無いので少しづつでも試していきたいと思います。

生成AI時代のWebアプリケーションアクセシビリティ改善

個人的に大変興味があったやまのくさんのセッション。 AI に指示をしない方がアクセシブルなコードというのが衝撃的でした。

やまのくさんが MCP を作成してくれたりして使ってみたいと思ったのと、自分も勉強していかなくてはと改めて感じました。

フロントエンドの未来を語る ─ React/Vue.js/Svelte が見据える次の 10 年

「フロントエンドの未来を語る ─ React/Vue.js/Svelte が見据える次の 10 年」の様子

今回一番聞きたかったのがこちら。 Evan You さん、Dan Abramov さん、dominikg さんという、なんとも豪華なパネルセッションでした。

注目度も高く、会場は立ち見の方も含めてかなりの人数が参加されていました。

AIによるコーディングが可能になる時代においても、それを行うための知識は必要であり、引き続き学習が重要!これからもやっていくぞ!!と気合が入りました。

終わりに

セッションはもちろん参加している方と会話も出来、とても刺激を受けた一日でした。 Vue.js はもちろん、VoidZero にも注目していきたいです!

運営の皆様、本当にありがとうございました!

パネルにたくさんの寄せ書き


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら medpeer.co.jp

■エンジニア紹介ページはこちら engineer.medpeer.co.jp

■メドピア公式YouTube  www.youtube.com

■メドピア公式note
style.medpeer.co.jp

GitHub ActionsでTerraformを実行する

こんにちは、SREの侘美です。

今回はGitHub Actions上でTerraformを実行する環境を構築したので紹介させていただきます。 もともと利用していた Terraform Cloud から使い勝手や機能を悪化させることなく移行を目指し、様々な工夫を行ったのでそれらも合わせて紹介いたします。

移行の理由

弊社では2020年頃からTerraformの統一的な実行環境としてTerraform Cloudを採用していました。 Terraform CloudはTerraformのplan/applyの実行、チームとアクセス権の管理、モジュールレジストリ機能などTerraformに必要な様々な機能を備えているTerraform開発元であるHashiCorp製のSaaSです。

特に機能面で大きな不満なく利用できていたのですが、2023年5月にTerraform Cloudの料金体系の変更が発表されました。

https://www.hashicorp.com/en/blog/terraform-cloud-updates-plans-with-an-enhanced-free-tier-and-more-flexibility

端的に言うとユーザー数での課金から管理するリソース数での課金に変わるという内容でした。 この発表を受け直近の管理リソース数からコストを試算したところ、新料金プランでは従来の約12倍のコストとなることが判明しました。

ここまでの値上がり幅となってしまったのは、少人数のSREで効率化しながら多数のAWSアカウントを管理できるよう努力してきた成果の表れとも言えます。

弊社では契約タイミングの都合で新料金体系に切り替わるのは2025年10月と比較的猶予がある状況でした。そこで、これを機に別の実行環境への移行を検討し2025年9月に晴れてGitHub Actionsへの移行を完了させました。

前提

先に以下の説明に関連する範囲で弊社のTerraformの運用フローやリポジトリ構成を説明しておきます。

  • リポジトリはサービス単位(約50リポジトリ)
  • リポジトリ内に複数のTerraform Workspace(stg, prd, datadog, etc...)
  • PR上で terraform plan を実行
  • デフォルトブランチにマージ後、 terraform apply を実行

構成

まずは移行前のTerraform Cloudを採用した構成と、移行後である現在のGitHub Actionsを採用した構成をご紹介します。

Before

Terraform CloudでTerraformを実行

特に特殊な使い方をしているわけではなく、一般的なTerraform Cloudの利用方法をしていました。 リポジトリ内で envs/stg envs/prd のようにディレクトリを分けて、それぞれのディレクトリをTerraform Cloudのworkspaceに対応させる形で利用していました。

After

GitHub ActionsでTerraformを実行

移行後もTerraform Cloudのworkspaceがそのまま同リポジトリ内のActionsのジョブに置き換わった形になります。 なお、Terraform Cloudはモジュールのレジストリ機能のみ今後も継続して利用するため残しています。

工夫点

ここからは移行に際して、使い勝手を落とさないため/既存の課題を解決するために行った工夫点をいくつか紹介させていただきます。

PR上にplan結果のコメント

plan結果をPull Requestのレビュー時に確認するのは必須となっています。

Before

PR上のChecks

Terraform Cloudの場合、外部のCIなのでPR上のChecksに結果へのリンクが掲載される仕組みでした。 リンクを開いて(未ログインならTerraform Cloudへログインして)plan結果を閲覧するという流れです。

After

PR上のコメント(トグルを開くと詳細なplan差分を確認可能)

Actionsで実行したplan結果は、何も追加の実装を行わなければActionsのログで閲覧するしかありません。 そこで、 suzuki-shunsuke/tfcmt を使いActionsで実行したplan結果をPR上にコメントとして記載するようにしました。

「リンクを開かずともサッとplan差分を見られるようになった」「Terraform Cloudへのログインが不要になった」と開発者からの評判は上々です。

※ PRのコメントなので極端に長いplan差分は一部省略されてしまいますが、レアケースですしActionsのログを確認すれば閲覧可能なのでOKとしました。

applyのレビュー待ちと通知

Terraform Cloudにはapply前に最終plan結果を確認し、そのplan結果でapplyを行ってよいかの確認をはさむConfirmという機能があります。

Terraform CloudのapplyのConfirm画面

PR上のplanが実行された後、別PRまたは手動でAWS上でリソースが変更される可能性はゼロではありません。こういった意図しない変更が入った状態でPRマージ後にapplyを行うと、PR上で確認したplan結果とは異なる差分が発生する可能性があります。

本番環境など、万が一リソースを誤って変更・削除してしまうと一発で障害につながるような環境では、apply直前のplan差分のチェックが必須です。

Actions移行後もこのConfirm機能相当のフローを継続することにしました。 GitHubのEnvironments機能を利用して同等の仕組みを実現可能できそうな目処がたったので、これを採用しました。

  • リポジトリ内のTerraform Workspaceに対応するEnvironmentsを作成する
    • 例: stg, prd, datadog
  • apply前のplan結果確認が必要なEnvironmentsに対してレビュアーを指定する
    • 例: stg, datadog → レビュアーなし / prd → 開発チーム + SREチームをレビュアーに指定
  • マージ後に実行されるplan → applyを行うActionsのジョブにおいて、applyジョブに environment を指定する
jobs:
  plan:
    # 省略

  apply:
    needs: plan

    environment:
      name: prd
      
    # 省略

上記の設定を行うことで、レビュアーが指定されたEnvironmentのみマージ後のplanジョブ完了後に「DeployのApprove待ち」状態となります。

GitHub ActionsでのDeployのApprove画面

参考:

一方でTerraform Cloudの時代から、「開発者が忘れてapply前の確認待ち状態で止まっている」というケースが発生する問題がありました。 Terraform CloudからのSlack通知はあるのですが、メンションがないためその他の通知と共に埋もれてしまう点を改善することにしました。

Terraform Cloudの通知

そこでマージ後にapplyのApproveが必要なジョブに関しては、planジョブの最後でSlackにメンション付きで通知を送るようにActionsを実装しました。 通知対象はマージしたユーザーとなるように設定しています。

今回実装したActionsからの通知

Actionの共通化

弊社ではもともとメタデータ構文機能を使い、Actionsの実装の共通部分を別リポジトリ( actions リポジトリ )に切り出しています。

このリポジトリに新たに terraform-apply terraform-plan terraform-notification などを追加し、各サービスのTerraformリポジトリ上のActionsではこのリポジトリをチェックアウトして uses で指定して利用する形としました。

https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax

さらにactionsリポジトリ側でバージョン番号をtagで管理することで、利用側でclone時にバージョン指定して利用できるようにもしています。 このバージョン固定の仕組みを導入することで、破壊的変更を伴うアップデートもより安全に行うことができるようになりました。(常に最新のコードを参照する仕組みだと、 actionsリポジトリ側で破壊的変更を加えた際に、即座に全利用リポジトリ側を修正する必要がありました。)

      - name: Checkout Actions
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          repository: 'org_name/actions'
          ssh-key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
          path: 'mpg-actions'
          ref: v2.0.0

秘匿情報の管理

秘匿情報の管理に関しても、Terraform Cloud時代にあった課題を解決することができました。

Before

Terraform Cloud上のterraformで扱う秘匿情報のうち、複数のworkspaceに同じ秘匿情報を設定したいケースが存在していました。 主にAWSや各種SaaSの認証情報など、TerraformのProviderを実行するのに必要な権限がここに該当します。

これらは事前に手動でTerraform Cloud上のOrganizationのVariable Setsという形で登録し、利用するWorkspaceから参照可能に紐づけるという形で管理していました。この管理方法を採用することで、Workspaceが増えるたびに手動で設定しなければならない事態は回避できていました。

Terraform Cloudでの秘匿情報の管理

一方で、この構成では手動で秘匿情報を設定する以上「誰がいつなんの値を登録したのか後から確認が困難」「再登録などのために別途秘匿情報を管理する必要がある」といった課題が残っていました。

After

弊社では、GitHub上の一部のリソース(ユーザー、チーム、Branch Protection、etc...)をTerraformで管理しています。今回の移行時にはTerraform関連のリポジトリの設定もいくつかTerraformで管理するようにしました。

GitHubのOrganizationまたはRepositoryの秘匿情報をAPIで扱うために、暗号化の仕組みが提供されています。 Organization/Repositoryから暗号化のための公開鍵を取得し、手元で秘匿情報を暗号化し、その暗号化された値をAPIで送ることでGitHub内部で復号されつつ秘匿情報が登録される仕組みです。GitHubのTerraform Providerからもこの仕組みは利用可能です。 暗号化用のスクリプトを用意し、これを利用してリポジトリに登録するための暗号化をスムーズに行えるように構築しました。

全体の流れは下記の図のようになります。

Terraformを利用したGitHubのSecretsの管理

この仕組みを構築したことで秘匿情報を暗号化してコード管理できるようになり、設定経緯・管理・属人化といった手動設定にまつわるもろもろの課題を解決できました!

参考:

workspace一覧

Terraform Cloudで地味に重宝していたのがWorkspace一覧画面でのapplyエラーの確認です。

Terraform CloudのWorkspace一覧画面

(移行済みなので若干古いUIの時の画像しか用意できませんでした、ご了承ください。)

Confirmが必要なprdなどはきちんとapplyが正常に終了するまでチェックされることが多いですが、Confirm不要でマージしたらplan→applyと自動で流れるworkspaceに関しては、たまにエラーが放置されているケースがあります。

もちろんapply状況のSlack通知を行っているためそちらで気づいて対応するケースが大半ですが、それでも開発者によって、または通知先チャンネルによっては気づかずにエラーが放置されてしまうケースは発生してしまいます。

そこで全workspaceにアクセス権のあるSREメンバーがこのworkspace一覧を見ることで、エラーになったままのworkspaceに気づく、という使い方ができ重宝していました。

さて、Actionsへ移行した場合このworkspace一覧に相当する画面を用意することは困難です。 各apply Actionはそれぞれのリポジトリ内で動作します。GitHub自体にOrganization内のActionsを横断的に表示する機能があれば良かったのですが、現時点ではそのような機能は存在しません。

そこでStatus BadgeをWikiに掲載する方法を採用しました。 幸い、弊社ではOrganization配下のTerraform系のリポジトリのコードをスキャンし、自前で構築した静的解析を行い結果をリポジトリのWikiに掲載するシステムがすでに稼働していました。そこで、この静的解析の機能を拡張し、各Terraform系リポジトリのworkflowファイルを取得→workflowファイル名からStatus BadgeのURLを組み立て→それをWikiに反映という形で実装しました。 この静的解析は定期的に実行されるので、リポジトリやworkspaceが増減しても自動でStatus Badge一覧が更新されます。 Status Badgeはその仕組み上、閲覧するタイミングで最新のジョブの終了状態が反映されるので、これでTerraform Cloud時代のworkspace一覧に相当する一覧ページを再現することができました。

applyジョブのStatus Badgeリスト(Wiki)

※ Status Badgeのリンク先は各リポジトリのActionsページにしてあるので、エラーの確認がスムーズなのも地味に便利ポイントです

移行手順

移行は概ね下記の手順で行いました。 手順をドキュメントにまとめてSREメンバーで手分けして全リポジトリをActionsに移行しました。

  • plan差分が無い状態にする
  • terraform state pull でStateをローカルに取得
  • Terraform CloudからGitHub Actionsへの実装に変更
    • バックエンドをTerraform CloudからS3へ変更
    • plan/apply等のActionsを追加
  • terraform state push で新しいバックエンドのS3にStateをpush
  • PRを出す(Terraform CloudとActions両方でplanが実行される)
  • マージ後、Terraform Cloudを管理しているリポジトリで移行完了したリポジトリのworkspaceを削除するPRを出す
  • SlackのGitHub Appで通知を設定
  • 必要に応じてRemote Stateで参照している側の実装を修正

特にミスが発生することもなくスムーズに全リポジトリで移行できました。

課題

移行に際して既存の課題はあらかた解決できたのですが、一部で新しい課題も出てきたので紹介させていただきます。

ローカルからのplan

Terraform Cloudを実行環境に利用している場合、PRやデフォルトブランチにコードをpushした際のplan/applyの流れは下記の図のようになります。

Terraform Cloudにおけるpushをトリガーとするplan/apply

一方で、ローカルで terraform plan を実行すると、ローカルのコードをTerraform Cloudに転送してplanを実行し、その結果を表示することができます。

Terraform Cloudを利用しているリポジトリでのローカルでのterraform plan

これはTerraform CloudがTerraform開発元であるHashiCorp社製のサービスであるから実現できる仕組みとなっています。terraformコマンド自体に、バックエンドがTerraform Cloudだった場合にコードを転送してplanを実行するような仕組みが組み込まれているためです。 この仕組みにより、「コードをpushすることなく迅速に」「開発者間で統一された実行環境で」 terraform plan の実行が実現されています。

Actions移行後はさすがにこれと同様の仕組みを提供することはできませんでした。

AWSを管理するworkspaceの場合、ローカルで terraform plan の実行に必要な権限をもったIAM Roleを利用可能な開発者はローカルから実行することは可能です。 しかし、この権限をもったエンジニアは一部ですし、弊社の権限設計的にもあまり多くの開発者にこの権限を付与するのは望ましくありません。 また、AWSのように個々に権限を管理する仕組みが整備されている場合は実現の目処はありますが、GitHubやSendGridやDataDogなどの各種SaaSで同様に個々の開発者がローカルから terraform plan を実行するためのAPIキーなどを発行するのはリスクが伴います。

現状は高速にplan結果を確認したいケースはそこまで頻繁に発生しないことから、いったんはplanはコードをpushして必ずActionsで実行するような運用としています。

deployのapprove時にplanを確認する手間

「applyのレビュー待ちと通知」の項にて、メンション付きでマージしたユーザーに通知を送る仕組みを紹介しました。 通知自体は問題ないのですが、このDeployのApprove画面からplan結果を確認するためにActionsのログを開くのに数回のページ遷移とスクロールを行わなければならず、若干の手間となっています。

① Actionsの画面からplanジョブを選択
② planジョブの中のplanのstepのログを展開 & スクロール
③ planの差分内容に問題のないことを確認
④ workflowのトップに戻りReviewへ遷移
⑤ チェックを入れApprove & deployを選択

PR上にコメントでplan結果を表示したように、Actionsのworkflow画面にplan結果を表示することができれば手間をもっと減らせそうですが、現状はそういった機能は提供されていません。

今後の取り組み: RenovateによるAuto Merge

今まではDependabotを利用していましたが、Renovateを導入して以下を実現したいと検証中です。

  • 同一リポジトリ内のworkspace間でのPRの統一
  • plan差分の無いPRの自動マージ

上記を実現することで、さらなるリポジトリ管理の工数削減を目指します。

まとめ

弊社ではTerraform Cloudの新料金体系への移行タイミングを契機に、より安価でカスタマイズ性の高いGitHub ActionsへTerraformの実行環境を移しました。 Terraform Cloud相当の使い勝手を維持しつつ、既存の課題をいくつか解決するなどしてかなり使い勝手の良い実行環境を整備することができました。 今後Terraformを自動実行する環境を構築する方の参考になれば幸いです。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら medpeer.co.jp

■エンジニア紹介ページはこちら engineer.medpeer.co.jp

■メドピア公式YouTube  www.youtube.com

■メドピア公式note
style.medpeer.co.jp

Kaigi on Rails 2025に参加してきました! #kaigionrails

はじめに

こんにちは。エンジニアの福田です。Kaigi on Railsお疲れ様でした!

今回はスポンサーとしてブースに立ちながら、参加者として複数のセッションを聴講しました。

この記事では、ブースでの交流から見えた肌感と、セッションで得た学びを、明日からの開発に活かしやすい形で書いていきます。

ジョブボードに名前を書きました!

スポンサーブース出展しました!

Day1

一日目はAIについてのパネルアンケートを実施しました。

多くの来訪者の方々とツール選定や使い心地について話しました。開発支援ツールはClaude CodeとCursorの利用されている方が多い印象で、生成AIを使った作業がすでに日常的に活用されていると改めて感じました!

また、弊社でも「AIファーストカンパニー」として導入と運用を推進しており、現場の現実感と重なる点が少なくありませんでした。

意外にもQ3の「生成AIの待ち値時間に何をしているか?」という問いには、「祈る」と答える方が想像以上に多かったです。皆さんはどれを選びますか?

Day1のパネルアンケート

Day2

二日目は今年一月にリリースした「ClinPeer」の技術選定ブログClinPeer Railsプロジェクトの技術選定(2025年版) - メドピア開発者ブログを題材にクイズを実施し、来訪者の方々と交流できました!

ブースを担当していく中で私自身まだ知らなかったGemがあったので、とても刺激になりました。新しくリリースした「ClinPeer」を含め、弊社を知っていただく良い機会になったと思います!

ぜひ、一度クイズの答えを考えてみて上記のブログを参照していただけると幸いです!

Day2のパネルクイズ

聴講したセッション紹介

私が聴講したセッションから一部感想を書かせていただきます。

5年間のFintech × Rails実践に学ぶ - 基本に忠実な運用で築く高信頼性システム

kaigionrails.org

運用とは「ビジネスを可能にすること」——まさにその通りだと感じました。指揮・連絡・一次調査といったガイドラインの制定、規制対象の機能を本体から分離するという「Citadel」の考え方も大きなヒントになりそうです。

また、アラートやエラー通知の量を減らし、トリアージのガイドラインを整えるのはとても有効だと思いました。あわせて、アラート通知に対して過去の対応手順書から類似内容を提案する仕組みもぜひ試してみたいです。これならドメイン知識が少なくても一定の時短につながりそうです。まずは「Runbook(ランブック)」としてドキュメントを残すことを意識します。

ビジネスを可能にするためには、運用の積み重ねが重要だと改めて実感したセッションでした。

Railsアプリケーション開発者のためのブックガイド

kaigionrails.org

技術を学ぶときに、どんな書籍を買えばよいか、学生のときから今でも迷います。この発表は、技術情報の紹介も含めてとても参考になりました。

セッションからは外れますが、実際に本屋さんでお話しさせてもらい、2冊ほど買わせていただきました。説明を受けながら本を買える体験は、なかなかないのではないかと思います。貴重な体験でした。

どんどん積読していきます。

2分台で1500 examples完走!爆速CIを支える環境構築術

kaigionrails.org

こちらは30分以上かかっていたCIを2分台までに短縮するお話でした。私個人はなるべくパフォーマンスを上げるようにコードを書くのかなと思っていました。ですが、並列実行で分散しつつ、時間のばらつきを平準化して最長区間を短縮できるものでした。そこまで時短できるのかという驚きました。

そこから物理マシンを強化して一気に詰めるアプローチはとても大事かもですね。個人的に試してみたいですね。 まずは計測から始めていこうと思います。

rails g authenticationから学ぶ Rails 8.0 時代の認証

kaigionrails.org

弊社技術顧問の前島さんのセッションです。

最近プライベートで認証機能を実装する際に認証ジェネレータを利用しましたが、理解できたとは言えませんでした。 ですが、セッションにある通りコードを読んでいくことで認証機能のベースを理解する良い機会です。

聴講前は、Sessionモデルを定義する理由が不明でした。ここでは「ユーザーが乗っ取られた時に、ログインセッションを無効化する」が一番大きい理由だと知りました。

has_secure_passwordを宣言するとpssword_reset_tokenが使えるのはRails 8 からなのを初めて知りました。自動で有効期限がつくのは便利ですね。

認証機能はセキュリティに直結することなので、魔法だと思わずに情報のキャッチアップを続けていきます。

さいごに

私自身、初参加でしたが、想定以上の刺激を受けました。新しく学んだことをアウトプットしていきます。来年もぜひ参加したいです。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら medpeer.co.jp

■エンジニア紹介ページはこちら engineer.medpeer.co.jp

■メドピア公式YouTube  www.youtube.com

■メドピア公式note
style.medpeer.co.jp