メドピア開発者ブログ

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

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

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

はじめに

こんにちは!メドピアにてモバイルアプリエンジニアをしている佐藤です。
今年のDroidKaigiには総勢3名のモバイルアプリエンジニアがオフラインにて参加しました。
実際に足を運んだセッションの中でも特に印象に残ったセッションをご紹介します。

UIだけじゃないComposeの可能性 ━ 宣言的に奏でるメロディ

usuiatさんによるセッションです。

youtu.be

composeがUIを表示するまでの仕組みを体系的に学ぶことができ、runtimeの可能性を感じることが出来ました!
特に何気なく普段から活用しているComposable関数ですが、Composable関数を呼び出した際のReusableComposeNodeがどのように動作するのか知れてとても面白く、オーディオブロックダイアグラムをComposable関数で表現する件にはこんなアプローチがあるのかと「ほえ〜」と感嘆しました。
音声処理ライブラリの解説の中でところどころ流れる音声も良い感じでしたので、YouTubeの動画での視聴がおすすめです!
開発する中での手札が明確に増えたと思える非常に勉強になったセッションでした。

はじめてのMaterial3 Expressive

chukaさんによるセッションです。

youtu.be

Material3 ExpressiveのComponentを実際のコードを交え紹介していただき、今自分が関わってるアプリならこのComponentを採用したら良さそうだな🤔と頭の中で考えながら聞いていました。
普段使いするようなLoadingindicatorやButtonsはすぐにでも差し替えを検討したいと思えましたし、SearchAppBarやFABmenuなどは知っておくだけでもUIの表現手段の一つとして検討余地の高いものなので知ることが出来て良かったです。
新機能開発におけるデザイン検討時などに定期的に見返したくなる素晴らしいセッションでした。

デザイナーがAndroidエンジニアに挑戦してみた

Kanon Fujitaさんによるセッションです。

youtu.be

Figmaの設定値とComposable関数における設定の相性の良さや、OOUIとデータクラスの組み合わせのわかりやすさなどはデザイナー視点だとそう思えるのかと聞いてて新鮮でとても面白かったです。
弊社でもデザイナーの方とのコミュニケーションは頻繁に発生するので、「一緒により良く作る」のコミュニケーションは改めて意識していきたいと思えました。
何はともあれAndroid最高!!!!!!!!!

意外と知らない Android と Google Play の世界

Rikako Katayamaさんによるセッションです。

日本市場ならではのGoogle Playの仕様や機能、また日本で先行して改訂されたポリシーがグローバルに展開された事例など、Googleが日本市場において取り組んできた内容を知ることができました。
さらに、Play Consoleの権限設定まわりも日本からのフィードバックをきっかけに改修された部分があるとのことです。
Androidエンジニアにとって切っても切り離せないGoogle Playの舞台裏について話を聞ける、有意義なセッションでした。

Flutterからネイティブへの挑戦と学び - 評価1.6から4.0への道のり

Ryo Kitamuraさん、kentaro fujiiさんによるセッションです。

youtu.be

ネイティブアプリからFlutterなどのクロスプラットフォーム化というのは弊社でもたまに出る話ではあるのですが、Flutterからネイティブアプリにリプレイスというのはあまり聞かない話なので、ネイティブアプリへのリプレイスまでの背景を知れて貴重な知見を得られました。
また、チームビルディングの部分では特に心理的安全性を高める取り組みの部分が面白かったです。
心理的安全性の部分はチームへの新規参入される方などが特に感じやすい部分だと思うので、そうした方を対象にしたオフィスアワーなどは実践したら良さそうだなと考えていました。

レビュー周りの取り組みもエンジニアとして定期的に挙がる話題なので聞けて良かったです。
コードレビューは知恵比べや技術力を誇示したりクイズ大会をする場ではない、まさにその通りですね!コードレビューは共同作業であるという意識を改めて高めることが出来ました。

「どこから読む?」コードとカルチャーに最速で馴染むための実践ガイド〜新メンバーを活躍に導くオンボーディング戦略〜

richako (risako070310)さんによるセッションです。

youtu.be

弊社では複数サービスを展開しているということもあり携わるサービスが変わるというのはちょくちょくある話でもあるので、なるほどなーとなる頷きの多いセッションでした。
特に良いと思ったのはドキュメント更新を文化と仕組みにするという部分です。
一番ドキュメントを必要としていて真剣に読むユーザーがメンテナーとなり、そしてオンボーディングの最終タスクとしてオンボーディングドキュメントを組み込むことで継続的にドキュメントが成長していく仕組みとする。
新メンバーがメンテナーになる文化、素晴らしいと思いました!

スマホ新法って何?12月施行?アプリビジネスに影響あるの?

健太 鈴木さんによるセッションです。

youtu.be

公正取引委員会の方のセッションをエンジニアイベントで聞く機会があるとは思っていませんでした笑
2025年12月18日に施工されるスマホ新法に関わる話です。
事業者目線で言うとスマホ新法の規制対象になるにはまず月間平均利用者数が4000万人以上になる必要があるとのことでしたので、
「公正取引委員会恐ろしい!!!」
「スマホ新法鬱陶しい!!!」
と思う為にはまずはそこまで利用者数を増やす必要がありそうです笑
開発者目線だと新しいアプリストアの登場やOS機能の利用可能性の向上が見込まれる変化としてあるとのことでしたので、そうした変化におけるキャッチアップや対応が楽しみだなと思いました。

法律に関するお堅い内容と見せかけてスピーカーである健太 鈴木さんのユーモアがすごく、公正取引委員会のマスコットキャラクターのパーカーが税金ではなく自腹という件では会場が笑いに包まれていました笑
私が今年参加したセッションの中では一番参加者が多くDroidKaigi2025のYouTubeで最も再生されている(2025年9月16日の記事公開時点)人気セッションでしたので、楽しみながらスマホ新法に関する理解を深められる素晴らしいセッションです。

OAuthを正しく実装する:Androidアプリのためのセキュアな認証

Chrystian Vieyra Cortesさんによるセッションです。

youtu.be

Oauth2.0認証を用いてモバイルアプリにログイン機能を実装する際に外部ブラウザにするか?WebViewにするか?といったことや、トークンの保存における暗号化について触れられていました。
ログイン機能を実装する際、外部ブラウザにするかWebViewにするかについてはよく議論に上がる部分かと思うので検討の際の参考になると思います。

また、トークンを共有設定保存した場合、ルート化しない限りは読み取り抽出する方法はないので暗号化せずに共有設定に保存するの下りの部分は特に面白かったです。
最近ログイン回りを実装した際にはとりあえず思考停止で暗号化してdatastoreに保存するようにしていたので、そういった考え方もあるのかと非常に興味深い内容でした。
ただトークンの暗号化についてはChrystian Vieyra Cortesさんも議論の余地のある見解と仰られていた通り人によって考え方が違う部分だと思いますので、よく検討した上で実装していきたいですね。

Cache Me If You Can

RyuNen344さんによるセッションです。

youtu.be

個人的に今年のDroidKaigiで一番におすすめのセッションです。
AGPやGradleについては何となくの理解で、Gradle周りの記述も公式に実装方法や説明が記載されているなどAGPやGradleのライフサイクルを意識したことはなかったのですが、内部ではこんな風に動いていたのかと非常に勉強になりました。
ConfigurationCacheを有効にする際も、しっかりと仕組みを理解して実装するのとしないのではキャッシュによる恩恵の受け方が大きく変わると思います。
これだけまとめられて濃密な内容が40分程の動画で無料で視聴出来てしまうのには感謝しかありません。

EncryptedSharedPreferences が deprecated になっちゃった!どうしよう!

Yuki Anzaiさんによるセッションです。

youtu.be

ちょうど1年ぐらい前にトークン保存における暗号化の際EncryptedSharedPreferencesがdeprecatedになっており対応をどうしようと頭を悩ませていたのでとても共感出来る内容でした。
deprecatedになった原因のクラッシュも過去に経験して頭を悩ませたことのある問題で普通に実装してると発生してしまいがちの内容だと思いますので、暗号化して内部に保存する際の実装方法を検討する際には非常におすすめのセッションです。
ちなみにクラッシュが発生した際はこのセッションでも触れられているバックアップ設定と例外をキャッシュして対応するようにしました。

暗号化した内容の保存については前述したChrystian Vieyra Cortesさんのセッションでも触れられているので、合わせての視聴がおすすめです。
Chrystian Vieyra Cortesさんの見解然り、Yuki Anzaiさんも思想的転換の章でOSの保護を信じて余計な暗号化はしないとありましたので、暗号化周りについては考えさせられることが多いDroidKaigiでした。

ブース、懇親会

セッション以外にも、ブース巡りや懇親会を大いに楽しむことができました。
特に印象に残ったのは、pixivさんとメルカリさんの企画です。

pixivさんのブースでは「pixiv Q」というアプリをBitrise経由で実際にインストールでき、アプリ内でAndroid開発にまつわるクイズを楽しむことができました。時間帯によって出題されるクイズの内容が変わる仕組みになっており、さまざまな問題に挑戦できたのが面白かったです。

メルカリさんのブースでは「プロンプトゴルフ」という企画が行われており、できるだけ短いプロンプトで課題となる機能を実現することに挑戦できました。アンケートフォームからプロンプトを入力・送信すると、Claude Code GitHub Actionsが連携して実行され、その場で結果を確認できる仕組みです。
企画自体を楽しめたのはもちろんですが、メルカリさんとしても多くの参加者が入力したプロンプトを通じて知見を得られる点も羨ましいなーと思いました。

弊社では「AIファーストカンパニー」を掲げていることもあり、メルカリさんのAIエージェントを活用したこの企画にはとても刺激を受けました。

style.medpeer.co.jp

懇親会では食事や雑談もちろんのこと、Android開発にまつわる情報交換と様々な話題を楽しめました!
弊社からDroidKaigiに参戦したメンバーの都合がつかず、私一人での参加だったということもあり色々な席にお邪魔させていただいたのですが、その中でも特にドワンゴさんとLINEヤフーさんの方とはAI周りの活用事例や最近注目している技術などの話題で長時間に渡り交流をさせていただき、この場を借りて心から感謝申し上げます。

最後に

今年のDroidKaigiもセッション、ブース、懇親会と楽しみつつも学びの多い有意義な大会でした。
毎年ブース巡りをしているとノベルティ等で荷物が溢れて大変になるのですが、今年は最初に大きめのバッグをいただけたこともあり荷物が溢れることもなく、毎年イベントのクオリティがより良くなっていってることを感じます。
このような素敵な大会を提供してくださったDroidKaigi運営の方々・スピーカーの皆様・多くの企業・DroidKaigiに関わるすべての方々に感謝を申し上げます。
また来年も参加できることを楽しみにしております!


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


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

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

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

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

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

SREチームで「AIエージェント縛り」をやってみた

はじめに

こんにちは。SREチームの侘美です。
弊社ではLLM(大規模言語モデル)を活用したコーディング、特にDevinやClaude Codeのようなエージェント型ツールを積極活用する方針を打ち立て、各種ツールを利用できる環境を整備しています。 また、習熟やノウハウの獲得のため各チームで一定期間AIエージェント縛りで開発を行い、得られた知見や課題を共有する活動も進めています。

我々SREチームでも2週間にわたってAIエージェント縛りで開発する実証実験を行いました。 本記事では、この実証実験を通じて得られた、通常のプロダクト開発チームとは異なるSREチームならではの課題や知見を共有します。

前置き

弊社SREチームの特徴

  • 5人チーム
  • 5名で社内の全プロダクトを分担して担当
  • 主な業務:
    • AWS/GCP/Azureの構築・メンテナンス
    • GitHubや各種SaaSの管理
    • プロダクトのアラート対応やフォロー
    • データ分析基盤の構築
  • 使用する言語・ツール
    • HCL(Terraform)
    • JavaScript/TypeScript
    • Python
    • etc

利用したAIエージェントツール

  • Claude Code
  • Devin
  • Gemini CLI

AIエージェント縛りに関するルール

  • 手動でのコーディングは原則禁止
    • 障害対応や納期的に厳しいものは例外
  • 任意のAIエージェントツールを利用可能
  • コミットメッセージ、Git操作、PR作成等は人間が実施してOK
  • 週の終わりに振り返り会を実施する(2週で計2回)

AIエージェントの得意領域

メリットや有効に活用できた場面をメンバー内で共有した結果、いずれのメンバーもほぼ同じ結論となっていました。その中で意見が多かったものを以下に記載します。

1. シェルスクリプト生成

全メンバーが一致して評価したのは、シェルスクリプト生成の精度の高さです。

特に以下のような作業で威力を発揮しました。

  • AWS CLI一括処理: RDSメトリクス取得、複数アカウントの管理作業
  • データ分析スクリプト: CloudWatch metricsの統計処理、CDC スループット計算
  • コード生成用スクリプト: removed block一括生成、設定ファイル自動生成

Aurora MySQL から Confluent Cloud への CDC スループット分析という複雑な要件に対し、400行を超える高度なシェルスクリプトを一発で生成することもできました。(手動では2-3時間かかる作業が30分で完成)

2. ドキュメント・PR説明文の生成

文書作成系の作業でも有効に活用できました。

  • プルリクエスト説明文: コミット履歴から適切な説明を自動生成
  • 技術ドキュメント: 実装内容をもとにした分かりやすい手順書作成
  • コミットメッセージ: Conventional Commits準拠の体裁の良いメッセージ

以下のようにClaude Code用のカスタムコマンドを作成してPR説明文生成を自動化するとかなり捗ったと紹介しているメンバーもいました。

---
allowed-tools: Bash(git log:*), Bash(git diff:*), Bash(git fetch -a:*), Bash(cat:*), Bash(pbcopy:*), Read(*.md), Fetch(*)
description: "現在の作業ブランチからプルリクの説明文を作成します。"
---

* この作業ブランチに含まれるコミットとコミットメッセージをまとめて, プルリクエストに記載するdescriptionをmarkdownで作成してください。
  * 作業ブランチとorigin/masterもしくはorigin/mainブランチと比較することで, その作業ブランチに含まれるコミットとコミットメッセージを抽出します。
    * ローカルのorigin/masterもしくはorigin/mainブランチが古い可能性も考慮してgit fetch -aを事前に実行してください。
* descriptionの書式
  * Claude Codeで作成したことがわかりやすいように, description内にClaude Codeの署名を末尾に含めてください。
  * 可読性を上げるために以下の対応を行なってください。
    * またタイトル等で絵文字を多用してぱっと見で見やすい方にしてください。
    * 可能な限り箇条書きとして閲覧しやすくします。
    * 箇条書きにした項目の関連性によって, 適切な範囲で段落を下げてください。
  * 以下の情報は不要なので省略してください。
    * プルリクエストを見るとわかる, 変更ファイルの一覧や統計情報は冗長であるため不要です。
    * GitHub ActionsのCIによってチェックされる項目はdescriptionには不要です。
* 作成完了しましたら, クリップボードに格納してください。
  * 末尾にEOF等のdescriptionとは関係のない文字列が入らないようにしてください。

3. JSON・設定ファイルの編集

構造化データの編集作業においても手作業に比べてかなりの効率化が見られました。

  • Slack通知のJSONペイロード: 複雑な条件分岐やフォーマット調整
  • Lambda関数の軽微な修正: ESM形式への変更、パッケージ設定の調整

4. 小規模の修正を広範囲に実施するケース

大量のファイルに似た小さい修正を行うような作業でも効果的に感じました。

  • アカウント削除: 複数サービスからの特定ユーザー一括削除
  • 設定ファイル更新: 複数環境への同一設定適用
  • リソース名変更: 命名規則に合わせた一括リネーム

AIエージェントの苦手領域・課題

一方でうまく実装できない領域や課題も多く発見されました。 特にこの実証実験前から懸念していた、Terraformなどに代表される宣言的コードとの相性の悪さが浮き彫りになりました。

1. Terraformとの相性の問題

全メンバーが一致して指摘していました。

古いリソース・非推奨属性の提案

  • TerraformのProvider更新が頻繁なためか、AIの精度が低い
  • 推奨されない属性や廃止予定のリソースタイプを提案
  • 最新のベストプラクティスに従わないコード生成
  • MCPを利用してもそこまで改善されない

宣言的コードの特性による非効率性

Terraformなどの宣言的コードでInfrastructure as Codeを実現するケースにおいては、設計が完了すれば後はその設計をそのままコード化するだけなので実装上で悩むシーンはそこまで多くありません。

一方、Ruby等のプログラミング言語でバックエンドサービスを構築する場合、設計が完了した後もメソッドの分割の仕方や同じ処理系でも実装方法が無限にあるなど、詳細設計〜実装〜テストとまだまだ考慮すべき点が山程あります。この工程をAIに行わせることでかなりの生産性向上が見込めます。

そのため「自然言語で作業を指示する」というAIエージェントの利用方法とTerraformの相性の悪さを感じる結果となりました。

学習データ不足

Terraformのコードはそこまで複雑ではないのにもかかわらず、LLMによるコーディングの精度が悪いことは各所で言及されています。

一般的なプログラミング言語はOSSとして質の高いコードがいくつもWeb上で公開されています。 Terraformの場合もAWSが提供する公式モジュール等、良いコードがいくつも公開されています。 ですが私達サービスを構築するSREチームが参考にすべきような、『実際に運用されている大規模サービスのTerraformコード』が公開されている例は多くないと思います。

このように、実用に耐えうるレベルの質の高い学習のためのコード不足がそのまま他の言語と比べて精度が悪いといった結果を引き起こしているのでは?という考察もメンバー内で上がっていました。

2. 指示作成コストの高さ

複雑な要件の言語化が困難

  • 正確な実装を得るために、自然言語でterraformを記述することになる
  • プロンプト作成時間が実装時間を上回るケースが頻発
  • 大きい作業は1プロンプトより細かく小出しに指示した方が正確だが、これは結局、自然言語でコードを書いているようなもの

3. editorconfig・Linterルールの無視

設定ファイルの強制適用が困難

  • .editorconfigの設定をAIに従わせる効果的な方法がない
  • コード生成後に手動でフォーマット修正が必要
  • 自動修正(fix)ツールの不足

こちらはHooksの登場により、整備することである程度解消できる目処が見えてきましたね。 (実証実験中にHooksが発表されました)

一貫性のないコードスタイル

  • チームの規約に合わないインデントや改行
  • 命名規則の不統一
  • コメントスタイルの不一致

4. git操作の危険性と制御の困難さ

予期しない操作の実行

このあたりはAIエージェントあるあるで有名だと思いますが、実際に期間中にメンバーがいくつか遭遇していました。

  • rebase指示すると作業がループする
  • 追加実装したコードを消される
  • PRが勝手にクローズされる

(SREチームがコミットメッセージやコミットの粒度に厳しいメンバーが多く、より綺麗なコミットに修正しようとしたため遭遇率が高かった可能性もあります)

5. コストと経費処理の複雑さ

Claude Codeの支払いを会社側が行い、社員をメンバーとして利用させるようなチームプラン的なものが現状ありません。 そのため弊社では「AWS/GCPでClaudeを従量課金で動かして一人 100USD/月 を超えないように管理する」or「個人でProプランやMAXプランを契約して経費精算」という少々手間のかかる運用になっています。

その他知見

AWSコスト分析

AWS Cost Explorerから取得したデータを使い、アカウント別コスト比較や増減理由の分析を素早く行うことができました。

mise.toml活用

asdfからmiseに移行し、タスクランナー機能でvalidate, fmt, lint, trivyを統合実行できる環境を構築することで、指示やカスタムコマンドを簡略化できて効率的でした。

まとめ

2週間実装の全てをAIエージェントにすることで、得意不得意や有効的な活用方法が見えてきました。 特に現状の精度では、SRE領域のタスク全てを無理にAIエージェントに任せようとすると、かえって生産性が落ちる状況です。そのため、これらの知見をチームで共有できた点でも、かなり有用な実証実験だったと考えられます。

現状、ある程度の使い分けの方針は見えてきましたが、AIに関しては日進月歩で進化している領域なのでこれら苦手領域を克服してくる日も遠くないのでしょう。 随時エージェントの性能改善に関する情報をキャッチアップしつつ最も業務効率が上がる使い方を模索し続ける必要がありそうです。


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


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

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

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

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

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

YAML + Rakeタスクで実現する「権限漏れゼロ」なBI運用

こんにちは、サーバーサイドエンジニアの長谷川(@hasehiro25)です。

今回の『ClinPeerアプリ開発の裏側』連載では、BIツール運用の実践的なTipsをご紹介します。 tech.medpeer.co.jp

BIツールの運用における課題として、「テーブルやカラムが追加された際のメンテナンスに手間がかかる」があります。

参照できるカラムをリストで管理する「許可リスト方式」では更新漏れが起きやすく、逆に参照できないカラムを管理する「拒否リスト方式」では、意図せず個人情報などのカラムが参照可能になってしまうリスクがあります。

そこでClinPeerでは、テーブルやカラムが変更された際にCIでチェックを行い、BIで参照できるデータを安全に更新する仕組みを導入しています。これにより、権限設定の抜け漏れを防いでいます。

本格的な分析用BIツールと、エラー調査用の簡易BIツールとしてBlazerをClinPeerでは導入しており、今回は後者のBlazerをメインにご紹介します。なお、両ツールとも共通でBI用スキーマファイルを使用しています。

github.com

specによるschemaチェック

ClinPeerでは、schema_for_bi.ymlというファイルで、Blazerから参照できるスキーマ情報を管理しています。

ファイルの中身は以下のようなイメージです。

articles:
  - id
  - created_at
  - updated_at
  - title
  - body

users:
  - id
  - created_at
  - updated_at
  - nick_name
  # - email

YAMLファイルの構造は、articles:のようにまずテーブル名を記述し、その配下にカラムをリスト形式で記述するシンプルなものです。

ここで重要なのが、リストの中でも#でコメントアウトされているカラムの扱いです。これらは意図的に参照を許可しないカラムとして扱われます。

コメントアウトを用いる目的は、カラムの存在を把握しつつ、「これは意図的に許可してないよ」という状態を誰が見ても分かるようにするためです。 もし「許可リスト方式」の場合、それが意図的に書いてないのか、単なる追加忘れなのかを区別できません。コメントアウトによって、その曖昧さをなくしています。

このYAMLファイルがデータベースの現状と一致していることを担保するために、CIで以下のようなspecを実行しています。

it "テーブル定義順がABC順であること" do
  tables = YAML.load_file("db/schema_for_bi.yml").keys
  expect(tables).to eq(tables.sort)
end

it "全てのスキーマ情報が記載されていること", aggregate_failures: false do
  expect_tokens = []
  ActiveRecord::Base.connection.tables.sort_by(&:itself).each do |table|
    expect_tokens << table
    columns = ActiveRecord::Base.connection.columns(table).map(&:name)
    columns.each { |column| expect_tokens << column }
  end

  yml_lines = File.readlines("db/schema_for_bi.yml")
  yml_lines.fill("", yml_lines.size..expect_tokens.size - 1).zip(expect_tokens).each do |actual_line, expect_token|
    expect(actual_line).to include(expect_token)
  end
end

内容はシンプルで、ActiveRecord::Base.connection.tables でテーブルとカラムの情報を全て取得し、File.readlinesで読み込んだYAMLファイルの内容と一致するかを一行ずつ確認します。(コメントアウト部分含む)

このspecがパスすれば、YAMLファイルがデータベースの現状を正しく反映していることが保証され、安全にBlazerの権限設定に進めます。

Rakeタスクによる権限更新

specのチェックを通過したYAMLファイルを使い、実際に権限を更新するためのRakeタスクがこちらです。

namespace :bi do
  task initialize_user: :environment do
    exec = ->(sql) { ActiveRecord::Base.connection.execute(sql) }
    exec["DROP USER IF EXISTS #{Setting::Blazer.db_user_name};"] # 権限リセット
    exec["CREATE USER #{Setting::Blazer.db_user_name} IDENTIFIED BY '#{Setting::Blazer.db_user_password}';"]
    exec["GRANT SHOW VIEW ON * TO #{Setting::Blazer.db_user_name};"]
  end

  task grant_select_columns: :initialize_user do
    YAML.load_file("db/schema_for_bi.yml").each do |table, columns|
      # 「read」など、MySQLの予約語に定義されたカラム名を、テーブル登録するカラム名として認識されるように、バッククォートで囲う
      columns = columns.map { |s| "`#{s}`" }.join(",")
      ActiveRecord::Base.connection.execute("GRANT SELECT (#{columns}) ON #{table} TO #{Setting::Blazer.db_user_name};")
    end
  end
end

initialize_userで古い権限設定を無効化して、新たな権限を適用する初期化処理を実行します。

そのあとgrant_select_columnsではYAML.load_file を使ってYAMLファイルを読み込み、それぞれのテーブルとカラムに対して権限設定を行なっています。コメントアウト部分は無視されるため、結果として参照を許可したいカラムにのみ SELECT 権限を付与する GRANT 文が実行される仕組みになっています。

grant_select_columnsの実行タイミングですが、他のデプロイ用タスクと合わせて追加しており、デプロイ時に自動で実行されるように設定しています。

namespace :deploy do
  task pre_hook: %i[setting:validate db:create db:migrate db:seed bi:grant_select_columns]
end

まとめ

BIツールの権限設定は後回しにされがちで、「いざデータを見たい!」という時に参照できない、といったことが起こりがちです。

今回ご紹介したように、テーブル構造の変更と同時に権限設定もチェックする仕組みをCIに組み込むことで、最新かつ安全な状態でデータを参照できる環境を維持できます。

「いつかやろう」と溜まりがちな作業は、CIを活用して日々コツコツと対応していくことで、少しずつ快適な開発環境に繋がっていくと思いますので、ぜひ参考にしてみてください。


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


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

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

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

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

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

Notion のタスクのメモはどこに書く?コメント機能よりも「ページ下部」がオススメな理由と実践方法

こんにちは。メドピア内 Slack チャンネル 「#club_notion」 部長の佐藤太一(@teach_kaiju)です。

今回の「ClinPeerアプリ開発の裏側連載記事」では Notion を用いたタスク管理におけるメモの取り方を紹介します。

tech.medpeer.co.jp

目次

はじめに

Notion はその自由度の高さから、様々な情報を集約できる万能ツールとして扱うことができます。特にタスク管理においては、詳細情報だけでなく、関連するメモやアイデア、Slack でのやりとりのリンクなどを一緒に残しておきたい場面も多いのではないでしょうか。

そんなとき、「タスクに関するメモ、どこに書いていますか?」

よく使われるのは Notion の画面上部のコメントだと思います。

コメント(画面上部)

手軽に書ける反面、他タスク管理ツールとの挙動の違いで戸惑った経験はありませんか?

今回は ClinPeer チームの開発タスクで実際に行われている、メモはコメント機能ではなく「タスクページの下部」に書くという文化を紹介したいと思います!

コメント機能のよくある課題点

手軽に使えるコメント機能ですが、タスクのメモを残す場所としては、いくつかの課題があります。 情報は2025年5月時点のものです。

課題1: コメントのリンクを取得することができない

コメントのスレッドのリンクは取得できますが、2つめ以降のコメントはリンクが取得できません。

コメントへのリンクがない

課題2: Enter で送信

誤って書き途中のまま送信してしまいがち。
(Enter は改行という挙動に切り替えられるようになってほしい)

課題3: 場所が画面上部で折り畳まれるデザイン

画面上部のコメントは数が多いと折り畳まれます。中身を確認するために開く必要があり、これが手間です。

コメントの折りたたみ

ページ下部へのメモの仕方

ページ下部にメモをとることで、上記の課題を解決できる他、ブロックを用いた見やすいデザインを作ることができる等のメリットも得られます。

その具体的な方法がこちらです。

ページ下部にメモ

  1. 「Memo」という見出しを作る
  2. 日付を書く
  3. 誰が書いたのかを示すために自身の絵文字のアイコンを出す(省略することはよくあります)
  4. 内容を記述

主なメモの内容

  • タスクの進捗
  • 関連するやりとりへのリンク
  • 思ったこと・気づき
  • 課題
  • 簡易的な議事録

特にタスクに関連するやりとりへのリンク(Slack等)はメモっておくと後から見返す時に非常に助かります。

タスクに関するMTGを行う場合は、最近出た AI ミーティングノートをタスクページに作るのも良さそうです。

Q & A

Q. 他のメンバーへの通知はどうする ?
A. 画面上部のコメント使います。内容が多い時は Memo のリンクをコメントに貼ります。ページ内メンションは使っていません。

Q. ページがどんどん長くなって見づらくならないか?
A. 画像を大量に貼ったりすると長くなりますが、トグルで折り畳めば気になりません。

まとめ

今回はタスクのメモをページ下部にとる具体的なやり方を紹介しました。

タスクにメモをとっておくと、タスクページそのものがドキュメントの役割を果たしたりすることが可能になります。情報を追いやすくなるためとてもオススメです。

また、本手法はチームでのタスク管理だけではなく、個人でのタスク管理でも使えます。
ぜひ試して見てください!


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


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

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

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

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

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

SwiftUIにおけるEnvironmentの活用法

こんにちは!メドピアにてモバイルアプリエンジニアをしている王です。 今回の「ClinPeerアプリ開発の裏側連載記事」では、SwiftUIのEnvironmentについてお話しできればと思います。 tech.medpeer.co.jp

SwiftUIのEnvironmentは、ビュー間でデータを共有するための強力な依存性注入(DI)の仕組みです。多くのSwiftUIプロジェクトで活用されており、ビュー間のデータ伝達を簡素化し、アプリ設計の柔軟性を高めます。ClinPeerでは、ほぼすべての画面をSwiftUIで構築しており、Environmentの活用について考察しています。

SwiftUIにおけるUI専用の依存性注入

依存性注入(Dependency Injection、DI)は、モダンなソフトウェア開発において重要な技術であり、以下の利点があります。

  • 疎結合:コンポーネント間の依存関係を最小限に抑えることで、モジュールの再利用性と保守性が向上します。
  • 保守性:依存関係が明示的になることで、コードの理解と修正が容易になります。
  • テスト容易性:依存関係を差し替えることで、ユニットテストが容易になります。

SwiftUIのEnvironmentは、ビューがロードされた後にのみ値を取得できるという特徴があります。この設計は、SwiftUIが宣言型UIフレームワークであることを反映しており、ビューの構築と更新がデータや環境と一貫性を保つようになっています。

値型以外の活用

Environmentは、単なる値型の注入にとどまらず、関数、ファクトリーメソッド、プロトコル制約など、さまざまな形式の依存性を注入できます。実際、SwiftUIの標準Environment値には、editModedismissmanagedObjectContextなど、非値型の例も含まれています。

開発者は、Environmentの活用を値型に限定せず、SwiftUIの特性を活かして、より柔軟で疎結合なコンポーネントを構築することが重要です。

Observationフレームワークの活用

EnvironmentObjectを使用する際、依存性の注入を忘れるとアプリがクラッシュするリスクがあります。一方、Environmentはデフォルト値を要求するため、より安全で信頼性の高い設計が可能です。iOS 17以降、Observationフレームワークの導入により、可観測オブジェクトの注入がさらに容易になりました。

extension EnvironmentValues {
    @Entry var article: Article = .init()
}

@Observable
class Article {
    // プロパティやメソッドを定義
}

struct ContentView: View {
    @Environment(\.article) var article
    var body: some View {
        // ビューの構築
    }
}

この方法では、クラッシュのリスクを回避し、同じ型の可観測インスタンスを複数使い分ける柔軟性も得られます。

extension EnvironmentValues {
    @Entry var article: Article = .init()
    @Entry var article1: Article = .init()
    @Entry var article2: Article = .init()
}

Environmentの最適化

Environmentを使用してアプリの状態を管理する際、ビューの更新効率がユーザー体験に影響を与えることがあります。以下の最適化戦略を採用することで、不要なビューの再描画を抑えることができます。

精密な注入

複数のサブ状態を含む複合値型に対して、特定のプロパティのみを注入することで、不要な更新を回避できます。ビューが実際に必要とする部分の状態だけを購読することで、より効率的なリアクティブUIを構築できます。

struct UserState {
    var height = 175 // 単位: cm
    var weight = 75  // 単位: kg
}

extension EnvironmentValues {
    @Entry var userState = UserState()
}

struct HeightView: View {
    // heightのみが更新対象
    @Environment(\.userState.height) var height
    var body: some View {
        Text("身長: \(height) cm")
    }
}

struct WeightView: View {
    // weightのみが更新対象
    @Environment(\.userState.weight) var weight
    var body: some View {
        Text("体重: \(weight) kg")
    }
}

struct BMIView: View {
    @Environment(\.userState) var userState
    var body: some View {
        let heightInMeters = Double(userState.height) / 100.0
        let bmi = Double(userState.weight) / (heightInMeters * heightInMeters)
        return Text(String(format: "BMI: %.2f", bmi))
    }
}

struct RootView: View {
    @State var userState = UserState()
    var body: some View {
        List {
            Button("身長を変更") {
                userState.height = Int.random(in: 130...220)
            }
            Button("体重を変更") {
                userState.weight = Int.random(in: 35...120)
            }
            HeightView()
            WeightView()
            BMIView()
        }
        .environment(\.userState, userState)
    }
}

条件付きの更新

transformEnvironmentを使用すると、特定の条件を満たす場合にのみ環境値を更新できます。これにより、更新頻度を減らし、アプリの応答性と滑らかさを向上させることができます。

struct RootView: View {
    @State var userState = UserState()
    @State var height = 175
    var body: some View {
        List {
            Button("身長を変更") {
                height = Int.random(in: 130...220)
            }
            HeightView()
        }
        .transformEnvironment(\.userState) { state in
            guard height > 150 else {
                print("無視: \(height)")
                return
            }
            state.height = height // height > 150 の場合のみ更新
        }
    }
}

Environmentとサードパーティ製DIフレームワークの併用

SwiftUIのEnvironmentは優れた機能を提供しますが、ビューのライフサイクルに厳密に制限されます。ビジネスロジックをViewModel層に分離したり、ユニットテストを行う場合、サードパーティ製のDIフレームワークの使用が有効です。ClinPeerでは、FactoryというSDKを使用しています。

@Observable
class UserState {
    var height: Int = 175
    var weight: Int = 75
}

extension Container {
    var userState: Factory<UserState> {
        Factory(self) { UserState() }
            .scope(.shared)
    }
}

// 使用例
@Injected(\.userState) private var userState

ハイブリッドアーキテクチャの利点

Environmentとサードパーティ製DIツールを併用することで、以下の利点が得られます。

  • 柔軟なアーキテクチャ:ビュー層ではSwiftUIのEnvironmentを使用し、ビジネスロジック層ではFactoryなどのDIツールを活用できます。
  • テストの容易性:ビジネスロジックをUIから完全に切り離すことで、ユニットテストやモック化が簡単になります。
  • 保守性の向上:特定のUIフレームワークへの依存を最小限に抑え、コードベースの長期的な安定性と再利用性を高めます。

まとめ

SwiftUIのEnvironmentは、依存性注入とビューのライフサイクルを一体化した設計であり、視覚的なUIコンポーネントの境界を明確にしながら、ビュー階層におけるデータの効率的かつ制御可能な伝達を実現します。

また、精密な注入や選択的な変更によって、不要なビュー更新を避けることでアプリ全体の効率を高めることができます。 さらに、SwiftUIのエコシステムにおいては、柔軟にサードパーティ製のDIフレームワークを取り入れることで、より複雑なユースケースにも対応可能です。

Environmentの設計に対する深い理解は、拡張性が高く品質の高いSwiftUIアプリを構築するための確かな基盤となります。


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


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

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

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

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

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