メドピア開発者ブログ

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

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

MedPeer で負荷試験基盤を構築しました - 定量的な計測のための精度の担保と自動化のコツ

はじめに

こんにちは。サーバーサイドエンジニアの近藤です。

先日、私が担当している MedPeer (医師・医学生向け医療情報サイト) にて負荷試験環境を構築しました。

本記事ではその取り組みについてご紹介します。

背景

MedPeer ではユーザーである医師会員向けに、薬剤情報などの講演会を生放送で配信するための、Web講演会というサービスを提供しています。

このWeb講演会サービスでは、講演会の終了時刻付近に講演会のアンケートフォームが開くことで、総リクエスト数が急増する傾向があります。

大量アクセスによるシステムの負荷上昇が懸念され、同時間帯に開催できる講演会数に制限があることがビジネス上の課題でした。

そこで、許容できるWeb講演会の開催規模を定量的に計測し、システム改修によってその上限を引き上げることを目的に、このたび負荷試験環境を構築する運びとなりました。

また、将来的に機能やインフラの追加・改修を行う際に、性能への影響を事前に評価できる環境を整えたいという目的もありました。

開発の指針

負荷試験環境の開発にあたり、下記の3つの指針を定めて開発を進めていました。

  1. 誰でも安全に負荷試験をできる環境が用意されていること
    • 負荷試験の実施において複雑な手順やインフラの専門的な知識を要求されず、メンバーの誰もが負荷試験を実施できる環境を目指しました
  2. 計測したい負荷 (Web講演会の同時開催時の大量アクセス) を正しくかけることができ、その負荷を計測できるようになっていること
    • 試験結果の信頼性を担保するため、Web講演会のピーク時に発生する複雑なアクセスパターンを忠実に再現できるよう、実装における工夫を取り入れました
  3. まずは最低限動く状態を目指して作ること
    • 最初から完璧な環境を目指すのではなく、まずは主要な課題を解決できる最小限の構成を目指しました

開発において判断に迷った際はこの指針を基に意思決定を行いました。

システムの概要

全体像

今回の負荷試験は、既存の Staging 環境を「試験中だけ本番環境相当にする」というコンセプトで行います。

具体的には、試験の実行中のみインフラの性能を本番環境と同等まで一時的に引き上げ、その環境に対して大量のリクエストを送信することで負荷試験を行います。

構成図

各要素の役割

  • 負荷生成 (k6 サーバー)
    • 負荷生成ツールには k6 を採用しました。専用サーバー上で k6 を実行し、シナリオファイル(リクエスト内容を定義したファイル)に基づいて試験対象へリクエストを送信します
  • 自動化 (GitHub Actions)
    • 試験の準備から実行、後片付けまでの一連のフローは GitHub Actions Workflow で自動化し、誰でも手軽に試験を実施できる体制を整えました
  • モニタリング (Datadog)
    • 試験中の主要なメトリクスは Datadog のダッシュボードでリアルタイムに可視化し、試験結果をすぐに確認できるようにしています
  • ダミーデータの準備
    • より本番に近い状態で試験を行うため、インフラ性能だけでなくデータベースの状態も重要です。システム構成図には記載がありませんが、事前に Staging 環境のデータベースへ大量のダミーデータを投入し、本番環境のデータ規模を再現しました

各システムの構成

ここからは、負荷試験環境を構成する各要素について、その詳細や採用理由などを解説します。

負荷試験環境のインフラ構成

スケールアップ対象の要素

負荷試験の実施中は、主に以下の要素を本番環境と同等の設定値へ変更します。

  • アプリケーションサーバー (Amazon ECS)
    • タスク数
    • CPU・メモリの割り当て量
  • Web サーバー (Unicorn)
    • ワーカー数
  • データベース (Amazon RDS)
    • インスタンスサイズ

実装方式

この動的な環境変更は、Terraform の Input変数機能を使って実現しました。

インフラ構成に load_test_mode という真偽値 (boolean) の変数を定義しました。この変数を true にすると、Terraformの三項演算子によって各リソースが本番環境相当の設定値で再構築される仕組みです。

resource "aws_db_instance" "rds" {
  instance_class = var.load_test_mode ? (本番環境の設定値) : (Staging環境の設定値)
  ...

設計上の判断と見送ったこと

開発速度とコストのバランスを考慮し、以下の2つの判断を行いました。

  1. 実装コストを考慮した初期スコープの絞り込み
    • 完全な環境再現には多大なコストがかかるため、まずはアプリケーションとデータベースといった主要な要素にスコープを絞りました。Redis などのキャッシュサーバーについては、試験結果からボトルネックとして顕在化した場合に、次のステップで対応する方針としています。
  2. 既存の Staging 環境の活用
    • 負荷試験専用の環境をゼロから構築する案もありましたが、実装工数の増大が懸念されたため見送りました。既存の Staging 環境を活用することで、迅速に試験環境を準備できました。

負荷発生サーバーのインフラ構成

負荷を発生させる k6 の実行環境は、試験対象のアプリケーションサーバーとは完全に分離された、専用の Amazon ECS タスクとして構築しました。

実行の仕組みと性能設計

負荷試験の実行フローは以下の通りです。

  1. CI/CD パイプラインで、k6 本体とシナリオファイルを含む Docker イメージを事前にビルドします。
  2. 試験実行時には、このイメージからコンテナを起動し、内部で k6 コマンドを実行する仕組みです。

サーバーのスペック(CPU, メモリ)は、k6 の公式ドキュメントを参考に決定しました。ドキュメントでは 1VU (仮想ユーザー) あたり約 1〜5MB のメモリを消費するとされており、これを基準に、想定する負荷量に対して十分なリソースを割り当てています。

Datadogとの連携

MedPeer ではモニタリングツールとして Datadog を採用しています。

ですが k6 から Datadog へのメトリクス送信は、標準ではサポートされていません。

k6 の公式ドキュメントを参考にし、StatsD プロトコルでメトリクスを送信できる拡張機能 (xk6-output-statsd) を組み込んだ k6 をビルドして使うようにしました。

JMeter・Grafana Cloud k6 との比較

k6 の他の主流な負荷試験用ツールとして、JMeter を利用する選択肢もありました。ですが JMeter はGUIのソフトウェアであることや、シナリオに XML ファイルを利用するためロジックの記述・再利用が難しそうなこと、k6 よりもメモリ使用量等のパフォーマンスが悪いことなどの理由から、k6 を選択しました。

また、k6 の実行環境として SaaS である Grafana Cloud k6 の利用も検討しましたが、実施コストを比較した結果、今回は自社環境にセルフホストする方式を選択しました。

実施コスト

負荷試験1回あたりの金銭コストとしては、模倣する本番環境やかける負荷の量次第ではありますが、今回構築した負荷試験環境・シナリオの場合、4時間の負荷試験環境の立ち上げで75$ほどのコストとなりました。Staging 環境の ECS サーバーと k6 を動かすサーバーの費用が大部分を占めているため、予めある程度はコストの予測ができると思います。

自動化された負荷試験ワークフロー

負荷試験の一貫性と再現性を担保するため、準備から片付けまでの一連のプロセスを GitHub Actions で自動化しました。これにより、誰でも安全かつ手軽に試験を実施できます。

ワークフローは、大きく分けて以下の4つのフェーズで構成されます。

1. 準備

まず、試験実行に伴う不要なアラートを防ぐため、Staging 環境の監視ツールを一時的に停止します。その後、Terraform を通じて環境を本番相当のスペックへスケールアップします。

name: Setup Load Test Environment

on:
  workflow_dispatch:

jobs:
  ...
  setup-load-test:
    steps:
      ...

      - name: terraform plan
        ...
        env:
          # STG_LOAD_TEST_MODE には true が入る
          TF_VAR_load_test_mode: ${{ vars.STG_LOAD_TEST_MODE }}

      - name: terraform apply
        ...

2. 実行

次に、負荷試験本体を実行します。このフェーズでは、純粋な負荷生成だけでなく、試験データの整合性を保つための処理も自動化しています。

  • データ準備・リセット: 試験シナリオによっては、特定のデータベースレコードの作成、更新、削除が必要です。これらの処理は Rake タスクとして定義し、k6 の実行前後で自動的に実行することで、常にクリーンな状態で試験を開始・終了できるようにしています
  • 負荷試験の実行: 事前にビルドされた Docker コンテナを起動し、内部で k6 コマンドを実行して負荷を発生させます
name: Run Load Test

on:
  workflow_dispatch:
    inputs:
      ...

jobs:
  before-load-test-task:
    steps:
      - name: Execute Before Test Rake Task
        run: |
          aws ecs run-task
            ...
                  "command": [
                "bundle",
                "exec",
                "rake",
                "load_test:'${{ github.event.inputs.test_scenario }}':before_test"
              ]
            ...

  run-load-test:
    steps:
      - name: Execute k6 Scenario on ECS Task
            run: |  
              ...
              aws ecs run-task
                ...
                "command": [
                  "k6",
                  "run",
                  ...
                  "load_test/${{ github.event.inputs.test_scenario }}.js"
                ]
                ...
        
  after-load-test-task:
    steps:
      - name: Execute After Test Rake Task
        run: |
          ...

3. 片付け

試験終了後、停止していた監視ツールを再開し、スケールアップした Staging 環境の性能を元の状態へ戻します。

name: Cleanup Load Test Environment

on:
  workflow_dispatch:

jobs:
  ...
  cleanup-load-test:
    steps:
      ...

      - name: terraform plan
        ...
        env:
          # STG_LOAD_TEST_MODE には false が入る
          TF_VAR_load_test_mode: ${{ vars.STG_LOAD_TEST_MODE }}

      - name: terraform apply
        ...

4. 確認

最後に、Datadog のダッシュボードで試験結果を評価します。

分析を効率化するため、ダッシュボードには2つの工夫を加えました。

  1. Staging/本番兼用ダッシュボード: ダッシュボードの変数機能を活用し、本番環境のモニタリングで普段から使っているダッシュボードをそのまま負荷試験にも流用しています。これにより、環境を切り替えるだけで同じ指標を比較できるようになります
  2. k6 専用ダッシュボード: 上記とは別に、負荷サーバー自体の CPU 使用率や k6 が収集したメトリクス(リクエスト数、レスポンスタイム等)を可視化する専用ダッシュボードも用意し、k6 サーバー側が過負荷に陥っていないかを手軽に確認できるようにしました

Datadog のダッシュボード

シナリオファイルの作成

負荷試験のシナリオファイルは、「どのようなリクエストを、どのくらいの量・頻度で送るか」を定義します。このセクションでは、実際のトラフィックを再現するためのシナリオ作成プロセスと、実装上の工夫について解説します。

リクエストパターンの調査

最初に、負荷試験の対象とする状況(例:講演会終了間際のピーク時)を定め、その時間帯のアクセスログを分析します。ここでの目的は、リクエストの総量だけでなく、時間経過に伴うリクエスト数の増減パターン(トラフィックの波形)を把握することです。このパターンを基にシナリオを作成します。

シナリオファイルの実装

次に、分析結果を基に k6 のスクリプトを作成します。http.getsleep を組み合わせてシナリオを記述します。

// 例
export default function () {
  http.get(`${host}/top`)

  sleep(1)

  http.get(`${host}/about`)
}

実装時には、負荷試験特有の注意点があります。

  • POST リクエストにおける CSRF トークンの処理を忘れないこと
  • 意図しない外部ドメインへのリダイレクトを防止すること

このような問題を特定しやすくするため、最初は 1 VU など、ごく少数のユーザーから試験を開始するのが有効です。

dry-run モードの導入

「ユーザーごとにリクエストタイミングをずらす」などの複雑な要件が加わると、コードから意図したリクエストパターンが生成されるかを確認するのは困難です。

この課題に対し、シナリオスクリプトに dry-run モードを導入しました。これは、HTTPリクエストや sleep を実際には実行せず、予定されていたリクエストのログを高速に出力する機能です。

  • sleep 処理の代替: dry-run モード時は、実際の待機処理をスキップし、内部で管理する論理的な時刻のみを進めます
  • HTTP リクエスト処理の代替: HTTP リクエストも実際には送信せず、予定時刻や URL といった情報を記録するのみとします
  • 結果の比較: dry-run の実行後、記録されたログを集計・グラフ化します。このグラフを、最初に調査した実際のトラフィック波形と比較することで、シナリオの妥当性を事前に検証します。

dry-run結果の出力

宣言的なシナリオ定義

複雑なリクエストパターンをループと sleep で記述する命令的な方法は、変更が全体に影響しやすく、メンテナンス性に課題がありました。

この問題に対し、より直感的にシナリオを定義できる宣言的なアプローチを導入しました。これは、再現したいトラフィックの波形を、アスキーアートで描くように定義することで、そこから各リクエストのタイミングが自動算出される仕組みです。

// アスキーアートでリクエストの分布を定義
const topPageAccessDistribution = distribution(`
  ###
  #####
  ##########
  #######
  ####
  ##
`)

実装の詳細は控えますが、このアプローチにより、複雑なシナリオの可読性とメンテナンス性が向上したと思います。

負荷試験環境の活用

負荷試験環境を利用して早速、本番環境では過去に事例が無い規模のアクセスピークを模した負荷試験を実施しました。

これにより、現行システムの性能上限と、システム増強によってどの程度性能を向上できるかの見込みを、定量的に把握できました。

また、今後は新規機能の追加やインフラ構成を変更する際に、性能への影響を事前に計測する環境としても負荷試験環境を活用できます。

この環境の構築により、システムの負荷を定量的に把握できるようになっただけでなく、試験実施にかかる検証コストも大幅に削減できました。

おわりに

これからも負荷試験の知見を活かしながら、安定したサービス提供と事業の成長の基盤となる仕組みづくりを続けていきます。

本負荷試験環境構築プロジェクトは、筆者の他にサーバーサイドエンジニア2名とSRE2名、技術顧問をしていただいた外部のアドバイザーの方1名、その他多くの方々のご協力により完了させることができました。ご協力いただいた皆様に改めて感謝いたします。

最後までお読みいただきありがとうございました。


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


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

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

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

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

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

OpenID Connect Authorization Server を Golang 実装から Rails 実装にリプレイスした話

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

社内の OpenID Connect Authorization Server を Golang 実装から Rails 実装にリプレイスしたので、技術ブログで紹介させていただきます。

前提

OpenID Connect は OAuth 2.0 認可プロセスを拡張し、認証目的で利用できるようにしたものになります。

Authorization Server は OpenID Provider (以下OP) とも呼ばれ、OpenID Connect を利用する OAuth 2.0 Client は Relying Party (以下 RP) とも呼ばれます。

OpenID Connect フロー概要は OpenID Connect Core 1.0 にある通り下記になります。

+--------+                                   +--------+
|        |                                   |        |
|        |---------(1) AuthN Request-------->|        |
|        |                                   |        |
|        |  +--------+                       |        |
|        |  |        |                       |        |
|        |  |  End-  |<--(2) AuthN & AuthZ-->|        |
|        |  |  User  |                       |        |
|   RP   |  |        |                       |   OP   |
|        |  +--------+                       |        |
|        |                                   |        |
|        |<--------(3) AuthN Response--------|        |
|        |                                   |        |
|        |---------(4) UserInfo Request----->|        |
|        |                                   |        |
|        |<--------(5) UserInfo Response-----|        |
|        |                                   |        |
+--------+                                   +--------+

背景

弊社では 過去 OP を PHP (認証エンドポイント) と Golang (トークンエンドポイント) のハイブリッド実装で提供を行なっていました。 ただ、ハイブリッド実装に伴う複雑性を解消するために 2022年に認証エンドポイントを PHP から Golang 実装にリプレイスを行い Golang 実装への統一を行いました。

これに伴い、複数言語間をまたぐ複雑性は解消されたのですが、弊社のバックエンドエンジニア採用は Rails エンジニアが主であり社内で Golang をメンテナンスできるメンバーが限られていたため、実質的に内藤一人が保守する体制になってしまいました。

サービス継続性の観点から、持続的なメンテナンスを可能にするため Rails への再リプレイスを実施したので、今回その話をさせて頂きます。

リプレイスにあたって検討した点

稼働中のサービスになるため、互換性を維持しつづ、今後のメンテナンス体制を確保するのが最大の目的です。

  • メンテナンス体制
    • 社内の Rails エンジニアリソースを活用して保守を行なっていくため、 Rails を用いた実装を行う。
    • メンテナンスを容易にするため、なるべく自前実装は避け、既存の gem ライブラリを活用する。(ただしドキュメントが少ないと引き継ぎが厳しいので、ドキュメントがあると良い。)
    • OP だけでは、動作確認が困難なため、検証用の RP も用意する。
    • 既存のモノリスに OP を統合する事で、別リポジトリでの管理を避け一体的なメンテナンスが行えるようにする。(ただし、可能ならモジュラーモノリス的な疎結合にしたい。)
  • 互換性
    • OpenID Connect として現在使用している機能が実現可能。(OpenID Connect で OPTIONAL 扱いの機能はライブラリ側で未サポートの場合があるため。)
    • スムーズに移行するため、RP とのインターフェース(リクエストパラメータ)は変更しない。 (社外の RP が存在するため弊社都合での変更はハードルが高い。)
    • RP によっては同意済ステータスを引き継ぐ必要があり、旧データベースの情報を参照する必要がある。

なお、本サービスは主に医師会員向けのサービスであり、日本国内の医師数は40万人程度なので、新たな性能面の要求はありませんでした。(=現状維持できれば良い。)

移行にあたっての gem の選定基準

主に下記が検討候補として考えれました。

  1. OAuth/OIDC Component as a Service のような BaaS を使う。
  2. openid_connect gem を用いて実装する。
  3. Rails エンジンの仕組みの doorkeeper gem (OAuth 2 provider) + doorkeeper-openid_connect gem (OpenID Connect 拡張) を用いて実装する。

このうち、1 は予算の観点から特に検討しませんでした。

2 の openid_connect gem は、社内では OP としての 利用例は無いが RP としては長年利用しており、最初に候補に上がったのですが、OP のドキュメントが will write someday.. と無かったため、導入に躊躇いがありました。

3 の doorkeeper 本体は、非常によく使われているため特に問題なさそうでしたが、 doorkeeper-openid_connect は、Looking for maintainers! とメンテナーを募集中であり、継続性に不安がありました。

ただ、doorkeeper 本体のメンテナーでもある Nikita Bulai 氏が doorkeeper-openid_connect gem のサポート(バグ修正&改善のみ、新機能の追加は対象外)を行う事になり、継続的にリリースがされているので、この点では問題は無さそうと判断しました。

また、ドキュメントの観点では、下記が非常に充実しており、リプレイスにあたり必要な機能(promptパラメータ等)がサポートされているか、カスタマイズの観点で何をすれば良いか記載が豊富なのも重要な点でした。

doorkeeper.gitbook.io

github.com

また、Rails エンジンで実装されており、既存のRails モノリス統合しても密結合を避けられるためモジュラーモノリスの観点でも良さそうでした。

OpenID Connect 検証環境の構築

上記を踏まえ、一旦 3 の doorkeeper gem + doorkeeper-openid_connect gem で検証環境を構築し、評価を進めていくことになりました。

下記の実装例を参考に、手順通り実装すれば OP を構築できた & 実際に動作させながら必要な機能が期待通り動作するか、カスタマイズが意図通り行えるかを検証できたのが大きかったです。

thinkami.hatenablog.com

※ 上記は RP の実装に omniauth-oauth2 gem を使われていますが、omniauth_openid_connect gem を使う事で、さらに実装量を減らしレビューコストを削減することができました。

評価の結果、 doorkeeper gem + doorkeeper-openid_connect gem は新規に OP を提供する場合は導入しやすい事がわかったのですが、カスタマイズの観点(特に既存DBにデータがある場合)からは課題があることが見えてきました。

データの移行方針の決定

この課題とは、doorkeeper-openid_connect のテーブル構造が既存DBのテーブル構造と異なるため、既存のリクエストフローとデータ保存のタイミングが異なるため、doorkeeper-openid_connect をカスタマイズしても既存DBをそのまま使い続ける事が困難な事がわかりました。

コードベース切り替え時に、既存DBをそのまま使い続ける事ができればシームレスなサービスリリースが可能になります。

具体的にはメンテナンス画面表示を用いたサービス停止が不要になり、仮に本番リリース時に問題があった場合でもコードを切り戻せばリリース前の状態にすぐに復帰する事が可能です。

ただ、これを実現するために既存DBのテーブルに無理やり doorkeeper-openid_connect のデータを書き込むと処理が複雑になり技術負債となってしまうため、残念ながらこの方針は断念し、既存DBテーブルのレコードのデータカラムを再構成し、新しく doorkeeper-openid_connect 用に用意したテーブルにデータマイグレーションする方針にしました。

これにより、リリース時は一旦サービスを停止し、メンテナンス画面を表示する対応となったのですが、メンテナンス表示対象を OpenID Connect を使用するサービスに限定する事ができたので、ビジネス影響を最小化する事で対処しました。

採用結果

最終的にOPの実装として、下記の点から 3 の doorkeeper gem + doorkeeper-openid_connect gemを採用することに決定しました。

  • 互換性
    • リプレイスにあたりOpenID Connect として必要な機能が実装されている。
    • RP とのインターフェースを(ルーティングパスのカスタマイズ定義は必要だが)維持できる。
    • 同意済ステータスを引き継ぐために、データマイグレーションは必要だが、マイグレーションをしてしまえば、複雑なカスタマイズは回避できる。
  • メンテナンス体制
    • 導入するにあたり、OpenID Connect 周りの処理はほぼ設定ファイルの記載で対応可能 (認可画面はデフォルトの画面が用意されているが、既存の Golang 実装の画面を erb で実装しました。)なため、メンテナンス対象のコードベースを最小化することで、OpenID Connect に詳しく無い開発者でもメンテナンスを容易にする。
    • 動作検証用に管理画面に omniauth_openid_connect gem を用いた簡易RPを用意することで、 単一リポジトリでのOPの動作確認を可能にし、開発環境構築周りのトラブルを回避する。

リリース作業

無事、開発が完了したので、下記の流れでリリース作業を実施しました。

  1. ユーザーに対しメンテナンス時間を事前告知し、不要なアクセスを回避します。
  2. CloudFront 側で、簡易的なメンテナンス画面を表示し、不要なアクセスが発生しないようにしました。
  3. maintenance_tasks gem を用いてデータマイグレーションを行いました。本番環境での手作業によるリスクを回避し、進捗状況もわかる非常に重宝している gem です。詳しくは下記をご覧ください。 tech.medpeer.co.jp
  4. Rails 単体であれば flipper 等を用いたフィーチャーフラグでの切り替えを行うのですが、今回は別言語間でリプレイスなので、この手法は使えず、CloudFront 側でルーティング対象を切り替え可能にする事で対応しました。
  5. 本番でテスト用アカウントを用いて動作検証を行い、問題無い事を確認できたので、メンテナンス表示を削除し、無事リリースできました。 🎉

結果

OpenID Connect Authorization Server を Rails に統合する事で、複数リポジトリ、複数言語間にまたがったシステムの複雑性を解消し、一つの Rails モノリスに無事統合する事ができました。

Rails エンジンを用いる事で疎結合になっているので、今後の複雑性を回避できそうなのもポイントです。

その結果、OpenID Connect 関連のその後の機能改修で内藤が担当する事なく(レビューアーとしては参加)、他のメンバーが改修を行う事ができており、チームでのメンテナンス体制の確立という目的を無事達成することができました。

振り返り

前回の移行作業では、互換性を維持しつつ技術負債の解消を第一に取り組んだのですが、入社後あまり時間が経っていない時期に移行計画を立てたため、社内のエンジニア採用計画を踏まえたメンテナンス体制の重要性まで考慮できていなかったのが一番の敗因でした。

自分だけがメンテナンスできても、それは自分自身の首を絞めるだけなので、チームとしてメンテナンスできる体制を確保できるかが重要ですね。


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


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

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

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

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

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

iOSDC Japan 2025に参加してきました!

こんにちは。メドピアでモバイルアプリエンジニアをしている王です。 先日「iOSDC Japan 2025」に参加してきました。参加は今年で4回目。毎回、新しい学びと人との出会いにワクワクします。

9/19には DroidKaigiに参加したレポートも投稿しましたが、今回はiOSDC Japanで印象に残ったセッションや現地で感じたことをレポートします。

会場と雰囲気

今年は会場が早稲田大学から有明セントラルタワーホールへ。アクセスしやすくなったこともあり、初日・2日目は現地参加しました。さらに今年は10周年という節目。受付からホールまで活気にあふれ、コミュニティの熱量を肌で感じました。

印象に残ったセッションと感想

今年もテーマは幅広く、初心者から上級者まで楽しめる内容がそろっていました。なかでも強く惹きつけられたのがルーキーズLT大会。発表者の多くが学習を始めたばかりの初学者で、しかも登壇は今回がはじめてという方が中心だったこともあり、既成概念にとらわれない新鮮な視点や挑戦が次々に飛び出しました。コミュニティの未来を感じる、活気ある時間でした。

印象に残ったセッションを具体的な事例や学びとあわせてご紹介します。プロダクトづくりのヒントや、日々の開発にすぐ取り入れられる知見をピックアップしていきます。

AIを活用したレシート読み取り機能の開発から得られた実践知 by 岩名勇輝

資料: speakerdeck.com

プロポーザル: fortee.jp

レシートの写真から内容を読み取る機能を、OCRのチューニング描画・処理の高速化で磨き込み、さらにローカルLLMで項目名や金額などの「必要情報」を抽出する実装事例が紹介されました。実務で参考にできるテクニックが多く、ユーザー体験の向上と開発効率の両立に示唆がありました。

一方で、レシートは店ごとに形式がバラバラ。この“ゆらぎ”にどう向き合うかが品質のカギ。最終的な使い心地を左右するのは、認識精度安定したパフォーマンスであることを改めて実感しました。

大規模アプリにおけるXcode Previews実用化までの道のり by ikesyo

資料: speakerdeck.com

プロポーザル: fortee.jp

LINE アプリのような超大規模プロダクト(コード 250万行超/Xcode プロジェクト 600以上)で、Static Frameworkを活用しつつXcode Previewsを「実務で使える」水準まで導入していったプロセスが語られたセッション。 規模が大きいほど、起動速度やモジュール依存関係、チーム横断の連携がボトルネックになりがちですが、そこをひとつずつ解きほぐし、プレビューの恩恵(UI開発のフィードバック高速化)を享受できるようにした点が圧巻でした。

メドピアでも、複数モジュールで動くプロダクトを運用しており、開発者体験(DX)を高める環境整備は重要テーマのひとつ。今回の知見を活かし、プレビュー駆動の UI 改善ビルド時間短縮に引き続き取り組んでいきます。

高セキュリティ要件を満たすための iOS アプリ開発 ― MASVS v2.0.0 対応から学ぶ実践知見

資料: speakerdeck.com プロポーザル: fortee.jp

MASVS(Mobile Application Security Verification Standard)v2.0.0の要点と、実プロダクトでの対策の落とし込みが具体例とともに紹介されたセッションでした。 モバイル特有のリスクに対して何をどこまで行うかを考えることの重要性を再認識。機能の速さだけでなく、安全性そのものがユーザー体験を形づくると感じました。



弊社の専門医向け臨床研鑽アプリ ClinPeerでも、MASVS の考え方を参考に、セッション管理/ローカルデータ保護/通信の安全性/改ざん耐性といった観点で整備を段階的に進めています。 セキュリティは一度きりではなく継続的に磨き上げるプロセス。ユーザーの信頼に直結する領域として、今後も優先度高く取り組んでいきます。

App Clip 5年史: 萌動と停滞のクロニクル by log5

資料: speakerdeck.com

プロポーザル: fortee.jp

App Clip のこの5年を、誕生・萌動・停滞・漸進・創意というキーワードで振り返る内容。App Clip は「アプリをインストールしていないユーザーに、必要な一部機能を素早く届ける」ための仕組みで、気に入ればそのまま本体アプリへ誘導できるのが本質的な価値だと整理されていました。

運用面の要点として、使わなければ自動的に削除される(非使用30日で削除)ため、ユーザー側のストレージ負担が残りにくい点も再確認。
またサイズ上限の緩和もアップデートの一つ。iOS 17以降は条件付きで最大100MB(デジタル誘導・iOS17限定等)まで拡大され、よりリッチな体験設計がしやすくなっています(それ以前は 10〜15MB)。


当社のClinPeerではまだ App Clip を導入していませんが、学会会場などでインストール不要で試せる導線を用意できれば、初回体験のハードルを下げ、より多くの方にプロダクトの価値を触れてもらえる可能性を感じました。

まとめ

10周年の熱気に包まれた数日間は、発見と刺激にあふれ、とても幸せな体験でした。たくさんの「面白い」に出会えて胸が躍りましたし、来年もこの空気を味わいに、また戻ってきたいです。

最後に──メドピアではiOSエンジニアを募集中です。ユーザー価値に真摯に向き合い、技術でプロダクトを磨くことにワクワクする方、ぜひカジュアルにお声がけください。


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


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

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

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

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

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

iOS開発者が見たDroidKaigi 2025——AIが当たり前になった日

こんにちは。メドピアでモバイルアプリエンジニアをしている王です。

今年は同僚2人と一緒に「DroidKaigi 2025」に参加してきました。会場では、AIの存在感が一段と増し、開発の進め方やスマホの中で動く賢い仕組み(オンデバイスAI)、複数OSでのアプリ開発、テストのやり方まで、さまざまな領域が前に進んでいるのを実感しました。

ふだんはiOSを中心に開発している私ですが、Androidのこれからには大きな伸びしろがある——そんな手応えを強く感じるイベントでした。

具体的なセッションの紹介は、同僚の佐藤がこちらの記事に詳しくまとめています

https://tech.medpeer.co.jp/entry/2025/09/16/123000)。

よろしければ合わせてご覧ください。 本稿では、iOS開発者の視点から「AI」と「マルチプラットフォーム開発(KMP/CMP)」に焦点を当て、DroidKaigi 2025で得た学びを整理します。

まずはAIについて。 従来のChatGPT、GitHub Copilot、Geminiに加え、今年はClaude Codeを選ぶ場面が目に見えて増えました。さらに、Android向けの開発支援ツールであるKoogComposeFlowが登場し、モバイル特化の"使えるAIツール"が実務に入ってきた印象です。

また、オンデバイスAIの代表例であるGemini Nanoのように、端末内で完結して動く仕組みも存在感を増しています。通信に頼りにくい場面でも素早く応答でき、プライバシー面でも有利です。

一方で、Apple側ではXcode 26が2025年9月16日にリリースされ、Coding Assistantが搭載されました。現時点ではChatGPTやClaude codeとの連携が中心で、Apple独自のAI体験はこれからに期待——という受け止めです。良い道具は早く取り入れて回す、という姿勢が以前にも増して大切になってきたと感じます。

次に、複数OSでの開発について。 ここ数年で「どちらか一方に寄せる」か「完全に別々に作る」かという二者択一ではなく、現実的な折り合いの付け方が広がってきました。たとえばKotlin Multiplatform(KMP)は、iOSとAndroidのあいだで"アプリの土台となるロジックやデータ処理"を共通化するための技術です。

さらにCompose Multiplatform(CMP)を使えば、一部の画面を共通化することも可能になってきました。ただし見た目や操作感には各OSの文化があります。そこは無理に揃えず、それぞれに合わせて仕上げる——この"いいとこ取り"がいまの主流だと感じます。

弊社プロダクトClinPeerは、いまは iOS が Swift、Android は Kotlin のネイティブで開発しています。KMP/CMP を“使う”と決めたわけではありませんが、まずは API やキャッシュ、データモデルなどの「裏側の共通化」で小さく PoC を試し、数値で効果を見極めたいと考えています。手応えがあれば、一覧・詳細・ブックマークといったシンプルな画面から CMP の適用を段階的に検討し、必要に応じていつでも戻せるようにしておきます。いきなり全部は変えず、良い結果だけを丁寧に積み上げていきます。

最後に少し個人的な話を。 モバイル開発の面白さは、深い技術に向き合うことと、使い心地を磨くことの両方に関われる点にあります。アプリを出すとすぐに反応が返ってきて、ユーザーの声を聞きながら素早く改善できる。この"手触りのあるものづくり"は、AIやマルチプラットフォームの進化によって、これからさらに面白くなるはずです。

イベントで得た学びは、私たちの開発にもすぐ生かしていきます。オンデバイスとクラウドのAIを場面に応じて使い分け、iOSとAndroidの"賢い共同制作"を小さく始める。日々の使い勝手を静かに、でも確実に良くしていきます。

もし今回の内容にご興味があれば、ぜひお気軽に声をかけてください。情報交換や共同での検証など、ご一緒できたらうれしいです。


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


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

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

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

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

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