この記事は「 MEDLEY Summer Tech Blog Relay 」11 日目の記事です。 はじめに こんにちは、メドレーでAI推進している人材プラットフォーム本部 VPoE の倉林( @terukura )です。 前回の記事「 AI for All - 全社でAI活用を推進する取り組み 」でお伝えした通り、メドレーでは「AI for All」を合言葉に、全社的なAI活用を推進しています。あれから約4ヶ月が経過し、プロダクト開発チームにおけるAI活用は大きく前進しました。 本記事では、メドレーのプロダクト開発におけるAI活用の現在地を、具体的な数値とともに振り返ってみます。 AI活用に関する発表では、制度としての一人n万円の支援制度、全社Cursor/Claude Max Plan導入、全社でn億円の投資などマクロな数字が注目されがちですが、本記事ではなるべく実際にどのツールをどれくらい使っているのか、具体的な利用状況を公開している例は意外と少ないため、皆様の参考になれば幸いです。 1〜2週間ごとに新しい技術やツールが登場する昨今、最適解はないと感じています。各社の状況や文化に合わせ変化に強い組織を作ることが重要だと考えています。あくまで一つの事例として、メドレーの取り組みをご紹介します。 メドレーのプロダクト開発におけるAI活用の方針 メドレーでは、以下の3つの方針を軸にAI活用を進めています。 1. ツールに縛られない柔軟な選定 特定のAIツールやサービスに固執せず、AI技術やモデルの進化に合わせて最適なツールを選定しています。例えば、コーディング支援ツールだけでも複数のオプションを並行して検証し、用途や開発者の好みに応じて使い分けています。 2. 継続的なモニタリングと改善 導入して終わりではなく、日々の利用状況や生産性向上の効果を定量的にモニタリングし、PDCAサイクルを回しています。月次でROIを評価し、効果が薄いツールは見直しを行います。 3. 開発プロセスへの深い統合 GitHub ActionsでのAIレビュー自動化、監視/アラートへのAI組み込みなど、開発プロセスの各段階にAIを統合しています。これにより、エンジニアが意識せずともAIの恩恵を受けられる環境を構築しています。 現在メドレーで利用・検証中の主なAIツール・サービス 2025年9月時点 で、メドレーでは以下のAIツール・サービスを主に活用しています。 コーディング支援ツール Claude Code、Cursor、Devin、Codex、GitHub Copilot、Gemini CLI AIモデルプロバイダー Google Vertex AI、Azure OpenAI、AWS Bedrock、Anthropic API、OpenAI API、BytePlus ModelArk ワークフロー・自動化 Dify、n8n コードレビュー支援 Claude Code Action、CodeRabbit、GitHub Copilot Review、Devin Review チャット・汎用AI ChatGPT Team、Claude Team 、Gemini その他のツール Langfuse、NotebookLM、Dia、Perplexity、Snyk、Mastra 数値でみる主な利用状況 Claude CodeについてはパワーユーザーはMax Plan、その他の利用者向けにはVertex AI経由で提供 GPT-5の登場により徐々にCodex CLIの利用も増加傾向(TeamPlan/ProPlan) 利用ModelとしてはClaude Sonnetの割合が高い、次点でOpus・GPT(Claude Max Plan ユーザー除く) Difyについては徐々に全従業員が触れ合うツールになりつつある、Model+Prompot+フローの検証利用としても拡大中 トータルで350-400万円/月 くらいがコスト観点での現在地 生産性への影響、FourKeysの変化などはまた次の機会に詳細を共有させていただければと思っています。 今後の展望 AI駆動開発(AI-Driven Development)への移行 AIが開発の中心となる新しいパラダイムへの移行を目指します。 自然言語での要件定義から実装までの自動化 AIエージェントの本格導入・開発 AIによるUI/UX設計・プロトタイピング AIによるアーキテクチャ設計の提案・レビュー CI/CDパイプラインとの深い統合 QA自動化 パフォーマンス最適化の自動実行 インシデント対応・管理の自動化 今回は主にプロダクト開発におけるAI活用の現在地点を利用状況を元に振り返ってみました。 「同じような利用状況の方」、「もっと活用している方」、「まだまだ未活用で活用方法を聞きたい方」 など ぜひ詳細など情報交換させていただければと思います。 直近はMEDLEY AI CLOUDの発表や、今回のMEDLEY Summer Tech Blog RelayでのAI関連の記事など AI関連の発信もどしどし行っていますので、ぜひ興味をもっていただければと思います。 MEDLEY AI CLOUD MEDLEY Summer Tech Blog Relay:組織で育てるAI活用テスト設計の仕組み MEDLEY Summer Tech Blog Relay:Claude Codeで「Context left until auto-compact: 15%」が出たときの対処法 MEDLEY Summer Tech Blog Relay:データ戦略グループにおけるcontext engineeringの取り組み 明日の「MEDLEY Summer Tech Blog Relay」の記事は、デザイナーの近藤さんです! We’re hiring! メドレーでは一緒に働く仲間を大募集しています。 AI と共に成長したいエンジニアの方 、ぜひお話させてください! カジュアル面談も実施しております。話だけでも聞いてみたい!ちょっと雑談してみたい!でも構いませんので、お気軽にお問い合わせください! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp 医療エンジニアリング領域盛り上がっています!メドレーについてお話します! メドレーのエンジニア組織・AI活用についてお話します! 「メドレーのエンジニア組織・AI活用についてお話します!」- 倉林 昭和さん pitta.me
この記事は メドレー夏のブログリレー 2025 6 日目の記事です。 はじめに こんにちは、医療プラットフォーム本部データ戦略グループの安東です。 データ戦略グループでは、データ基盤の構築から可視化、分析、ダッシュボード作成まで担い、データ活用を促進することで事業成長と医療ヘルスケアの未来に貢献することをミッションにしています。 そのため、日々データを扱う業務をしているのですが、最近チームでこんな議論がありました。 「AI を用いてデータ分析する際にテーブルのメタデータだけでなく、ビジネスロジックや過去の分析知見、議論の文脈まで理解させることが大切だよね。」 皆さんのデータアナリスト組織でも、AI を活用したデータ分析で同じような課題を感じることはないでしょうか。 当社もデータ分析の生産性を高めるため、Cursor や ClaudeCode といった AI ツールを積極的に活用しています。 その過程で、データアナリストに新たな価値提供の機会が広がっていると感じており、その鍵となるのが「 context engineering 」と考えています。 context engineering とは、 AI がタスクを達成できるように、適切な知識・ツール・求める出力形式を提供することです。(参考: The rise of “context engineering” ) 今回は、「Analytics Knowledge as Code」という形で、分析における context engineering の取り組みについてお話しします。 なお、Analytics Knowledge as Code の考えは、AWS Summit で紹介されていた「 AI Agent 時代のソフトウェア開発の型 〜 Everything as Code で叡智を伝える 〜 」の内容を参考にしています。 AI によって変わる分析の風景 生成 AI の登場で、SQL の記述、前処理、集計、可視化といったデータ分析作業の多くが効率化できるようになりました。 では、すべての分析業務が AI に置き換えられるでしょうか? 少なくとも現時点では、分析業務にはグラデーションがあり、AI に置き換え可能な領域とそうでない領域に分かれると思います。 下図のように、分析要求の複雑性によって、データ分析における AI と人間の役割が分かれてきています。 単純なデータ抽出やレポート作成は、BI ツールによるセルフサービスで十分です。やや複雑な要件も、AI Agent で対応できつつあります。 しかし、複雑性の高い分析では ステークホルダーと議論しながら曖昧な要件を明確にし、分析の定義、解決する問いのディスカッションを通じて、意思決定を支援 することが求められます。 事業部と認識を揃えつつ、丁寧な合意形成も欠かせません。 この領域のデータアナリストには、 課題を主体的に定義し、その内容に即した最適な分析アプローチを設計する力が、核心的な価値として求められるようになってきています。 データ分析は過去・現在のコンテキストが重要 ここで重要なのは、企業での分析は一度きりの独立したタスクではないということです。過去の分析結果や現在の議論状況に依存する性質を持っています。 例えば、解約率の分析を行う際には、単に数値の変化を見るだけでは不十分です。 過去の解約要因、顧客セグメントの特性、競合サービスの動向、外部環境の変化 といったコンテキストがあってこそ、意味のある洞察が得られます。 一例ですが、メドレーのような医療ヘルスケア領域では、以下のような特殊な文脈が重要になるケースもあります: 診療報酬改定: 制度変更が事業指標に与える影響 季節性: 疾病の流行期や健康診断シーズンなど医療特有のパターン 規制環境: 関連省庁のガイドラインの変更等 業界慣習: 医療における事業者の意思決定プロセスの変化 こうした 蓄積された文脈(コンテキスト)があってこそ、質の高い分析が可能になります 。 しかし、モデルは自動的に賢くなっていく一方で、このコンテキストの整備は人間に委ねられています。 リソースは有限なので、求められる品質やビジネス要件を満たして期日までに意思決定を行うためには、AI が効率的に活用できるよう適切なコンテキストを整備することが不可欠です。 Analytics Knowledge as Code:なぜ今、この考え方が重要なのか そのため、分析ナレッジを「コード化」する Analytics Knowledge as Code の取り組みを始めています。その背景には 3 つの課題があります。 1. 分析による意思決定過程のナレッジ共有の課題 多くの分析組織では、事業部に専属のデータアナリストが配置される体制を取っているケースが多いと思います。 この体制では、各事業の データ構造の知識や成り立ち、過去の分析インサイト が、一人のデータアナリストに集中しがちです。 当社でもデータ分析チーム全体として、共通知見を蓄積することに課題があります。 2. AI には人間向けドキュメントだけでは不十分 社内 Wiki や分析ドキュメントは人間が読むことを前提としていますが、 AI が効率的に参照・活用するには構造化された情報が必要 です。 特に重要なのは、コンテキストエンジニアリングにおいて、AI に広範囲を探索させるよりも、 探索させる空間を狭くして、その空間内の情報の質を高めるアプローチの方がより期待に沿った応答を返してくれる という点です。 そのため、データ分析における AI が参照するナレッジは共通化して、品質の高い状態を管理する価値が高いと考えています。 3. 再利用性と保守性の担保 ナレッジは日々更新されていくため、継続的に更新し、信頼できる状態を担保する必要があります。 ソフトウェア開発のように、 バージョン管理、レビュープロセス、継続的な改善 を分析ナレッジにも適用することで、組織全体の分析品質を向上させることが必要になります。 コンテキストを「コード」として管理する手法 Analytics Knowledge as Code は、データ分析における知識やノウハウを、ソフトウェア開発のコードと同様に管理・共有する考えです。 管理する方法として、 GitHub リポジトリを活用したナレッジ管理 により、以下の要素を実現します。 データカタログやプロンプトエンジニアリングでは難しい領域をカバーします。 項目 内容 分析ナレッジの保守 分析結果のテンプレート化による標準化、バージョン管理による変更履歴の追跡 ビジネスロジックの反映 対象の分析がどのようなビジネスモデル、ロジックなのか理解させる お手本 SQL や分析手法の例示 AI にゼロから判断させずに、参考になる SQL や分析パターンを参照させる 過去分析の知識 過去の分析議論過程や意思決定の内容を参照させる Analytics Knowledge as Code のイメージ:GitHub リポジトリに分析ナレッジ蓄積 私たちは分析プロジェクトごとに、上図のような構成で、ナレッジを GitHub リポジトリで管理する取り組みを始めています。 これにより次回の分析では、過去のナレッジをベースに、条件の検討や定義のズレによるコミュニケーションエラーを減らすことができています。 今後、メンバーに変更があった際にも、過去の分析上の判断根拠を理解した上で分析に取り組めるようになることを目指しています。 MCP を活用したコンテキスト補完 ナレッジリポジトリと MCP を組み合わせることで、AI が分析時に過去の知見を参照できるようにしています。 この取り組みにおいて重要なのは、「人間が決めて、AI が分析を実行する」という以下のような役割分担です 項目 内容 問題定義 データアナリストがステークホルダーと協力して課題を明確化 分析実行 AI が SQL 生成、前処理、基本統計の処理、可視化といった分析ワークフローの実施 結果解釈 データアナリストが主導し、AI が集計結果から解釈を支援 意思決定 最終的な判断は必ず人間が行う このような役割分担を実現するために、AI が効率的に動作するための 意思決定過程の言語化と構造化された文脈の共有 が不可欠になります。 そのため、私たちは以下の要素も体系的に管理することを目指しています: 項目 内容 ドメイン知識 医療業界特有の制約や業界慣習 ステークホルダー情報 各事業部のビジネス戦略、ロードマップ、意思決定軸 過去のプロジェクトの意思決定履歴 会議の議事録や議論の文脈 データ定義と品質ルール テーブル構造やデータの具体的な使い方 これにより、データアナリストが持つドメイン知識を組織の資産として活用し、より質の高い分析を効率的に行えるようになることを想定しています。 AI とデータアナリストの役割分担 AI はデータ集計やクロス集計といった処理を得意とし、膨大な情報を効率的に整理することができます。 一方で、「何を解くべきか」という問いを立てたり、仮説検証の結果をどう意思決定につなげるかといった部分は、人間だからこそ担える役割 です。 単純な集計や事象確認は AI に任せることでスピードと効率を得られますが、複雑なテーマでは過去の分析や議論、外部環境を踏まえて設計し、ステークホルダーと議論しながら最適な進め方を探る必要があります。 ときには分析にかけるコストと成果を比較し、データ分析以外の手段を選ぶ判断も求められます。 つまり、AI は「効率的に分析を実行する力」、データアナリストは「問いを設定し意思決定へ橋渡しする力」を発揮することで、それぞれが補い合いながら最大の価値を生み出していく と考えます。 AI × 人間のベストな協働を目指して Analytics Knowledge as Code は、こうした新時代のデータアナリストに求められる能力を組織レベルで実現するための取り組みです。 LLM のモデルは今後も性能が向上していきますが、コンテキストの整備は組織が担う必要があります。 AI エディタや GitHub Copilot などのツールにより、従来はエンジニアの専門領域だったコードやリポジトリの管理の障壁は大幅に下がっていると感じています。 「なぜそれが必要なのか」「どう組織の価値に繋がるのか」 を理解し、積極的に取り組む姿勢があれば、どのようなデータ分析組織でも始められると思います。 AI が正しく・早くデータ分析を遂行するための context engineering の整備が、これからのデータ組織に求められる役割の一つと言えるでしょう。 メドレーでは今後も、医療・ヘルスケア領域における課題解決に向けて、こうした AI × データ分析の発展に取り組んでいきます。 同じような取り組みを検討されている組織の方々と、ぜひ知見を共有できればと思います。 We’re hiring! メドレーの医療プラットフォーム本部のデータ戦略グループでは、AI 時代の新しいデータ分析にチャレンジしたいデータアナリスト・データエンジニアを募集しています。Analytics Knowledge as Code の実践や、医療ヘルスケア領域でのデータ活用にご興味をお持ちの方は、ぜひお気軽にお声がけください! 参考 The rise of "context engineering" Header image from Dex Horthy on Twitter. Context engineering is building dynamic systems to provide the right information and tools in the right format such that the LLM can plausibly accomplish the task. Most of the time when an agent is not performing reliably the underlying cause is that the blog.langchain.com pages.awscloud.com pages.awscloud.com Medley Summer Tech Blog Relay 7 日目は、人材プラットフォーム本部の山邉さんの記事です!
はじめに こんにちは。医療プラットフォーム本部 プラットフォーム開発室の島谷です。メドレーでは 2019 年から新卒エンジニアの採用を続けており、毎年新卒向けエンジニア研修を実施しています。 本記事では、2025 年に実施した新卒研修の設計思想とプログラムの全体像をご紹介します。メドレーで働くことに関心のある学生の方はもちろん、私たちの開発文化に興味のあるエンジニアの方にも、メドレーという会社の雰囲気が伝わる内容になれば幸いです。 新卒研修の目的 私たちが大切にしているのは、単なるスキルの習得にとどまらず、「課題にどう向き合うか」「どのように学び、成長していくか」といった姿勢やマインドセットを身につけてもらうことです。 新卒研修のミッションは、エンジニアとして必要な基礎技術と、問題解決に欠かせない考え方を習得し、配属後に高い成果を発揮するための土台を築くことにあります。研修では「技術的な基礎力」と「問題解決力」を重点的に養い、配属後すぐに開発を進められる状態を目指します。 もっとも、研修期間だけで一人前のエンジニアになることは難しく、だからこそ長いキャリアを通じて自ら成長し続ける姿勢が欠かせません。そうした背景があるからこそ、私たちはスキル以上に「成長し続けるための姿勢やマインドセット」を大切にしています。 研修の全体像 今年の新卒研修は、大きく 4 つのステップで構成されています。 まず社会人として必要なビジネススキルとマインドを身に付け、技術的な基礎を作り、そのうえで実務としての開発を体験し、最後に成果発表会で振り返りを行い今後の成長へつなげる。学びを段階的に深めていく一連の流れを設計しています。 期間と研修全体の流れ HC 研修、外部研修 人事部が実施する内製の HC(Human Capital) 研修と、外部研修を実施しました。HC 研修では、社会人に必要な基本動作(報連相や PDCA)やマインド(ニーズを理解しニーズに応える)の基本を習得してもらい、外部研修では、より体験的な演習を通じて社会人としての自覚を持つきっかけとしました。 基礎研修 エンジニアに求められる 横断的な基礎知識の習得、深く学び続ける姿勢の醸成、チームで成果を出すためのコミュニケーション能力の向上 を目的としています。 フロントエンド(TypeScript/React)、バックエンド(Ruby on Rails)、インフラやミドルウェア(データベース、AWS)など幅広い領域を実践形式で扱いました。 加えて、CTO・VPoE による事業説明や、メドレーでエンジニアとして働くうえでの心構えを学ぶセッションも実施。QA チームやデザインチームからは、それぞれの取り組みを共有してもらい、組織全体での開発の在り方についても学んでもらいました。今年は新たな取り組みとして、AI 活用に関する講義も行っています。 VPoE 山﨑さんによる講義の様子 事業部 OJT、開発 OJT 事業部 OJT では、複数の事業部でビジネスの理解を深め、現場業務を体験します。現場社員とのコミュニケーションを通じて、エンジニアへの期待やその期待の裏側にある状況の一端を理解し、顧客志向を育みます。 開発 OJT では基礎研修で身につけた知識やスキルを実際の開発現場で活かすフェーズです。既存プロダクトの開発チームに加わり、実務としての開発を体験します。 成果発表会 研修の集大成として、これまでの学びや開発 OJT での取り組みを整理し発表してもらいました。 メンター制度 研修を支える仕組みとして、今年も一人ひとりにメンターをつける「メンター制度」を実施しました。研修本体での学びを補い、成長をより加速させることを目的としています。 研修期間中は毎日、日報を書いてもらっています。その日取り組んだことや学んだことを弊社の行動原則( Our Essentials )に基づき整理し、読み手を意識した文章にまとめることがルールです。日報はメンターが必ず目を通し、毎日フィードバックを行いました。やりとりの内容は、技術的な疑問点への対応だけでなく、課題への向き合い方や物事の捉え方といった長期的な成長につながる視点を与えることを意識してもらっています。短期的な知識の提供ではなく、将来にわたって役立つ考え方を養うことを重視しました。 さらに、週次では 1on1 を実施し、振り返りの場を設けました。目標や課題を言語化し、メドレーの行動規範の観点から自らの振る舞いを点検。翌週に試す具体的な一手へと落とし込むことで、実践的な成長サイクルを回しました。もちろん、真面目な内容だけでなく、砕けた相談も自由にできる場とし、安心して取り組める環境づくりも大切にしています。 日報に対するフィードバックの様子 研修の詳細 HC 研修、外部研修 HC 研修では、社会人に求められるビジネスマナーやキャリアマインド、ロジカルシンキング、コミュニケーション能力などのポータブルスキルを学びました。座学だけでなく、ワークショップを行うことでメドレーグループの他職種の新卒とも交流も行いました。最終日はチーム対抗の寸劇を行うことで、一人ひとりの発言力を高め、一体感を生み出すことができました。 外部研修では、講師が顧客役を演じたり、チームを企業に見立てた競争環境で取り組んだりと、実際の現場に近いハイプレッシャーな状況を体験しました。その中で、適切なコミュニケーションやチームワークの大切さを実感し、成果につなげるプロセスを学ぶことを目指しました。 基礎研修 基礎研修は、実際にプロダクト開発に携わっているメドレーのエンジニアが、教材づくりから講師までを担当します。単なる知識の習得にとどまらず、判断の背景や根拠を言語化し、他者に伝わる形で残すことまで含めて学ぶことを大切にしています。 全体を通して繰り返し伝えられていたことは、「課題を終わらせること自体を目的にしない」ということです。わからない点をそのままにせず、ドキュメント・実装・計測結果を往復しながら自分の言葉で理解し直す。この姿勢を身につけることが、研修を通じて最も大事にしている部分です。 ここでは主要な研修内容を紹介します。 TypeScript/ React この研修では、フロントエンド開発の基礎として TypeScript と React を扱いました。TypeScript の型システムなどの言語仕様を理解したうえで、React のコンポーネント設計、状態管理、フック などの基礎を学びます。 演習課題は Pull Request 形式で提出し、背景や選択肢、判断理由を文章にまとめたうえでコードレビューを受ける流れとしました。これにより、単に技術を学ぶだけでなく、技術を深く掘り下げる姿勢や、GitHub を使った開発フロー、レビューを通じたテキストコミュニケーションを実践的に身につけることを狙いとしています。 データベース(DB) 次に、データベースの基礎を幅広く学びました。テーブル設計や ER 図、リレーションの理解に加え、実際にクエリを書く演習として「クエリ 100 本ノック」に挑戦。WHERE や JOIN といった開発や分析で頻出する構文を身につけました。さらに、クエリ実行計画の読み方やインデックス設計など、パフォーマンスチューニングにも取り組みました。長期的な保守性や変更容易性、パフォーマンスを意識しながらデータベースを扱うための基礎を、実践的に学ぶ内容です。 アプリケーション開発実践 フロントエンドとバックエンドをつなげた実践的な開発演習として、Rails on Rails で API を実装し、React で SPA(Single Page Application)を構築する研修を行いました。フードデリバリーサービス(Uber Eats のようなアプリ)を題材に、決められた要件定義から設計、開発、テストまでを一気通貫で経験します。 ここでは、TypeScript/React 研修で身につけた基礎を応用しながら、実際にプロダクトとして動くアプリケーションを作り上げることに挑戦しました。仕様の整理からテスト設計までを通じて、フロントエンドとバックエンドを横断的に理解し、開発に必要な実践力を養うことが目的です。 Docker / AWS 最後に、インフラ領域の基礎として Docker と AWS を扱いました。メドレーでは AWS を中心に利用しているため、実際にアプリケーションを AWS 上にデプロイし、動作させるところまでを体験します。可用性やスケーラビリティを考慮したシステム構築に加え、ログ、監視、アラート設定といった運用面も含めて一通り設定。さらに簡易的に負荷をかけ、スケールアウトやアラートが正しく機能するかを検証しました。単に作るだけでなく、安定的に運用できる状態にすることの重要性を学ぶハンズオンとなっています。 事業部 OJT、開発 OJT 基礎研修を終えたあとは、現場社員から研修を受ける事業部 OJT に取り組みました。事業部によって取り組む内容は異なりますが、市場環境やミッション、今後の方向性を理解することから始まり、Sales や Customer support の業務の一部を体感します。 その後は、既存プロダクトの開発現場に加わり、実際の開発を体験する OJT に取り組みました。ここでは、抽象度の高い課題に対して自ら問題を整理し、解決に向けた手順を考え、周囲と協調しながら前に進める力が求められます。 開発の過程では、リリースに至るまでの一連のプロセスを経験します。わからないことを適切に言語化して周囲に伝え、協力を得ながら課題を乗り越えることも重要なポイントです。 今年の OJT で取り組まれていたタスクをいくつかピックアップしてみます。 デザインシステムの MCP サーバー構築 顧客向け管理画面の UI/UX 改善 電子カルテにおける書類作成機能の改善 患者向け通知機能の UX 改善 タスクを進める中では、先輩エンジニアから様々な観点でフィードバックを受けます。これまで自分になかった視点や考え方に触れることで、新卒メンバーは大きな学びを得ていた様子でした。 成果発表会 研修の締めくくりとして行ったのが成果発表会です。ここでは、研修や OJT を通じて学んだこと、自身の成長・変化を自分の言葉で整理し、周囲に伝える力を養うことを目的としています。単に振り返るだけでなく、学びを言語化して共有することで、個人の経験をチーム全体の資産へと昇華させる場でもあります。 当日は、CTO や VPoE、メンター、配属先のマネージャーなど、研修に関わった多くのメンバーが参加しました。 新卒の皆さんは緊張した面持ちでしたが、先輩たちから温かいフィードバックや応援の言葉が寄せられ、会場は和やかな雰囲気に包まれていました。 各 VP の皆様を始め、関わった方々からコメントいただきました まとめ 研修という限られた期間の中で、一人前のエンジニアとしての力をすべて身につけることは容易ではありません。だからこそ、この期間を通じて「これから成長していくための土台」を築いてもらうことを大切にしてきました。本番はここから始まります。 新卒の皆さんには、これからは仲間として実際の開発現場で活躍しながら、さらに経験を積み重ねていくことを期待しています。私たちのミッションである 「医療ヘルスケアの未来をつくる」 を実現するために、それぞれが力を発揮し、共に歩んでいけることを心から楽しみにしています。 We’re hiring! サマーインターン参加者募集中! メドレーでは、この夏に開催するエンジニア向けサマーインターンの参加者を募集しています。 実際のプロダクト開発を体験しながら、エンジニアとしての成長につながる機会を提供します。 「まずは話だけ聞いてみたい」「雰囲気を知りたい」という方に向けて、カジュアル面談も実施しています。少しでも興味をお持ちの方は、お気軽にご応募ください。 募集はこちら サマーインターン(エンジニア新卒) / 株式会社メドレー 株式会社メドレーはサマーインターン(エンジニア新卒)を採用しています。 open.talentio.com 中途採用も積極募集中! また、エンジニアをはじめとした中途採用も積極的に行っています。 ヘルスケアの未来を一緒につくる仲間を幅広く募集していますので、ぜひこちらもご覧ください。 中途採用の募集一覧はこちら 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
この記事は メドレー夏のブログリレー2025 5日目の記事です。 はじめに こんにちは!メドレーでDevRelをしている重田です。 突然ですが、転職先を探す時に実際に働く環境や雰囲気って気になりますよね? この記事では、メドレーのエンジニアが普段どんな環境で仕事をしているのかを写真メインでご紹介します! 「メドレーのオフィスで働きたい!」「働くイメージが持てた!」と思っていただけたら嬉しいです🙌 では、早速オフィスツアースタートです📣 🏢 私たちが働くオフィス 私たちのオフィスは日比谷線 六本木駅直結の六本木ヒルズ森タワーの12階と13階にあります。 詳細は以下記事でご紹介しています🙋♀️ メドレーオフィス(13階)へのアクセスと入館方法のご案内 [六本木ヒルズ森タワー]メドレーのオフィスを初披露 🏙️外観 六本木ヒルズ森タワーは54F建て、高さ238メートルの超高層オフィスビルです。 上を見上げる高さで、たまに見ると「おお〜〜都会だ!」となり背筋が伸びます。笑 🏥13階 エンジニアの中でも医療プラットフォームのプロダクト開発エンジニアやコーポレートITのメンバーが13階で働いています。 エントランス(受付) 13階のエントランスは清潔感のある白の壁に温かみのある木目調の床です。 憩いスペース 約100席と、広々したスペースです。テーブル席の他、ソファもあるのでゆったりと過ごせます。 カウンターには賞状などが飾られています。 🏥12階 エンジニアの中でも人材プラットフォームのプロダクト開発エンジニアが12階で働いています。 憩いスペース こちらは今年の春に増設されたスペースです! ランチだけではなく、1on1やチームミーティングでも大活躍のスペースです。 ボックス席にはディスプレイが付いており、チームミーティングの際に役立ちます。 ゆっくりしたいときはこの席がおすすめです🍵 東京タワーを眺めて仕事ができるのは六本木、そして12階に位置する特権です🗼 憩いスペース入口には弊社プロダクトを体験できるコーナーがあります。 また、自動販売機2台・コーヒー販売機2台・食品自動販売機1台・冷蔵庫が完備されています☕️ 小腹が減った時の救世主🙏コンビニに行くのが面倒な時に大活躍です! 持参/購入したお弁当を温められるのも嬉しいポイント💡 🏥共通 会議室 会議室は50室以上あり、来客専用・社内専用の会議室が分かれています。 ✨メドレーのカルチャー「クリーンデスクポリシー」に込められた想い メドレーが大切にしているカルチャーのひとつに「クリーンデスクポリシー」があります。これはデスクの上には仕事道具以外置かない、帰宅時にはモニタ・キーボード・マウス以外は全て片付けるというルールのことです。 「クリーンデスクポリシー」は代表の瀧口が 「佐藤可士和の超整理術」 という本から影響を受け、創業時から大切にしているカルチャーです。なぜ「クリーンデスクポリシー」が大切なのか、瀧口が社内で共有している資料から引用してご紹介します。 整理整頓には、視点が必要です。複雑なものであれば、それぞれの因果関係を見抜き、何が大切なのかの優先順位をつけることが必要です。「頭で理解しているつもりのこと」と「身体に染み付いて実践できること」には大きな隔たりがあります。実践への近道は、たった一つの象徴的なことを徹底することではないでしょうか。 例えば、この記事 「日本電産が赤字会社を速攻で再生できたワケ」 の「組織体質を変える一番の早道は」を読んでみてください。整理・整頓・清潔・躾(自主的な)を徹底して、営業回数を増やせば成功すると書いています。 そもそも仕事机は、仕事をするためのものです。クリーンデスクというのは整理整頓ができる組織であることの象徴です。このような背景で、クリーンデスクポリシーは僕にとって、大切にしたい考え方なのです。 実際、社員の机は常に整理されています。 また、過去にも「クリーンデスクポリシー」に触れているのでぜひご覧いただけると嬉しいです! [六本木ヒルズ森タワー]メドレーのオフィスを初披露 📚スキルアップ支援やライフステージに合わせた柔軟な働き方を実現 この記事では主にエンジニアの制度をご紹介します! 支援制度 社内勉強会支援 社内で勉強会を開催する際の飲食補助が出ます! 書籍購入の補助 会社の費用で購入可! ※資産管理の観点から電子書籍は除く 資格取得支援 AWS認定試験・Ruby技術認定試験を会社の費用で受験可! ※一定条件あり カンファレンス参加費用の補助 RubyKaigi などの外部カンファレンスへ会社の費用で参加可! 制度を利用する際は、以下のようにSlackで申請をしています。 希望PC・モニター・アーロンチェアの貸与 エンジニアの方には希望PCとモニターが貸与されます。 基本的にはスペックも自由に選べます🙆♀️ 湾曲型モニター また、希望者にはアーロンチェアも用意しています。 アーロンチェア 働く環境 よくある質問をもとにご紹介します! Q. 勤務時間は決まっていますか? A. 開発職では裁量労働制を採用しており、多くの社員は事業部の稼働時間に合わせて10:00~19:00で勤務しています。 一方で、子育て中の社員については、勤務時間中に一時的に離席するなど、個々の状況に応じた柔軟な体制をとっています。 Q. 出社とリモートの頻度はどのぐらいですか? A. エンジニア/デザイナーの場合は週2出社、週3リモートをベースにしています。 こちらも勤務時間と同様、体調やご家庭の事情など、柔軟に対応できる体制になっています。 Q. キャリアロールはどのようになっていますか? A. 「スペシャリスト」と「マネジメント」の2つに大別されます。事業責任者のロールも存在しますが、役職ではなく、一つのロールとして位置づけています。 Q. 担当するプロダクトは決まっていますか? A. エージェントからのご紹介時・スカウトをお送りする際などにプロダクトを限定している場合もありますが、基本的には選考を通じて皆様のご希望や志向、スキルスタックなども合わせてご提案・ご相談しながら決めています。 イベントでも大活躍のオフィス メドレーでは定期的にイベントを開催したり、オフィスを他社様や外部コミュニティのイベント会場として提供したりしています。 こちらは5月にリンケージ社とヘンリー社と開催したイベント、 HealthTech Meetup の様子です。 Roppongi.rb 、 Omotesando.rb の会場としても定期的にご利用いただいています🙌 🍀最後に 最後までご覧いただきありがとうございました! メドレーで働くイメージはつきましたでしょうか!? 直近では、以下のイベントを開催予定です! 少しでもご興味のある方は、ぜひイベントを機にオフィスにいらしてください!ご参加お待ちしております✨ 9/4(木)19:30 Omotesando.rb#113 9/5(金)19:00 【有料プランユーザー限定×オフライン】MagicPodユーザーミートアップ We’re hiring 最後まで読んでいただきありがとうございます! メドレーでは、一緒に働く仲間を大募集中です👫 少しでも興味を持っていただけましたら、ぜひカジュアル面談でお待ちしています! 🤝 募集一覧 🗣️ カジュアル面談 Medley Summer Tech Blog Relay 6 日目は、医療プラットフォーム本部の安東(Andō)さんの記事です! それでは良い週末をお過ごしください!
こんにちは!メドレーでDevRelをしている重田です。 今年も暑い日が続いていますがいかがお過ごしですか? メドレーでは夏企画として『MEDLEY Summer Tech Blog Relay』と題して、ブログリレーを開催します! 8/25(月)〜9/26(金)まで毎日異なるメンバーが技術やエンジニアリング、個人開発など幅広いテーマでテックブログを公開していきます! 本記事にて毎日ブログを追記更新していくので、ぜひお楽しみください✨ ※土日祝を除く ブログリレーカレンダー 🗓️第1週(8/25~8/29) Day1:SRE屋のひとりごと(玉井) Day2:AIで実現する10x時代の組織学習型QA(小島) Day3:Lambda@Edgeを使った画像リサイズ配信の構築記録(森川) Day4:Design→Codeの現実解を考える(中村) Day5:メドレーエンジニアの働く環境をご紹介(重田) 🗓️第2週(9/1~9/5) Day6:データアナリストの分析プロセスにおけるAI活用(安東) Day7:データの何かについて(山邊) Day8:事業部=>QAにジョブチェンジしてみた話(内堀) Day9:OpenSearchについて(仮)(山下) Day10:WACATE2025夏に参加した話(井津) 🗓️第3週(9/8~9/12) Day11:エンジニア組織におけるAI活用状況とモニタリング(仮)(倉林) Day12:デザイナーは何考えながらデザイン作ってるのか書きます(仮)(近藤) Day13:TBD(稲村) Day14:医療PFマーケティングアセットについて書きます(進) Day15:DRの話 or 何か(小泉) 🗓️第4週(9/16~9/19) Day16:TBD(山田) Day17:GraphQL ruby読んでみた(仮)(川原) Day18:歯科向け電子レセプトビューアを作ってみた(平林) Day19:Playwright * デザインシステムMCPを使って楽したい (仮)(池田) 🗓️第5週(9/22~9/26) Day20:生成AIを使用してバグを効率的に解消した話(仮)(山下) Day21:CLINICS負荷試験の話かSLO運用について話します(山田) Day22:生成AI時代に向けて、開発効率10xを支えるリリース戦略の見直し(桶谷・小島) Day23: TBD(前田) 🍉We’re hiring! メドレーでは、「医療ヘルスケアの未来をつくる」仲間を大募集しています! 少しでも興味をお持ちいただけましたら、ぜひ、カジュアル面談にお越しください🙌 メドレーで働く | 株式会社メドレー メドレーの組織文化や募集要項をご紹介します www.medley.jp Medley Engineer Entrance Book この度は株式会社メドレーに興味をお寄せいただきありがとうございます。本資料は、メドレーへの転職をご検討いただいている皆様に、当社をより深くご理解いただくために作成いたしました。 medley-inc.notion.site
はじめに こんにちは! 医療プラットフォーム本部 プラットフォーム開発室 SRE グループの山田です。 医療機関向け SaaS である CLINICS の安定稼働とシステム信頼性の向上に取り組んでいます。 メドレーは 7 月 11 日、12 日に TOC 有明 (東京都江東区)で開催された SRE NEXT 2025 に LOGO Sponsor として協賛しました! SRE NEXT は、信頼性に関するプラクティスに深い関心を持つエンジニアのためのカンファレンスです。 医療プラットフォーム本部 SRE グループは発足して間もないため、他社のさまざまな挑戦や SRE プラクティスを学ぶべく、私を含め数名のエンジニアが参加し、たくさんの方々と交流させていただきました。 本レポートでは、SRE NEXT 2025 の会場や企業ブースの様子、そして発表の内容についてご紹介します。 会場の様子 SRE NEXT 2025 は、オンラインとオフラインのハイブリッド形式で開催されました。 50 を超える企業の協賛のもと、740 名もの SRE エンジニアが現地に参加しました。 広々とした会場 企業ブースも多数出展しており、SRE にまつわるアンケートや SRE プラクティスの紹介など非常に面白かったです。 株式会社タイミー様のブースにお邪魔させていただきました 2 日目の最後には懇親会も催され、様々な SRE エンジニアの方と交流することができました。 懇親会の様子 発表の様子 どのセッションも大変興味深かったのですが、特に印象深かった下記のセッションについてご紹介します。 Day1: SRE 不在の開発チームが障害対応と 向き合った 100 日間 Day2: 伴走から自律へ:形式知へと導く SRE イネーブリングによる、プロダクトチームの信頼性オーナーシップ向上 Day2: Four Keys から始める信頼性の改善 SRE 不在の開発チームが障害対応と 向き合った 100 日間 (Loglass 勝丸真さん) 引用元: speakerdeck.com カスタマーサクセスチームからのフィードバックをきっかけに、インシデント対応の改善に取り組んだ際の課題と、その解決策が紹介されました。 開発チームが障害対応に直面する中で、エンジニアによって対応品質にばらつきがあり、カスタマーサクセスチームから「障害対応がスムーズに進まない」「全体の体制や連絡手段が曖昧」といったフィードバックを受けるという課題があったとのことです。特に、あるエンジニアは単純な修正作業のみを行う一方で、別のエンジニアは影響範囲の特定やカスタマーサクセスへの回避策伝達まで含めた包括的な対応を実施するなど、対応者によって大きな差が生じていました。 そこで、専任のインシデントコマンダーチームを編成し、全エンジニアによるローテーション制から専門化による品質向上を図るアプローチへ切り替えたことが説明されました。また、障害対応フローの明確化とシンプル化、インシデントレベルの再定義、外部ツール「Warroom」の導入による自動記録・AI 要約機能の活用についても具体的な手法が紹介されました。 さらに、プロジェクトの推進においてはプロセス整備だけでなく、ビジネスチームやプロダクトチームとの継続的な対話を重視し、「なぜこの変更が必要なのか」という背景を丁寧に共有することで組織全体の理解を深め、技術的なベストプラクティスとビジネス要求のギャップを埋めていく文化づくりの重要性も解説されていました。 所感 このセッションで特に印象的だったのは、教科書的なベストプラクティスをそのまま適用するのではなく、現場の実情に合わせて大胆な割り切りを行っていた点です。中でも、「全エンジニアがインシデントコマンダーになる」という理想を一旦捨て、あえて属人化を許容して専門チームを作るという判断は、実践が進んでいるからこその現実的な選択だと感じました。 CLINICS SRE でも、理想的なインシデント対応体制を構築しようとする際に、全エンジニアのスキルレベルやモチベーションのばらつきという現実的な課題に直面することがあります。この発表から学んだのは、完璧な体制を目指すよりも、まずは実効性のある仕組みを作り上げることの重要性です。専門チームによる安定した対応基盤があってこそ、その後の全体的なスキル向上や体制の民主化が可能になるのだと理解しました。 また、プロセス整備だけでは解決できない人間関係や組織文化の課題に対し、継続的な対話を通じて理解を深めていく姿勢も非常に参考になりました。CLINICS でも、開発チームや事業部との間で、障害対応時の連携や認識に齟齬が生じることは少なくありません。この発表から学んだ対話を重ねることが重要であるという点は、今後の SRE の取り組みに活かしていきたいと思います。 現場の泥臭い課題に真摯に向き合い、組織として解決策を模索する姿勢は、同じような課題に取り組む SRE チームにとって大きな学びとなる発表でした。 伴走から自律へ:形式知へと導く SRE イネーブリングによる、プロダクトチームの信頼性オーナーシップ向上 (ビズリーチ 佐々木康徳さん) 引用元: speakerdeck.com このセッションでは、WAF 運用をプロダクトチームへイネーブリングするにあたり、SECI モデルに基づいて実践された事例が紹介されました。 具体的には、共同化、表出化、連結化、内面化の 4 つのプロセスを通じて、SRE が持つ暗黙知を段階的にプロダクトチームに移転し、チームのオーナーシップを醸成する具体的な方法が示されました。 この取り組みにより、WAF 運用をプロダクトチームへ移管できただけでなく、プロダクト開発チームのオーナーシップ向上やコミュニケーションの効率化といった効果も得られたと説明されていました。 所感 このセッションで、SECI モデルという概念を初めて知りました。 私自身、暗黙知から他者への暗黙知のプロセスを飛ばしていきなりドキュメント化(表出化)から始めたり、連結化まで進めずに終わってしまったりと、SECI モデルの観点から見ると不完全な取り組みでイネーブリングに失敗した経験があります。そのため、今回の発表は非常に納得感があり、共感を深く覚えました。 今後、CLINICS SRE でも SECI モデルを意識した開発チームへのイネーブリングを進めていきたいと考えています。 Four Keys から始める信頼性の改善 (DMM 尾崎耕太さん) 引用元: speakerdeck.com このセッションは、Four Keys (チーム生産性を可視化することを目的とした指標のこと) を軸に DevOps 文化を作り、その結果としてユーザにとって魅力的で信頼されるプロダクトを提供することを目指す戦略を取ることで、信頼性の改善を進めたという事例と導入効果について紹介されました。 信頼性指標である SLI/SLO やエラーバジェットは、機能開発チームにとって理解しにくく、日々の開発サイクルに組み込みにくいという課題がありました。そこで、より開発プロセスに近く、チームがオーナーシップを持ちやすい Four Keys を採用。これを軸に DevOps 文化を醸成することで、段階的に信頼性を獲得するというアプローチをとったと説明されています。 その結果、Four Keys の数値が改善しただけでなく、プロダクト開発チームが自律的に変化に取り組むようになったという大きな効果があったと紹介されていました。 所感 最も印象的だったのは、「指標の改善が目的ではなく、その先の状態が目標」という考え方です。指標を取るのは気づきを得て、アクションに繋げるためのものであるため、「指標を改善すること」にこだわりすぎない方が良いという話には深く共感できました。数値の向上に注力するあまり、なぜその指標を見ているのかという根本的な目的を見失ってしまうことは、実際の現場でもよく起こりがちな問題です。 CLINICS でも SLO 運用を行っていますが、この考え方は非常に参考になりました。SLO の数値を改善することに集中してしまい、そもそもなぜ SLO を設定しているのか、SLO 運用を通じて何を実現したいのかという本質的な目的を見失わないよう気をつけたいと思います。 SLO 運用をした先に SRE チームが何を目指しているのか、サービスの信頼性向上によってどのような価値をユーザーや事業に提供したいのかという意識を、開発チームにもさらに伝えられるようにしたいと感じました。 また、開発チームがオーナーシップを持って変化に取り組んでいることが重要という点も非常に納得できました。具体的な成果よりも、開発チーム自身が主体的に改善活動を推進していることの方が本質的な価値があるという考え方は、持続可能な改善文化を構築する上で欠かせない要素だと思います。 実践面で特に参考になったのは、単純に Four Keys を導入するのではなく、プロダクト開発チームと対話してアクションに繋げられるように Keys を詳細化している点です。開発者が「なぜ悪化したのか」「どの活動が効果的だったのか」を理解できるよう工夫している取り組みは、実際の現場での指標活用を考える上で非常に実用的なアプローチだと感じました。 さいごに 他社のさまざまな挑戦や SRE プラクティスを学べただけでなく、たくさんの方々と交流することができて刺激的な 2 日間でした。 来年も 7 月 10 日、11 日に TOC 有明 での開催を予定しているそうです。 メドレーは今後も SRE NEXT だけでなく、他の技術イベントやコミュニティの発展を積極的に支援し、参加、貢献していきます。 運営スタッフの皆さん、登壇者の皆さん、一緒に盛り上がった参加者の皆さん、本当にありがとうございました! メドレーではエンジニアを積極採用中です! エンジニアを積極採用中です メドレーでは、「医療ヘルスケアの未来」を共に創っていく SRE エンジニアを積極的に採用しています。 興味を持たれた方は、以下のリンクより、ぜひカジュアル面談の応募をお願いします。 募集の一覧 https://www.medley.jp/jobs/ ※カジュアル面談ご希望の際は、<その他> にてその旨をご記載ください
こんにちは。医療プラットフォーム本部の日下( @mkusaka )です。 私の所属する統合基盤チームでは、医療プラットフォームの複数のシステムを支えるサービス群を運用しています。 これまではユーザーへの影響を最小限に抑えるため、リリース作業を深夜や早朝に限定していましたが、その結果として運用チームへの負担増やリリースタイミングの制約といった課題が生じていました。 こうした課題を解決し、日中でも安全かつ段階的にリリースを行うため、統合基盤に Blue/Green デプロイメントを導入しました。今回はその詳細を紹介します。 統合基盤コンポーネントの紹介 統合基盤チームでは、患者と医療機関の双方に使われる医療システムの根幹を支える重要な基盤の開発・運用を行っています。 管理するコンポーネントは、「医療機関向け」と「患者向け」の 2 つに分類できます。 「医療機関向け」機能は、 Pharms 、 Dentis 、 CLINICS などの各サービス間でコミュニケーションのハブとして機能し、イベントの配信を行います。 一方、「患者向け」機能としては、 総合医療アプリ CLINICS から送られたリクエストを適切なサービスに振り分けるゲートウェイの役割を担っています。 このようなアーキテクチャを採用した背景には、医療プラットフォームが複数のプロダクト(Pharms、Dentis、CLINICS など)を統合的に運用する必要があるという背景があります。 医療機関向けには、いずれか一方のシステムの状態に他方が引きずられない構成とすることで、各医療機関の業務システムの可用性を最大限高めるという狙いがあります。 一方、患者向けには、複数の医療機関向けシステムに対して共通のアプリからアクセスできるようにするため、患者情報の一元管理や統一された認証基盤の整備が不可欠です。これにより、患者にとってシームレスな体験を実現し、より効率的で質の高い医療サービスの提供を目指しています。 つまり、統合基盤が管理するこれらのコンポーネントに障害が発生すると、サービス間にまたがる業務だけでなく、患者向けのサービス提供にも影響が及ぶため、高い可用性が求められています。 Blue/Green デプロイメント導入の目的 統合基盤の各サービスは、AWS ECS(Elastic Container Service)上で運用されています。これまでは、運用のシンプルさからローリングデプロイを採用していました。しかしこの手法では、デプロイが開始されると新バージョンが一斉に展開されてしまうため、問題発生時の影響範囲が広く、迅速なロールバックも難しいという課題がありました。 こうした課題への対策として、Blue/Green デプロイメントを導入することを決定しました。 Blue/Green デプロイメントとは、新旧 2 つの環境(Blue と Green)を用意し、新バージョンを片方に展開した後、徐々にトラフィックを移行していく方法です。これにより、致命的な問題やパフォーマンスの劣化を早期に発見し、迅速なロールバックが可能になります。 Blue/Green デプロイメントの要件と独自実装の選択 システムを Blue/Green デプロイする際に、大きく分けて 2 つの考慮事項がありました。 1 つ目は 2 つの明確な検証フェーズを設けることです。 致命的なエラー検出フェーズ :新環境に約 10%のトラフィックを流し、システムの安定性を確認。 負荷時のパフォーマンス検証フェーズ :新環境に約 50%のトラフィックを流し、負荷によるパフォーマンスの劣化を確認。 これら 2 つのフェーズを明確に設け、十分な検証時間を取ることで、より高い自信をもってリリース作業を進めることが可能になると考えました。 2 つ目は非同期処理を担当する Worker サービスを新旧の環境それぞれで用意することです。 Worker サービスでは、ジョブが SQS(Simple Queue Service)キューに投入されてから実際に処理されるまでタイムラグがあります。その間に新しい環境へ切り替えが行われると、旧環境のジョブが新環境の Worker によって処理され、データの不整合が起きる可能性があります。このリスクを防ぐため、Blue と Green の環境それぞれに独立した SQS キューを用意し、それぞれのジョブが確実に自環境の Worker で処理を完了する形式を取ることとしました。 ECS には CodeDeploy を利用した標準的な Blue/Green の段階的デプロイ戦略として、Canary デプロイ(一部のトラフィックで検証後、一気に切り替え)や Linear デプロイ(一定割合ずつ徐々に適用)が提供されています。しかし、私たちが求めるようなトラフィック調整にはこれらの手法が十分に適しておらず、また SQS を分離するための Blue 環境/Green 環境どちらかを判定するような仕組みが整備されていないようだったので、内製化することとしました。 採用した構成 自前の Blue/Green デプロイを実現するため、以下の構成を採用しました。 ALB(Application Load Balancer)の weighted target groups を利用して、トラフィックを柔軟に調整しています。 ECS 環境は Blue/Green のそれぞれが並行稼働し、SQS は各環境ごとに独立したキューを設けます。 新バージョンは Green 環境として起動し、初期状態では Blue 環境(現行バージョン)がすべてのトラフィックを処理します。その後、Green 環境へのトラフィックを 10%、50%と段階的に増やしながら、各段階でメトリクスの監視を行います。 10%の段階で致命的なエラーがないことを確認し、50%の段階で負荷時のパフォーマンスに問題ないと判断されたら、最終的にすべてのトラフィックを Green 環境へ切り替えます(100%)。 また、各種ステップは承認操作やロールバックも含めて CI 上で完結するように整備を行うことで、デプロイの複雑性も抑えています。 実装サンプル Terraform と AWS CLI を組み合わせて実装しています。 ALB の weighted routing 設定(Terraform) resource "aws_lb_listener_rule" "weighted_routing" { listener_arn = aws_lb_listener. https . arn action { type = "forward" forward { target_group { arn = aws_lb_target_group. app_blue . arn weight = 100 # 初期状態では Blue が 100% } target_group { arn = aws_lb_target_group. app_green . arn weight = 0 # Green は 0% } } } # AWS CLI で weight を動的に調整するため、Terraform での変更を無視 lifecycle { ignore_changes = [ action ] } } トラフィック配分の変更(AWS CLI) デプロイ時のトラフィック配分は、AWS CLI を使用して動的に変更します。実際の運用では、Green の weight を指定すると Blue が自動的に 100 - Green になるように調整しています: # Green の weight を指定して Blue/Green の配分を設定 # 例:GREEN_WEIGHT=10 の場合、Blue=90、Green=10 に自動計算 set-weight: @aws elbv2 modify-rule \ --rule-arn $( LISTENER_RULE_ARN ) \ --actions '[{ "Type": "forward", "ForwardConfig": { "TargetGroups": [ { "TargetGroupArn": "$(BLUE_TG_ARN)", "Weight": ' $$ (( 100 - $( GREEN_WEIGHT ))) ' }, { "TargetGroupArn": "$(GREEN_TG_ARN)", "Weight": ' $( GREEN_WEIGHT ) ' } ] } }]' デプロイフロー 実際のデプロイでは、以下のような手順で段階的にトラフィックを移行します: # 1. Green 環境へのデプロイ make app-green.deploy TAG= $( NEW_VERSION ) # 2. トラフィックを段階的に移行 make set-weight GREEN_WEIGHT= 10 # 10% を Green へ make set-weight GREEN_WEIGHT= 50 # 50% を Green へ make set-weight GREEN_WEIGHT= 100 # 100% を Green へ(切り替え完了) # 3. 問題が発生した場合のロールバック make set-weight GREEN_WEIGHT= 0 # すべてのトラフィックを Blue へ戻す この実装により、段階的なリリースと問題発生時の迅速なロールバックが可能になっています。 導入後の効果 Blue/Green デプロイメント導入後、深夜のリリース作業がなくなり、運用チームの負担が大幅に軽減されました。また日中であってもリスクを最小限に抑えたリリースが実現でき、問題が発生した場合にも迅速にロールバックできるようになったため、システムの安定性を犠牲にすることなくリリースを行えるようになりました。 まとめ 今回は、医療システム統合基盤における Blue/Green デプロイメントの導入事例を紹介しました。 医療システムには高い可用性が求められる中、従来のローリングデプロイでは段階的なリリースや迅速なロールバックが困難で、深夜作業が必須となっていました。 Blue/Green デプロイメントの導入により、以下を実現しました: ALB の weighted target groups を活用した段階的トラフィック移行(10%→50%→100%)により品質を確認しながらのリリース進行 Worker サービス用の SQS キューを環境ごとに分離し、データ不整合を防止 深夜作業を廃止し、日中でも安全にリリース作業を実施可能に 今後も医療システムに求められる高可用性を維持しながら、開発効率の向上を目指し、継続的な改善を進めていきます。 We’re hiring 統合基盤チームでは、医療システムの根幹を支える重要な基盤の開発・運用を行っています。 今回紹介したような Blue/Green デプロイメントの実装をはじめ、高可用性が求められるシステムにおいて、技術的な課題に向き合いながら医療現場と患者体験の向上に貢献できる環境があります。 医療の未来をエンジニアリングで支えることに興味がある方のご応募をお待ちしています。 患者統合基盤 Webエンジニア / 株式会社メドレー 株式会社メドレーは患者統合基盤 Webエンジニアを採用しています。 open.talentio.com 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
こんにちは。医療プラットフォーム本部の日下( @mkusaka )です。 私の所属する統合基盤チームでは、医療プラットフォームの複数のシステムを支えるサービス群を運用しています。 これまではユーザーへの影響を最小限に抑えるため、リリース作業を深夜や早朝に限定していましたが、その結果として運用チームへの負担増やリリースタイミングの制約といった課題が生じていました。 こうした課題を解決し、日中でも安全かつ段階的にリリースを行うため、統合基盤に Blue/Green デプロイメントを導入しました。今回はその詳細を紹介します。 統合基盤コンポーネントの紹介 統合基盤チームでは、患者と医療機関の双方に使われる医療システムの根幹を支える重要な基盤の開発・運用を行っています。 管理するコンポーネントは、「医療機関向け」と「患者向け」の 2 つに分類できます。 「医療機関向け」機能は、 Pharms 、 Dentis 、 CLINICS などの各サービス間でコミュニケーションのハブとして機能し、イベントの配信を行います。 一方、「患者向け」機能としては、 総合医療アプリ CLINICS から送られたリクエストを適切なサービスに振り分けるゲートウェイの役割を担っています。 このようなアーキテクチャを採用した背景には、医療プラットフォームが複数のプロダクト(Pharms、Dentis、CLINICS など)を統合的に運用する必要があるという背景があります。 医療機関向けには、いずれか一方のシステムの状態に他方が引きずられない構成とすることで、各医療機関の業務システムの可用性を最大限高めるという狙いがあります。 一方、患者向けには、複数の医療機関向けシステムに対して共通のアプリからアクセスできるようにするため、患者情報の一元管理や統一された認証基盤の整備が不可欠です。これにより、患者にとってシームレスな体験を実現し、より効率的で質の高い医療サービスの提供を目指しています。 つまり、統合基盤が管理するこれらのコンポーネントに障害が発生すると、サービス間にまたがる業務だけでなく、患者向けのサービス提供にも影響が及ぶため、高い可用性が求められています。 Blue/Green デプロイメント導入の目的 統合基盤の各サービスは、AWS ECS(Elastic Container Service)上で運用されています。これまでは、運用のシンプルさからローリングデプロイを採用していました。しかしこの手法では、デプロイが開始されると新バージョンが一斉に展開されてしまうため、問題発生時の影響範囲が広く、迅速なロールバックも難しいという課題がありました。 こうした課題への対策として、Blue/Green デプロイメントを導入することを決定しました。 Blue/Green デプロイメントとは、新旧 2 つの環境(Blue と Green)を用意し、新バージョンを片方に展開した後、徐々にトラフィックを移行していく方法です。これにより、致命的な問題やパフォーマンスの劣化を早期に発見し、迅速なロールバックが可能になります。 Blue/Green デプロイメントの要件と独自実装の選択 システムを Blue/Green デプロイする際に、大きく分けて 2 つの考慮事項がありました。 1 つ目は 2 つの明確な検証フェーズを設けることです。 致命的なエラー検出フェーズ :新環境に約 10%のトラフィックを流し、システムの安定性を確認。 負荷時のパフォーマンス検証フェーズ :新環境に約 50%のトラフィックを流し、負荷によるパフォーマンスの劣化を確認。 これら 2 つのフェーズを明確に設け、十分な検証時間を取ることで、より高い自信をもってリリース作業を進めることが可能になると考えました。 2 つ目は非同期処理を担当する Worker サービスを新旧の環境それぞれで用意することです。 Worker サービスでは、ジョブが SQS(Simple Queue Service)キューに投入されてから実際に処理されるまでタイムラグがあります。その間に新しい環境へ切り替えが行われると、旧環境のジョブが新環境の Worker によって処理され、データの不整合が起きる可能性があります。このリスクを防ぐため、Blue と Green の環境それぞれに独立した SQS キューを用意し、それぞれのジョブが確実に自環境の Worker で処理を完了する形式を取ることとしました。 ECS には CodeDeploy を利用した標準的な Blue/Green の段階的デプロイ戦略として、Canary デプロイ(一部のトラフィックで検証後、一気に切り替え)や Linear デプロイ(一定割合ずつ徐々に適用)が提供されています。しかし、私たちが求めるようなトラフィック調整にはこれらの手法が十分に適しておらず、また SQS を分離するための Blue 環境/Green 環境どちらかを判定するような仕組みが整備されていないようだったので、内製化することとしました。 採用した構成 自前の Blue/Green デプロイを実現するため、以下の構成を採用しました。 ALB(Application Load Balancer)の weighted target groups を利用して、トラフィックを柔軟に調整しています。 ECS 環境は Blue/Green のそれぞれが並行稼働し、SQS は各環境ごとに独立したキューを設けます。 新バージョンは Green 環境として起動し、初期状態では Blue 環境(現行バージョン)がすべてのトラフィックを処理します。その後、Green 環境へのトラフィックを 10%、50%と段階的に増やしながら、各段階でメトリクスの監視を行います。 10%の段階で致命的なエラーがないことを確認し、50%の段階で負荷時のパフォーマンスに問題ないと判断されたら、最終的にすべてのトラフィックを Green 環境へ切り替えます(100%)。 また、各種ステップは承認操作やロールバックも含めて CI 上で完結するように整備を行うことで、デプロイの複雑性も抑えています。 実装サンプル Terraform と AWS CLI を組み合わせて実装しています。 ALB の weighted routing 設定(Terraform) resource "aws_lb_listener_rule" "weighted_routing" { listener_arn = aws_lb_listener. https . arn action { type = "forward" forward { target_group { arn = aws_lb_target_group. app_blue . arn weight = 100 # 初期状態では Blue が 100% } target_group { arn = aws_lb_target_group. app_green . arn weight = 0 # Green は 0% } } } # AWS CLI で weight を動的に調整するため、Terraform での変更を無視 lifecycle { ignore_changes = [ action ] } } トラフィック配分の変更(AWS CLI) デプロイ時のトラフィック配分は、AWS CLI を使用して動的に変更します。実際の運用では、Green の weight を指定すると Blue が自動的に 100 - Green になるように調整しています: # Green の weight を指定して Blue/Green の配分を設定 # 例:GREEN_WEIGHT=10 の場合、Blue=90、Green=10 に自動計算 set-weight: @aws elbv2 modify-rule \ --rule-arn $( LISTENER_RULE_ARN ) \ --actions '[{ "Type": "forward", "ForwardConfig": { "TargetGroups": [ { "TargetGroupArn": "$(BLUE_TG_ARN)", "Weight": ' $$ (( 100 - $( GREEN_WEIGHT ))) ' }, { "TargetGroupArn": "$(GREEN_TG_ARN)", "Weight": ' $( GREEN_WEIGHT ) ' } ] } }]' デプロイフロー 実際のデプロイでは、以下のような手順で段階的にトラフィックを移行します: # 1. Green 環境へのデプロイ make app-green.deploy TAG= $( NEW_VERSION ) # 2. トラフィックを段階的に移行 make set-weight GREEN_WEIGHT= 10 # 10% を Green へ make set-weight GREEN_WEIGHT= 50 # 50% を Green へ make set-weight GREEN_WEIGHT= 100 # 100% を Green へ(切り替え完了) # 3. 問題が発生した場合のロールバック make set-weight GREEN_WEIGHT= 0 # すべてのトラフィックを Blue へ戻す この実装により、段階的なリリースと問題発生時の迅速なロールバックが可能になっています。 導入後の効果 Blue/Green デプロイメント導入後、深夜のリリース作業がなくなり、運用チームの負担が大幅に軽減されました。また日中であってもリスクを最小限に抑えたリリースが実現でき、問題が発生した場合にも迅速にロールバックできるようになったため、システムの安定性を犠牲にすることなくリリースを行えるようになりました。 まとめ 今回は、医療システム統合基盤における Blue/Green デプロイメントの導入事例を紹介しました。 医療システムには高い可用性が求められる中、従来のローリングデプロイでは段階的なリリースや迅速なロールバックが困難で、深夜作業が必須となっていました。 Blue/Green デプロイメントの導入により、以下を実現しました: ALB の weighted target groups を活用した段階的トラフィック移行(10%→50%→100%)により品質を確認しながらのリリース進行 Worker サービス用の SQS キューを環境ごとに分離し、データ不整合を防止 深夜作業を廃止し、日中でも安全にリリース作業を実施可能に 今後も医療システムに求められる高可用性を維持しながら、開発効率の向上を目指し、継続的な改善を進めていきます。 We’re hiring 統合基盤チームでは、医療システムの根幹を支える重要な基盤の開発・運用を行っています。 今回紹介したような Blue/Green デプロイメントの実装をはじめ、高可用性が求められるシステムにおいて、技術的な課題に向き合いながら医療現場と患者体験の向上に貢献できる環境があります。 医療の未来をエンジニアリングで支えることに興味がある方のご応募をお待ちしています。 患者統合基盤 Webエンジニア / 株式会社メドレー 株式会社メドレーは患者統合基盤 Webエンジニアを採用しています。 open.talentio.com 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに こんにちは! 人材プラットフォーム本部プロダクト統括部プロダクト開発部アカデミー開発グループ所属の城間(シロマ)です。 私は 2024 年 4 月に新卒エンジニアとして入社し、現在はオンライン動画研修サービス「 ジョブメドレーアカデミー 」の新規プロダクト開発に携わっています。 先日、5 月 23 日、24 日に東京都千代田区のベルサール神田にて開催された TSKaigi 2025 にメドレーは Silver Sponsor として協賛しました! TypeScript をテーマにしたこの大規模なカンファレンスには、多様なバックグラウンドを持つ TypeScript エンジニアが全国から集結。 惜しくもブースの抽選には外れてしまいましたが、私も含め弊社からは数名のエンジニアが現地参加し、多くの素晴らしい出会いと学びがありました。 今回は、当日の会場の様子や印象に残ったセッション、そして今、私が感じていることを中心にご紹介します! 会場の熱気と「型にとらわれない」エンジニアたちの交流 今年の TSKaigi 2025 は 2 日間とも天候に恵まれ、 61 もの企業スポンサーと「TypeScript を扱う型にとらわれないエンジニア」が集結し、会場は終日、熱気と活気に満ち溢れていました。 場内の様子 スポンサーボード 2 日間に渡り、合計 3 会場でセッションや LT が盛んに行われ、時には立ち見の人が出るほどに盛況していました。 参加者同士でテーマに対してトークする OST(Open Space Technology) や、夜の豪華な懇親会では、セッションでは聞けないような深掘りした技術の話や、各社の開発文化に関する交流が盛んに行われ、当日はおよそ 50 名以上のエンジニアの方々と色々なお話をさせていただきました。 OST のテーマ 懇親会の様子 TypeScript ケーキ また、今回参加者に配布された公式グッズは、TSKaigi のオリジナル T シャツやトートバッグに加え、各企業が工夫を凝らした遊び心あふれるグッズで盛りだくさんでした。 特に、今回ブース出展のあった 19 社全てを周るスタンプラリー では、全てのスタンプを集めることで豪華景品が当たる抽選に参加でき、会場の賑わいに一役買っていました。 公式ノベルティ(一部) スタンプラリー 印象に残ったセッション:AI エージェント全盛の時代と TypeScript の可能性 どのセッションも非常に興味深く、時には理解が追いつかないほど最先端の内容でしたが、特に今の私が行う開発と照らし合わせて印象に残ったセッションをいくつかご紹介します。 Day1: AI Coding Agent Enablement in TypeScript 引用元: AI Coding Agents Enablement in TypeScript - Speaker Deck 概要 このセッションでは、人間による介入を最小限に抑え、AI エージェントが大規模な作業をどのように「自走」して実行させるかについて深く掘り下げられました。 なぜ自走が難しいのか?その主な要因は AI エージェントが「任意の TypeScript」のようなあまりにも広い「解空間」で動くため、精度が低くなってしまうためです。 すなわち AI エージェントの精度を高めるためには 「可能な限り解空間を絞る」 ことが基本方針であり、会社やプロジェクト固有の規約、ドメイン知識、デザインなどに基づいて適切に制約を与える重要性が何度も強調されていました。 その解空間を絞る具体的なアプローチとして 「コンテキスト注入」 と 「機械的検査とフィードバック」 の 2 つが挙げられました。 コンテキスト注入 : ドキュメント、規約、ドメイン知識などを LLM にインプットし、「解空間の定義」を与えること。特に TypeScript コード自体をドキュメントとして育てることが、コンテキスト注入に役立つ。 機械的検査とフィードバック : LLM が生成したコードが定義された解空間から外れていないか、型チェック、Linter、自動テストといった古典的な手法で検査し、NG の場合はエージェントにフィードバックして解空間へと押し戻すように促す。Linter は次にやるべきアクションを提示しやすく、決定的である点が非常に有効である。 また、TypeScript 開発における型の役割についても興味深い見解がありました。現状、型によってコード生成の精度が上がるという定量的な根拠はまだないものの、「解空間に押し戻す」ためのエージェントへの入力(フィードバック)としては役立つ可能性は大いにあるとのこと。AI はまだ高度な型解決が苦手なので、人間がドメインモデルや関数のシグネチャ(型定義)をしっかり書き、その後の実装をエージェントに任せるアプローチが有効であるという話は、非常に現実的で納得感がありました。 最後に、 「エコシステムの未来 - Speed is King」 という言葉が印象的でした。これは、AI エージェントのコード生成が加速すると、開発プロセス全体の速度がボトルネックとなることを示唆しています。具体的には、人間がコードを 30 分かけて書く時代には 1 分程度の静的解析は許容範囲でしたが、AI が同じコードをわずか 30 秒で書くようになると同じ 1 分間の静的解析は AI の作業時間に対して相対的に非常に長く、無視できないボトルネックになるということです。さらにクラウド型エージェントではチャットごとにコンテナが作成される仕組みが主流であり、その際にパッケージマネージャの速度がボトルネックになります。また、AI エージェントによるコード生産量が爆発的に増加すれば、デプロイの機会も増えるためビルド(バンドラー)の速度もボトルネックとして顕在化し、加えて将来的に LLM がコードを生成するタイミング(デコーディング時)ごとに型制約を守ろうとする「制約付きデコーディング」のようなアプローチが進めば、型チェッカーがボトルネックになる可能性も指摘されていました。故に、ツールチェイン全体の高速化が今後より重要になるという示唆は、将来的な開発環境を考える上で非常に重要な視点だと感じました。 Day2: TS 特化 Cline プログラミング 引用元: tskaigi.mizchi.workers.dev 概要 このセッションでは、LLM を活用した「Cline Agentic Coding」の全般について、特に TypeScript に特化した実践的なプロンプトのコツが紹介されました。 まず冒頭に うまくいくうまくいくプロンプトのコツ として、以下が挙げられていました。 書きすぎない(再現性ある範囲で詠唱破棄) 執拗に出力例を例示する 両立条件の矛盾を避ける 規模感に合わせて厳しくする また特に強調されていたのは、 テスト駆動開発(Test Driven Development) の重要性です。コード生成時に対応するユニットテストを常に生成し、コード修正時にはテストがパスすることを確認することで、AI エージェントの自己修復能力を高め、放置しても完成する高品質なコードに繋がるとのことでした。 その他にも、次のような実践的なプロンプトの一例が紹介されていました。 コメントによる自己記述 : 各ファイルの冒頭にコメントで仕様を記述することで、再修正時のコード解釈の一貫性を保つ In Source Testing : 実装と同じファイルにユニットテストを書くことで、コメント・実装・テストを三位一体で管理する types.ts にドメイン型を集約 : 中規模以上のプロジェクトでは src/types.ts にドメインモデルを集約し、SSoT(Single Source of Truth)とすることで、ファイル間の整合性を保ち read_file の頻度を減らす TS + 関数型ドメインモデリング : 状態の発散を抑えるために class を使わず関数による実装を優先し、代数的データ型でドメインをモデリングする ファイル配置規則の明記 : モノレポなどのファイル配置規則を明記することで、タスクごとのエージェントの推測コストを減らす 詳細指示を docs/*.md に分割 : 大規模プロジェクトでは無関係な指示によるノイズを減らすために、詳細な指示をドキュメントファイルに分割し、必要に応じて参照させる カバレッジに基づくテストの自動生成 : vitest でカバレッジを計測し、最もカバレッジが上がるテストコードを AI に考察・追加させる 機械的なマイグレーション : lodash の削除や類似 API を持つライブラリへの置き換えなど、面倒なマイグレーション作業を AI に自動化させる URL を読む能力 : URL の内容を読み込ませ、要約・保存させることで、Deep Research 的な挙動を可能にする 一方で、 うまくいかないプロンプト のパターンもいくつか示唆されました。 型だけで設計しようとする : ほぼ確実に無視され、抽象的な設計能力は期待できない 非同期例外処理が下手 : 思考停止気味に try-catch で握り潰しがちで、大規模開発で破綻の原因となる 環境構築が下手 : ゼロショットでの環境構築は発散しやすく、手数が仇となり環境を破壊する可能性がある モジュールインターフェースが発散 : 実装次第で全て export してしまい、モジュール間の契約が肥大化・破綻する 「ある」のがよくない(チェーホフの銃の法則) : 無関係なリソースを読み込ませると、それを使うことに固執し、大規模コードではノイズになる デバッグログを食いすぎる : 自身が生成したプリントデバッグでコンテキストウィンドウを消費し、デバッグコードを放置しがちである 結論として、現状の LLM はコーディングが下手であり、低品質なコードで設計が破綻し自滅する傾向があるとの見解でした。特に、リファクタリングの指針がなく、不要コードの判定やモジュール視点での API 設計が苦手で、ユーザー側でリファクタリングしても元の低品質なコードに書き戻すこともあるという少し厳しい評価が下されていました。 しかし TypeScript と LLM の組み合わせには良い点も多く、GitHub の公開コードが豊富で学習量が多いこと、安全性よりも表現力を選んだピーキーな型システムが自然言語と対応した型のモデリングをしやすいこと、豊富な静的解析手段とユーザー層の厚さがあることなどが挙げられていました。 現時点でのベストプラクティスとしては、PoC/プロトタイプのコード生成(1 ファイル完結 800 行以内が目安)に活用し、人間によるインターフェース設計、失敗パターンのプロンプトへの反映、成功するパターンのドキュメント化、そしてこちらでも Lint ルールの整備についての言及がありました。 そして最後にテスト駆動開発や LLM の得意領域・発達段階を予測する技術、プロンプトエンジニアリングの重要性の増加など Agentic Coding によるプログラミング自体の変質と不変である点について説明され締めくくられました。 感想 これらのセッションを通して、私が携わる新規プロダクト開発において、 実践できている部分 と 改善の余地がある部分 が明確になりました。 実践できている点として、全社的に積極的に利用している AI エディタ「Cursor」のプロジェクトルールや自立型 AI エージェント「Devin」の knowledge には、プロジェクトごとに解空間を明確にするコンテキストを与えています。これは、「AI Coding Agent Enablement in TypeScript」で述べられていた「コンテキスト注入」と「解空間を絞る」という考え方に合致していると感じました。また、コードと仕様を記述したドキュメントを同じリポジトリ内で管理し、開発を進めている点も、AI へのコンテキスト注入に貢献していると言えるでしょう。 開発フローにおいても、AI の特性を踏まえた工夫を凝らしています。「AI Coding Agent Enablement in TypeScript」で言及があった「3 回くらいループした末に any とかキャストで誤魔化しがち」という点に対し、例えば API 開発では、まず仕様やテストケースのドキュメントをコードベースと同じファイル内に記述し、次に Request や Response Body を zod を用いて厳密に型を記述します。これらをエンジニアがレビューした後、テストデータのセットアップや記法などをエンジニアが整え、残りを AI で実装を進めます。さらにアプリケーションコードの実装においても、用意したユニットテストが通過するという制約の元 AI に実装させることにより、型解決などに AI エージェントが費やす時間が最小化され、 実装フェーズの開発工数を大幅に短縮することに成功 しています。 mizchi さんのセッションで言及のあったテスト駆動開発(Test Driven Development)のスキルは今後もより重要になっていくでしょう。しかし、もう少し先の未来では、弊社メドレーが大切にしている価値観(Our Essential)の一つである「 ドキュメントドリブン 」に倣い、 ドキュメントドリブン開発(Document Driven Development) のスキルがより重要になっていくのではないか、と私は考えています。 具体的なイメージ さらに「Speed is King」の思想を体現するために、Linter と Formatter には Biome、パッケージマネージャーには Bun を採用するなどツールチェインの速度にはかなりシビアな意思決定をしており、より AI エージェントが生産的に活動できるような体制を整えています。 一方で、 改善の余地がある点 も明確になりました。特に双方のセッションで強調されていた「機械的検査とフィードバック」の継続的な改善、すなわち Linter については開発初期から現在に至るまであまり積極的に更新できていないと痛感しています。AI に与えるフィードバックをその場での単発のもので終わらせるのではなく、(人間と同じように)再発防止策を考えさせるように Linter を定期的にアップデートし、より決定的で高速に AI エージェントが機能するような環境をより一層整備していきます。 さいごに TypeScript の可能性を肌で感じ、熱意ある仲間たちと出会い、技術への情熱を改めて確認できた素晴らしいイベントでした。 メドレーは今後も TSKaigi だけでなく、他の技術イベントやコミュニティの発展を積極的に支援し、参加、貢献していきます。 過去にスポンサーとして協賛した技術カンファレンスの参加レポート記事はこちら! RubyKaigi 2025 参加レポート - Platinum Sponsor として協賛しました! | MEDLEY Developer Portal はじめに こんにちは! 人材プラットフォーム本部プロダクト開発室 第一開発グループ所属の山下です。 メドレーには今年2月に入社したエンジニアで、日本最大級の医療介護求人サイト ジョブメドレー の開発を担当しています。 メドレーは 4 月 1... developer.medley.jp pmconf 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。医療プラットフォーム本部で Product Manager をしている佐藤です。2024 年 5 月にメドレーにジョインし、医療機関向けプロダクト開発に奔走しています。社内では ”papa”、家では”おじさん”と呼ばれ可愛がら... developer.medley.jp JSConf JP 2024に プレミアムスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは!人材プラットフォーム本部で技術広報兼エンジニア採用をしている重田(@Shige0096)です。2024 年 11 月にメドレーにジョインし、初の社外イベントに参加してきました。 今回、メドレーは 2024/11/23 に九段坂上... developer.medley.jp DroidKaigi 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。人材プラットフォーム本部でエンジニアをしている山河です。2023 年 4 月に新卒として入社し、徐々に業務の幅を広げています! さて、メドレーは 2024/9/11 〜 9/13 の 3 日間にベルサール渋谷ガーデンにて開催され... developer.medley.jp TSKaigi 2025 の運営スタッフの皆さん、登壇者の皆さん、一緒に盛り上がった参加者の皆さん、本当にありがとうございました! メドレーではエンジニアを積極採用中です! メドレーでは領域を問わず、TypeScript を積極的に活用して医療ヘルスケアの未来をつくるプロダクトを開発しています。 TypeScript を活用した医療ヘルスケア領域の課題解決に興味がある方は、ぜひお気軽にご連絡ください! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp ※カジュアル面談ご希望の際は、<その他> にてその旨をご記載ください
はじめに こんにちは! 人材プラットフォーム本部プロダクト統括部プロダクト開発部アカデミー開発グループ所属の城間(シロマ)です。 私は 2024 年 4 月に新卒エンジニアとして入社し、現在はオンライン動画研修サービス「 ジョブメドレーアカデミー 」の新規プロダクト開発に携わっています。 先日、5 月 23 日、24 日に東京都千代田区のベルサール神田にて開催された TSKaigi 2025 にメドレーは Silver Sponsor として協賛しました! TypeScript をテーマにしたこの大規模なカンファレンスには、多様なバックグラウンドを持つ TypeScript エンジニアが全国から集結。 惜しくもブースの抽選には外れてしまいましたが、私も含め弊社からは数名のエンジニアが現地参加し、多くの素晴らしい出会いと学びがありました。 今回は、当日の会場の様子や印象に残ったセッション、そして今、私が感じていることを中心にご紹介します! 会場の熱気と「型にとらわれない」エンジニアたちの交流 今年の TSKaigi 2025 は 2 日間とも天候に恵まれ、 61 もの企業スポンサーと「TypeScript を扱う型にとらわれないエンジニア」が集結し、会場は終日、熱気と活気に満ち溢れていました。 場内の様子 スポンサーボード 2 日間に渡り、合計 3 会場でセッションや LT が盛んに行われ、時には立ち見の人が出るほどに盛況していました。 参加者同士でテーマに対してトークする OST(Open Space Technology) や、夜の豪華な懇親会では、セッションでは聞けないような深掘りした技術の話や、各社の開発文化に関する交流が盛んに行われ、当日はおよそ 50 名以上のエンジニアの方々と色々なお話をさせていただきました。 OST のテーマ 懇親会の様子 TypeScript ケーキ また、今回参加者に配布された公式グッズは、TSKaigi のオリジナル T シャツやトートバッグに加え、各企業が工夫を凝らした遊び心あふれるグッズで盛りだくさんでした。 特に、今回ブース出展のあった 19 社全てを周るスタンプラリー では、全てのスタンプを集めることで豪華景品が当たる抽選に参加でき、会場の賑わいに一役買っていました。 公式ノベルティ(一部) スタンプラリー 印象に残ったセッション:AI エージェント全盛の時代と TypeScript の可能性 どのセッションも非常に興味深く、時には理解が追いつかないほど最先端の内容でしたが、特に今の私が行う開発と照らし合わせて印象に残ったセッションをいくつかご紹介します。 Day1: AI Coding Agent Enablement in TypeScript 引用元: AI Coding Agents Enablement in TypeScript - Speaker Deck 概要 このセッションでは、人間による介入を最小限に抑え、AI エージェントが大規模な作業をどのように「自走」して実行させるかについて深く掘り下げられました。 なぜ自走が難しいのか?その主な要因は AI エージェントが「任意の TypeScript」のようなあまりにも広い「解空間」で動くため、精度が低くなってしまうためです。 すなわち AI エージェントの精度を高めるためには 「可能な限り解空間を絞る」 ことが基本方針であり、会社やプロジェクト固有の規約、ドメイン知識、デザインなどに基づいて適切に制約を与える重要性が何度も強調されていました。 その解空間を絞る具体的なアプローチとして 「コンテキスト注入」 と 「機械的検査とフィードバック」 の 2 つが挙げられました。 コンテキスト注入 : ドキュメント、規約、ドメイン知識などを LLM にインプットし、「解空間の定義」を与えること。特に TypeScript コード自体をドキュメントとして育てることが、コンテキスト注入に役立つ。 機械的検査とフィードバック : LLM が生成したコードが定義された解空間から外れていないか、型チェック、Linter、自動テストといった古典的な手法で検査し、NG の場合はエージェントにフィードバックして解空間へと押し戻すように促す。Linter は次にやるべきアクションを提示しやすく、決定的である点が非常に有効である。 また、TypeScript 開発における型の役割についても興味深い見解がありました。現状、型によってコード生成の精度が上がるという定量的な根拠はまだないものの、「解空間に押し戻す」ためのエージェントへの入力(フィードバック)としては役立つ可能性は大いにあるとのこと。AI はまだ高度な型解決が苦手なので、人間がドメインモデルや関数のシグネチャ(型定義)をしっかり書き、その後の実装をエージェントに任せるアプローチが有効であるという話は、非常に現実的で納得感がありました。 最後に、 「エコシステムの未来 - Speed is King」 という言葉が印象的でした。これは、AI エージェントのコード生成が加速すると、開発プロセス全体の速度がボトルネックとなることを示唆しています。具体的には、人間がコードを 30 分かけて書く時代には 1 分程度の静的解析は許容範囲でしたが、AI が同じコードをわずか 30 秒で書くようになると同じ 1 分間の静的解析は AI の作業時間に対して相対的に非常に長く、無視できないボトルネックになるということです。さらにクラウド型エージェントではチャットごとにコンテナが作成される仕組みが主流であり、その際にパッケージマネージャの速度がボトルネックになります。また、AI エージェントによるコード生産量が爆発的に増加すれば、デプロイの機会も増えるためビルド(バンドラー)の速度もボトルネックとして顕在化し、加えて将来的に LLM がコードを生成するタイミング(デコーディング時)ごとに型制約を守ろうとする「制約付きデコーディング」のようなアプローチが進めば、型チェッカーがボトルネックになる可能性も指摘されていました。故に、ツールチェイン全体の高速化が今後より重要になるという示唆は、将来的な開発環境を考える上で非常に重要な視点だと感じました。 Day2: TS 特化 Cline プログラミング 引用元: tskaigi.mizchi.workers.dev 概要 このセッションでは、LLM を活用した「Cline Agentic Coding」の全般について、特に TypeScript に特化した実践的なプロンプトのコツが紹介されました。 まず冒頭に うまくいくうまくいくプロンプトのコツ として、以下が挙げられていました。 書きすぎない(再現性ある範囲で詠唱破棄) 執拗に出力例を例示する 両立条件の矛盾を避ける 規模感に合わせて厳しくする また特に強調されていたのは、 テスト駆動開発(Test Driven Development) の重要性です。コード生成時に対応するユニットテストを常に生成し、コード修正時にはテストがパスすることを確認することで、AI エージェントの自己修復能力を高め、放置しても完成する高品質なコードに繋がるとのことでした。 その他にも、次のような実践的なプロンプトの一例が紹介されていました。 コメントによる自己記述 : 各ファイルの冒頭にコメントで仕様を記述することで、再修正時のコード解釈の一貫性を保つ In Source Testing : 実装と同じファイルにユニットテストを書くことで、コメント・実装・テストを三位一体で管理する types.ts にドメイン型を集約 : 中規模以上のプロジェクトでは src/types.ts にドメインモデルを集約し、SSoT(Single Source of Truth)とすることで、ファイル間の整合性を保ち read_file の頻度を減らす TS + 関数型ドメインモデリング : 状態の発散を抑えるために class を使わず関数による実装を優先し、代数的データ型でドメインをモデリングする ファイル配置規則の明記 : モノレポなどのファイル配置規則を明記することで、タスクごとのエージェントの推測コストを減らす 詳細指示を docs/*.md に分割 : 大規模プロジェクトでは無関係な指示によるノイズを減らすために、詳細な指示をドキュメントファイルに分割し、必要に応じて参照させる カバレッジに基づくテストの自動生成 : vitest でカバレッジを計測し、最もカバレッジが上がるテストコードを AI に考察・追加させる 機械的なマイグレーション : lodash の削除や類似 API を持つライブラリへの置き換えなど、面倒なマイグレーション作業を AI に自動化させる URL を読む能力 : URL の内容を読み込ませ、要約・保存させることで、Deep Research 的な挙動を可能にする 一方で、 うまくいかないプロンプト のパターンもいくつか示唆されました。 型だけで設計しようとする : ほぼ確実に無視され、抽象的な設計能力は期待できない 非同期例外処理が下手 : 思考停止気味に try-catch で握り潰しがちで、大規模開発で破綻の原因となる 環境構築が下手 : ゼロショットでの環境構築は発散しやすく、手数が仇となり環境を破壊する可能性がある モジュールインターフェースが発散 : 実装次第で全て export してしまい、モジュール間の契約が肥大化・破綻する 「ある」のがよくない(チェーホフの銃の法則) : 無関係なリソースを読み込ませると、それを使うことに固執し、大規模コードではノイズになる デバッグログを食いすぎる : 自身が生成したプリントデバッグでコンテキストウィンドウを消費し、デバッグコードを放置しがちである 結論として、現状の LLM はコーディングが下手であり、低品質なコードで設計が破綻し自滅する傾向があるとの見解でした。特に、リファクタリングの指針がなく、不要コードの判定やモジュール視点での API 設計が苦手で、ユーザー側でリファクタリングしても元の低品質なコードに書き戻すこともあるという少し厳しい評価が下されていました。 しかし TypeScript と LLM の組み合わせには良い点も多く、GitHub の公開コードが豊富で学習量が多いこと、安全性よりも表現力を選んだピーキーな型システムが自然言語と対応した型のモデリングをしやすいこと、豊富な静的解析手段とユーザー層の厚さがあることなどが挙げられていました。 現時点でのベストプラクティスとしては、PoC/プロトタイプのコード生成(1 ファイル完結 800 行以内が目安)に活用し、人間によるインターフェース設計、失敗パターンのプロンプトへの反映、成功するパターンのドキュメント化、そしてこちらでも Lint ルールの整備についての言及がありました。 そして最後にテスト駆動開発や LLM の得意領域・発達段階を予測する技術、プロンプトエンジニアリングの重要性の増加など Agentic Coding によるプログラミング自体の変質と不変である点について説明され締めくくられました。 感想 これらのセッションを通して、私が携わる新規プロダクト開発において、 実践できている部分 と 改善の余地がある部分 が明確になりました。 実践できている点として、全社的に積極的に利用している AI エディタ「Cursor」のプロジェクトルールや自立型 AI エージェント「Devin」の knowledge には、プロジェクトごとに解空間を明確にするコンテキストを与えています。これは、「AI Coding Agent Enablement in TypeScript」で述べられていた「コンテキスト注入」と「解空間を絞る」という考え方に合致していると感じました。また、コードと仕様を記述したドキュメントを同じリポジトリ内で管理し、開発を進めている点も、AI へのコンテキスト注入に貢献していると言えるでしょう。 開発フローにおいても、AI の特性を踏まえた工夫を凝らしています。「AI Coding Agent Enablement in TypeScript」で言及があった「3 回くらいループした末に any とかキャストで誤魔化しがち」という点に対し、例えば API 開発では、まず仕様やテストケースのドキュメントをコードベースと同じファイル内に記述し、次に Request や Response Body を zod を用いて厳密に型を記述します。これらをエンジニアがレビューした後、テストデータのセットアップや記法などをエンジニアが整え、残りを AI で実装を進めます。さらにアプリケーションコードの実装においても、用意したユニットテストが通過するという制約の元 AI に実装させることにより、型解決などに AI エージェントが費やす時間が最小化され、 実装フェーズの開発工数を大幅に短縮することに成功 しています。 mizchi さんのセッションで言及のあったテスト駆動開発(Test Driven Development)のスキルは今後もより重要になっていくでしょう。しかし、もう少し先の未来では、弊社メドレーが大切にしている価値観(Our Essential)の一つである「 ドキュメントドリブン 」に倣い、 ドキュメントドリブン開発(Document Driven Development) のスキルがより重要になっていくのではないか、と私は考えています。 具体的なイメージ さらに「Speed is King」の思想を体現するために、Linter と Formatter には Biome、パッケージマネージャーには Bun を採用するなどツールチェインの速度にはかなりシビアな意思決定をしており、より AI エージェントが生産的に活動できるような体制を整えています。 一方で、 改善の余地がある点 も明確になりました。特に双方のセッションで強調されていた「機械的検査とフィードバック」の継続的な改善、すなわち Linter については開発初期から現在に至るまであまり積極的に更新できていないと痛感しています。AI に与えるフィードバックをその場での単発のもので終わらせるのではなく、(人間と同じように)再発防止策を考えさせるように Linter を定期的にアップデートし、より決定的で高速に AI エージェントが機能するような環境をより一層整備していきます。 さいごに TypeScript の可能性を肌で感じ、熱意ある仲間たちと出会い、技術への情熱を改めて確認できた素晴らしいイベントでした。 メドレーは今後も TSKaigi だけでなく、他の技術イベントやコミュニティの発展を積極的に支援し、参加、貢献していきます。 過去にスポンサーとして協賛した技術カンファレンスの参加レポート記事はこちら! RubyKaigi 2025 参加レポート - Platinum Sponsor として協賛しました! | MEDLEY Developer Portal はじめに こんにちは! 人材プラットフォーム本部プロダクト開発室 第一開発グループ所属の山下です。 メドレーには今年2月に入社したエンジニアで、日本最大級の医療介護求人サイト ジョブメドレー の開発を担当しています。 メドレーは 4 月 1... developer.medley.jp pmconf 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。医療プラットフォーム本部で Product Manager をしている佐藤です。2024 年 5 月にメドレーにジョインし、医療機関向けプロダクト開発に奔走しています。社内では ”papa”、家では”おじさん”と呼ばれ可愛がら... developer.medley.jp JSConf JP 2024に プレミアムスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは!人材プラットフォーム本部で技術広報兼エンジニア採用をしている重田(@Shige0096)です。2024 年 11 月にメドレーにジョインし、初の社外イベントに参加してきました。 今回、メドレーは 2024/11/23 に九段坂上... developer.medley.jp DroidKaigi 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。人材プラットフォーム本部でエンジニアをしている山河です。2023 年 4 月に新卒として入社し、徐々に業務の幅を広げています! さて、メドレーは 2024/9/11 〜 9/13 の 3 日間にベルサール渋谷ガーデンにて開催され... developer.medley.jp TSKaigi 2025 の運営スタッフの皆さん、登壇者の皆さん、一緒に盛り上がった参加者の皆さん、本当にありがとうございました! メドレーではエンジニアを積極採用中です! メドレーでは領域を問わず、TypeScript を積極的に活用して医療ヘルスケアの未来をつくるプロダクトを開発しています。 TypeScript を活用した医療ヘルスケア領域の課題解決に興味がある方は、ぜひお気軽にご連絡ください! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp ※カジュアル面談ご希望の際は、<その他> にてその旨をご記載ください
はじめに こんにちは! 人材プラットフォーム本部プロダクト統括部プロダクト開発部アカデミー開発グループ所属の城間(シロマ)です。 私は 2024 年 4 月に新卒エンジニアとして入社し、現在はオンライン動画研修サービス「 ジョブメドレーアカデミー 」の新規プロダクト開発に携わっています。 先日、5 月 23 日、24 日に東京都千代田区のベルサール神田にて開催された TSKaigi 2025 にメドレーは Silver Sponsor として協賛しました! TypeScript をテーマにしたこの大規模なカンファレンスには、多様なバックグラウンドを持つ TypeScript エンジニアが全国から集結。 惜しくもブースの抽選には外れてしまいましたが、私も含め弊社からは数名のエンジニアが現地参加し、多くの素晴らしい出会いと学びがありました。 今回は、当日の会場の様子や印象に残ったセッション、そして今、私が感じていることを中心にご紹介します! 会場の熱気と「型にとらわれない」エンジニアたちの交流 今年の TSKaigi 2025 は 2 日間とも天候に恵まれ、 61 もの企業スポンサーと「TypeScript を扱う型にとらわれないエンジニア」が集結し、会場は終日、熱気と活気に満ち溢れていました。 場内の様子 スポンサーボード 2 日間に渡り、合計 3 会場でセッションや LT が盛んに行われ、時には立ち見の人が出るほどに盛況していました。 参加者同士でテーマに対してトークする OST(Open Space Technology) や、夜の豪華な懇親会では、セッションでは聞けないような深掘りした技術の話や、各社の開発文化に関する交流が盛んに行われ、当日はおよそ 50 名以上のエンジニアの方々と色々なお話をさせていただきました。 OST のテーマ 懇親会の様子 TypeScript ケーキ また、今回参加者に配布された公式グッズは、TSKaigi のオリジナル T シャツやトートバッグに加え、各企業が工夫を凝らした遊び心あふれるグッズで盛りだくさんでした。 特に、今回ブース出展のあった 19 社全てを周るスタンプラリー では、全てのスタンプを集めることで豪華景品が当たる抽選に参加でき、会場の賑わいに一役買っていました。 公式ノベルティ(一部) スタンプラリー 印象に残ったセッション:AI エージェント全盛の時代と TypeScript の可能性 どのセッションも非常に興味深く、時には理解が追いつかないほど最先端の内容でしたが、特に今の私が行う開発と照らし合わせて印象に残ったセッションをいくつかご紹介します。 Day1: AI Coding Agent Enablement in TypeScript 引用元: AI Coding Agents Enablement in TypeScript - Speaker Deck 概要 このセッションでは、人間による介入を最小限に抑え、AI エージェントが大規模な作業をどのように「自走」して実行させるかについて深く掘り下げられました。 なぜ自走が難しいのか?その主な要因は AI エージェントが「任意の TypeScript」のようなあまりにも広い「解空間」で動くため、精度が低くなってしまうためです。 すなわち AI エージェントの精度を高めるためには 「可能な限り解空間を絞る」 ことが基本方針であり、会社やプロジェクト固有の規約、ドメイン知識、デザインなどに基づいて適切に制約を与える重要性が何度も強調されていました。 その解空間を絞る具体的なアプローチとして 「コンテキスト注入」 と 「機械的検査とフィードバック」 の 2 つが挙げられました。 コンテキスト注入 : ドキュメント、規約、ドメイン知識などを LLM にインプットし、「解空間の定義」を与えること。特に TypeScript コード自体をドキュメントとして育てることが、コンテキスト注入に役立つ。 機械的検査とフィードバック : LLM が生成したコードが定義された解空間から外れていないか、型チェック、Linter、自動テストといった古典的な手法で検査し、NG の場合はエージェントにフィードバックして解空間へと押し戻すように促す。Linter は次にやるべきアクションを提示しやすく、決定的である点が非常に有効である。 また、TypeScript 開発における型の役割についても興味深い見解がありました。現状、型によってコード生成の精度が上がるという定量的な根拠はまだないものの、「解空間に押し戻す」ためのエージェントへの入力(フィードバック)としては役立つ可能性は大いにあるとのこと。AI はまだ高度な型解決が苦手なので、人間がドメインモデルや関数のシグネチャ(型定義)をしっかり書き、その後の実装をエージェントに任せるアプローチが有効であるという話は、非常に現実的で納得感がありました。 最後に、 「エコシステムの未来 - Speed is King」 という言葉が印象的でした。これは、AI エージェントのコード生成が加速すると、開発プロセス全体の速度がボトルネックとなることを示唆しています。具体的には、人間がコードを 30 分かけて書く時代には 1 分程度の静的解析は許容範囲でしたが、AI が同じコードをわずか 30 秒で書くようになると同じ 1 分間の静的解析は AI の作業時間に対して相対的に非常に長く、無視できないボトルネックになるということです。さらにクラウド型エージェントではチャットごとにコンテナが作成される仕組みが主流であり、その際にパッケージマネージャの速度がボトルネックになります。また、AI エージェントによるコード生産量が爆発的に増加すれば、デプロイの機会も増えるためビルド(バンドラー)の速度もボトルネックとして顕在化し、加えて将来的に LLM がコードを生成するタイミング(デコーディング時)ごとに型制約を守ろうとする「制約付きデコーディング」のようなアプローチが進めば、型チェッカーがボトルネックになる可能性も指摘されていました。故に、ツールチェイン全体の高速化が今後より重要になるという示唆は、将来的な開発環境を考える上で非常に重要な視点だと感じました。 Day2: TS 特化 Cline プログラミング 引用元: tskaigi.mizchi.workers.dev 概要 このセッションでは、LLM を活用した「Cline Agentic Coding」の全般について、特に TypeScript に特化した実践的なプロンプトのコツが紹介されました。 まず冒頭に うまくいくうまくいくプロンプトのコツ として、以下が挙げられていました。 書きすぎない(再現性ある範囲で詠唱破棄) 執拗に出力例を例示する 両立条件の矛盾を避ける 規模感に合わせて厳しくする また特に強調されていたのは、 テスト駆動開発(Test Driven Development) の重要性です。コード生成時に対応するユニットテストを常に生成し、コード修正時にはテストがパスすることを確認することで、AI エージェントの自己修復能力を高め、放置しても完成する高品質なコードに繋がるとのことでした。 その他にも、次のような実践的なプロンプトの一例が紹介されていました。 コメントによる自己記述 : 各ファイルの冒頭にコメントで仕様を記述することで、再修正時のコード解釈の一貫性を保つ In Source Testing : 実装と同じファイルにユニットテストを書くことで、コメント・実装・テストを三位一体で管理する types.ts にドメイン型を集約 : 中規模以上のプロジェクトでは src/types.ts にドメインモデルを集約し、SSoT(Single Source of Truth)とすることで、ファイル間の整合性を保ち read_file の頻度を減らす TS + 関数型ドメインモデリング : 状態の発散を抑えるために class を使わず関数による実装を優先し、代数的データ型でドメインをモデリングする ファイル配置規則の明記 : モノレポなどのファイル配置規則を明記することで、タスクごとのエージェントの推測コストを減らす 詳細指示を docs/*.md に分割 : 大規模プロジェクトでは無関係な指示によるノイズを減らすために、詳細な指示をドキュメントファイルに分割し、必要に応じて参照させる カバレッジに基づくテストの自動生成 : vitest でカバレッジを計測し、最もカバレッジが上がるテストコードを AI に考察・追加させる 機械的なマイグレーション : lodash の削除や類似 API を持つライブラリへの置き換えなど、面倒なマイグレーション作業を AI に自動化させる URL を読む能力 : URL の内容を読み込ませ、要約・保存させることで、Deep Research 的な挙動を可能にする 一方で、 うまくいかないプロンプト のパターンもいくつか示唆されました。 型だけで設計しようとする : ほぼ確実に無視され、抽象的な設計能力は期待できない 非同期例外処理が下手 : 思考停止気味に try-catch で握り潰しがちで、大規模開発で破綻の原因となる 環境構築が下手 : ゼロショットでの環境構築は発散しやすく、手数が仇となり環境を破壊する可能性がある モジュールインターフェースが発散 : 実装次第で全て export してしまい、モジュール間の契約が肥大化・破綻する 「ある」のがよくない(チェーホフの銃の法則) : 無関係なリソースを読み込ませると、それを使うことに固執し、大規模コードではノイズになる デバッグログを食いすぎる : 自身が生成したプリントデバッグでコンテキストウィンドウを消費し、デバッグコードを放置しがちである 結論として、現状の LLM はコーディングが下手であり、低品質なコードで設計が破綻し自滅する傾向があるとの見解でした。特に、リファクタリングの指針がなく、不要コードの判定やモジュール視点での API 設計が苦手で、ユーザー側でリファクタリングしても元の低品質なコードに書き戻すこともあるという少し厳しい評価が下されていました。 しかし TypeScript と LLM の組み合わせには良い点も多く、GitHub の公開コードが豊富で学習量が多いこと、安全性よりも表現力を選んだピーキーな型システムが自然言語と対応した型のモデリングをしやすいこと、豊富な静的解析手段とユーザー層の厚さがあることなどが挙げられていました。 現時点でのベストプラクティスとしては、PoC/プロトタイプのコード生成(1 ファイル完結 800 行以内が目安)に活用し、人間によるインターフェース設計、失敗パターンのプロンプトへの反映、成功するパターンのドキュメント化、そしてこちらでも Lint ルールの整備についての言及がありました。 そして最後にテスト駆動開発や LLM の得意領域・発達段階を予測する技術、プロンプトエンジニアリングの重要性の増加など Agentic Coding によるプログラミング自体の変質と不変である点について説明され締めくくられました。 感想 これらのセッションを通して、私が携わる新規プロダクト開発において、 実践できている部分 と 改善の余地がある部分 が明確になりました。 実践できている点として、全社的に積極的に利用している AI エディタ「Cursor」のプロジェクトルールや自立型 AI エージェント「Devin」の knowledge には、プロジェクトごとに解空間を明確にするコンテキストを与えています。これは、「AI Coding Agent Enablement in TypeScript」で述べられていた「コンテキスト注入」と「解空間を絞る」という考え方に合致していると感じました。また、コードと仕様を記述したドキュメントを同じリポジトリ内で管理し、開発を進めている点も、AI へのコンテキスト注入に貢献していると言えるでしょう。 開発フローにおいても、AI の特性を踏まえた工夫を凝らしています。「AI Coding Agent Enablement in TypeScript」で言及があった「3 回くらいループした末に any とかキャストで誤魔化しがち」という点に対し、例えば API 開発では、まず仕様やテストケースのドキュメントをコードベースと同じファイル内に記述し、次に Request や Response Body を zod を用いて厳密に型を記述します。これらをエンジニアがレビューした後、テストデータのセットアップや記法などをエンジニアが整え、残りを AI で実装を進めます。さらにアプリケーションコードの実装においても、用意したユニットテストが通過するという制約の元 AI に実装させることにより、型解決などに AI エージェントが費やす時間が最小化され、 実装フェーズの開発工数を大幅に短縮することに成功 しています。 mizchi さんのセッションで言及のあったテスト駆動開発(Test Driven Development)のスキルは今後もより重要になっていくでしょう。しかし、もう少し先の未来では、弊社メドレーが大切にしている価値観(Our Essential)の一つである「 ドキュメントドリブン 」に倣い、 ドキュメントドリブン開発(Document Driven Development) のスキルがより重要になっていくのではないか、と私は考えています。 具体的なイメージ さらに「Speed is King」の思想を体現するために、Linter と Formatter には Biome、パッケージマネージャーには Bun を採用するなどツールチェインの速度にはかなりシビアな意思決定をしており、より AI エージェントが生産的に活動できるような体制を整えています。 一方で、 改善の余地がある点 も明確になりました。特に双方のセッションで強調されていた「機械的検査とフィードバック」の継続的な改善、すなわち Linter については開発初期から現在に至るまであまり積極的に更新できていないと痛感しています。AI に与えるフィードバックをその場での単発のもので終わらせるのではなく、(人間と同じように)再発防止策を考えさせるように Linter を定期的にアップデートし、より決定的で高速に AI エージェントが機能するような環境をより一層整備していきます。 さいごに TypeScript の可能性を肌で感じ、熱意ある仲間たちと出会い、技術への情熱を改めて確認できた素晴らしいイベントでした。 メドレーは今後も TSKaigi だけでなく、他の技術イベントやコミュニティの発展を積極的に支援し、参加、貢献していきます。 過去にスポンサーとして協賛した技術カンファレンスの参加レポート記事はこちら! RubyKaigi 2025 参加レポート - Platinum Sponsor として協賛しました! | MEDLEY Developer Portal はじめに こんにちは! 人材プラットフォーム本部プロダクト開発室 第一開発グループ所属の山下です。 メドレーには今年2月に入社したエンジニアで、日本最大級の医療介護求人サイト ジョブメドレー の開発を担当しています。 メドレーは 4 月 1... developer.medley.jp pmconf 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。医療プラットフォーム本部で Product Manager をしている佐藤です。2024 年 5 月にメドレーにジョインし、医療機関向けプロダクト開発に奔走しています。社内では ”papa”、家では”おじさん”と呼ばれ可愛がら... developer.medley.jp JSConf JP 2024に プレミアムスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは!人材プラットフォーム本部で技術広報兼エンジニア採用をしている重田(@Shige0096)です。2024 年 11 月にメドレーにジョインし、初の社外イベントに参加してきました。 今回、メドレーは 2024/11/23 に九段坂上... developer.medley.jp DroidKaigi 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。人材プラットフォーム本部でエンジニアをしている山河です。2023 年 4 月に新卒として入社し、徐々に業務の幅を広げています! さて、メドレーは 2024/9/11 〜 9/13 の 3 日間にベルサール渋谷ガーデンにて開催され... developer.medley.jp TSKaigi 2025 の運営スタッフの皆さん、登壇者の皆さん、一緒に盛り上がった参加者の皆さん、本当にありがとうございました! メドレーではエンジニアを積極採用中です! メドレーでは領域を問わず、TypeScript を積極的に活用して医療ヘルスケアの未来をつくるプロダクトを開発しています。 TypeScript を活用した医療ヘルスケア領域の課題解決に興味がある方は、ぜひお気軽にご連絡ください! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp ※カジュアル面談ご希望の際は、<その他> にてその旨をご記載ください
はじめに こんにちは! 人材プラットフォーム本部プロダクト統括部プロダクト開発部アカデミー開発グループ所属の城間(シロマ)です。 私は 2024 年 4 月に新卒エンジニアとして入社し、現在はオンライン動画研修サービス「 ジョブメドレーアカデミー 」の新規プロダクト開発に携わっています。 先日、5 月 23 日、24 日に東京都千代田区のベルサール神田にて開催された TSKaigi 2025 にメドレーは Silver Sponsor として協賛しました! TypeScript をテーマにしたこの大規模なカンファレンスには、多様なバックグラウンドを持つ TypeScript エンジニアが全国から集結。 惜しくもブースの抽選には外れてしまいましたが、私も含め弊社からは数名のエンジニアが現地参加し、多くの素晴らしい出会いと学びがありました。 今回は、当日の会場の様子や印象に残ったセッション、そして今、私が感じていることを中心にご紹介します! 会場の熱気と「型にとらわれない」エンジニアたちの交流 今年の TSKaigi 2025 は 2 日間とも天候に恵まれ、 61 もの企業スポンサーと「TypeScript を扱う型にとらわれないエンジニア」が集結し、会場は終日、熱気と活気に満ち溢れていました。 場内の様子 スポンサーボード 2 日間に渡り、合計 3 会場でセッションや LT が盛んに行われ、時には立ち見の人が出るほどに盛況していました。 参加者同士でテーマに対してトークする OST(Open Space Technology) や、夜の豪華な懇親会では、セッションでは聞けないような深掘りした技術の話や、各社の開発文化に関する交流が盛んに行われ、当日はおよそ 50 名以上のエンジニアの方々と色々なお話をさせていただきました。 OST のテーマ 懇親会の様子 TypeScript ケーキ また、今回参加者に配布された公式グッズは、TSKaigi のオリジナル T シャツやトートバッグに加え、各企業が工夫を凝らした遊び心あふれるグッズで盛りだくさんでした。 特に、今回ブース出展のあった 19 社全てを周るスタンプラリー では、全てのスタンプを集めることで豪華景品が当たる抽選に参加でき、会場の賑わいに一役買っていました。 公式ノベルティ(一部) スタンプラリー 印象に残ったセッション:AI エージェント全盛の時代と TypeScript の可能性 どのセッションも非常に興味深く、時には理解が追いつかないほど最先端の内容でしたが、特に今の私が行う開発と照らし合わせて印象に残ったセッションをいくつかご紹介します。 Day1: AI Coding Agent Enablement in TypeScript 引用元: AI Coding Agents Enablement in TypeScript - Speaker Deck 概要 このセッションでは、人間による介入を最小限に抑え、AI エージェントが大規模な作業をどのように「自走」して実行させるかについて深く掘り下げられました。 なぜ自走が難しいのか?その主な要因は AI エージェントが「任意の TypeScript」のようなあまりにも広い「解空間」で動くため、精度が低くなってしまうためです。 すなわち AI エージェントの精度を高めるためには 「可能な限り解空間を絞る」 ことが基本方針であり、会社やプロジェクト固有の規約、ドメイン知識、デザインなどに基づいて適切に制約を与える重要性が何度も強調されていました。 その解空間を絞る具体的なアプローチとして 「コンテキスト注入」 と 「機械的検査とフィードバック」 の 2 つが挙げられました。 コンテキスト注入 : ドキュメント、規約、ドメイン知識などを LLM にインプットし、「解空間の定義」を与えること。特に TypeScript コード自体をドキュメントとして育てることが、コンテキスト注入に役立つ。 機械的検査とフィードバック : LLM が生成したコードが定義された解空間から外れていないか、型チェック、Linter、自動テストといった古典的な手法で検査し、NG の場合はエージェントにフィードバックして解空間へと押し戻すように促す。Linter は次にやるべきアクションを提示しやすく、決定的である点が非常に有効である。 また、TypeScript 開発における型の役割についても興味深い見解がありました。現状、型によってコード生成の精度が上がるという定量的な根拠はまだないものの、「解空間に押し戻す」ためのエージェントへの入力(フィードバック)としては役立つ可能性は大いにあるとのこと。AI はまだ高度な型解決が苦手なので、人間がドメインモデルや関数のシグネチャ(型定義)をしっかり書き、その後の実装をエージェントに任せるアプローチが有効であるという話は、非常に現実的で納得感がありました。 最後に、 「エコシステムの未来 - Speed is King」 という言葉が印象的でした。これは、AI エージェントのコード生成が加速すると、開発プロセス全体の速度がボトルネックとなることを示唆しています。具体的には、人間がコードを 30 分かけて書く時代には 1 分程度の静的解析は許容範囲でしたが、AI が同じコードをわずか 30 秒で書くようになると同じ 1 分間の静的解析は AI の作業時間に対して相対的に非常に長く、無視できないボトルネックになるということです。さらにクラウド型エージェントではチャットごとにコンテナが作成される仕組みが主流であり、その際にパッケージマネージャの速度がボトルネックになります。また、AI エージェントによるコード生産量が爆発的に増加すれば、デプロイの機会も増えるためビルド(バンドラー)の速度もボトルネックとして顕在化し、加えて将来的に LLM がコードを生成するタイミング(デコーディング時)ごとに型制約を守ろうとする「制約付きデコーディング」のようなアプローチが進めば、型チェッカーがボトルネックになる可能性も指摘されていました。故に、ツールチェイン全体の高速化が今後より重要になるという示唆は、将来的な開発環境を考える上で非常に重要な視点だと感じました。 Day2: TS 特化 Cline プログラミング 引用元: tskaigi.mizchi.workers.dev 概要 このセッションでは、LLM を活用した「Cline Agentic Coding」の全般について、特に TypeScript に特化した実践的なプロンプトのコツが紹介されました。 まず冒頭に うまくいくうまくいくプロンプトのコツ として、以下が挙げられていました。 書きすぎない(再現性ある範囲で詠唱破棄) 執拗に出力例を例示する 両立条件の矛盾を避ける 規模感に合わせて厳しくする また特に強調されていたのは、 テスト駆動開発(Test Driven Development) の重要性です。コード生成時に対応するユニットテストを常に生成し、コード修正時にはテストがパスすることを確認することで、AI エージェントの自己修復能力を高め、放置しても完成する高品質なコードに繋がるとのことでした。 その他にも、次のような実践的なプロンプトの一例が紹介されていました。 コメントによる自己記述 : 各ファイルの冒頭にコメントで仕様を記述することで、再修正時のコード解釈の一貫性を保つ In Source Testing : 実装と同じファイルにユニットテストを書くことで、コメント・実装・テストを三位一体で管理する types.ts にドメイン型を集約 : 中規模以上のプロジェクトでは src/types.ts にドメインモデルを集約し、SSoT(Single Source of Truth)とすることで、ファイル間の整合性を保ち read_file の頻度を減らす TS + 関数型ドメインモデリング : 状態の発散を抑えるために class を使わず関数による実装を優先し、代数的データ型でドメインをモデリングする ファイル配置規則の明記 : モノレポなどのファイル配置規則を明記することで、タスクごとのエージェントの推測コストを減らす 詳細指示を docs/*.md に分割 : 大規模プロジェクトでは無関係な指示によるノイズを減らすために、詳細な指示をドキュメントファイルに分割し、必要に応じて参照させる カバレッジに基づくテストの自動生成 : vitest でカバレッジを計測し、最もカバレッジが上がるテストコードを AI に考察・追加させる 機械的なマイグレーション : lodash の削除や類似 API を持つライブラリへの置き換えなど、面倒なマイグレーション作業を AI に自動化させる URL を読む能力 : URL の内容を読み込ませ、要約・保存させることで、Deep Research 的な挙動を可能にする 一方で、 うまくいかないプロンプト のパターンもいくつか示唆されました。 型だけで設計しようとする : ほぼ確実に無視され、抽象的な設計能力は期待できない 非同期例外処理が下手 : 思考停止気味に try-catch で握り潰しがちで、大規模開発で破綻の原因となる 環境構築が下手 : ゼロショットでの環境構築は発散しやすく、手数が仇となり環境を破壊する可能性がある モジュールインターフェースが発散 : 実装次第で全て export してしまい、モジュール間の契約が肥大化・破綻する 「ある」のがよくない(チェーホフの銃の法則) : 無関係なリソースを読み込ませると、それを使うことに固執し、大規模コードではノイズになる デバッグログを食いすぎる : 自身が生成したプリントデバッグでコンテキストウィンドウを消費し、デバッグコードを放置しがちである 結論として、現状の LLM はコーディングが下手であり、低品質なコードで設計が破綻し自滅する傾向があるとの見解でした。特に、リファクタリングの指針がなく、不要コードの判定やモジュール視点での API 設計が苦手で、ユーザー側でリファクタリングしても元の低品質なコードに書き戻すこともあるという少し厳しい評価が下されていました。 しかし TypeScript と LLM の組み合わせには良い点も多く、GitHub の公開コードが豊富で学習量が多いこと、安全性よりも表現力を選んだピーキーな型システムが自然言語と対応した型のモデリングをしやすいこと、豊富な静的解析手段とユーザー層の厚さがあることなどが挙げられていました。 現時点でのベストプラクティスとしては、PoC/プロトタイプのコード生成(1 ファイル完結 800 行以内が目安)に活用し、人間によるインターフェース設計、失敗パターンのプロンプトへの反映、成功するパターンのドキュメント化、そしてこちらでも Lint ルールの整備についての言及がありました。 そして最後にテスト駆動開発や LLM の得意領域・発達段階を予測する技術、プロンプトエンジニアリングの重要性の増加など Agentic Coding によるプログラミング自体の変質と不変である点について説明され締めくくられました。 感想 これらのセッションを通して、私が携わる新規プロダクト開発において、 実践できている部分 と 改善の余地がある部分 が明確になりました。 実践できている点として、全社的に積極的に利用している AI エディタ「Cursor」のプロジェクトルールや自立型 AI エージェント「Devin」の knowledge には、プロジェクトごとに解空間を明確にするコンテキストを与えています。これは、「AI Coding Agent Enablement in TypeScript」で述べられていた「コンテキスト注入」と「解空間を絞る」という考え方に合致していると感じました。また、コードと仕様を記述したドキュメントを同じリポジトリ内で管理し、開発を進めている点も、AI へのコンテキスト注入に貢献していると言えるでしょう。 開発フローにおいても、AI の特性を踏まえた工夫を凝らしています。「AI Coding Agent Enablement in TypeScript」で言及があった「3 回くらいループした末に any とかキャストで誤魔化しがち」という点に対し、例えば API 開発では、まず仕様やテストケースのドキュメントをコードベースと同じファイル内に記述し、次に Request や Response Body を zod を用いて厳密に型を記述します。これらをエンジニアがレビューした後、テストデータのセットアップや記法などをエンジニアが整え、残りを AI で実装を進めます。さらにアプリケーションコードの実装においても、用意したユニットテストが通過するという制約の元 AI に実装させることにより、型解決などに AI エージェントが費やす時間が最小化され、 実装フェーズの開発工数を大幅に短縮することに成功 しています。 mizchi さんのセッションで言及のあったテスト駆動開発(Test Driven Development)のスキルは今後もより重要になっていくでしょう。しかし、もう少し先の未来では、弊社メドレーが大切にしている価値観(Our Essential)の一つである「 ドキュメントドリブン 」に倣い、 ドキュメントドリブン開発(Document Driven Development) のスキルがより重要になっていくのではないか、と私は考えています。 具体的なイメージ さらに「Speed is King」の思想を体現するために、Linter と Formatter には Biome、パッケージマネージャーには Bun を採用するなどツールチェインの速度にはかなりシビアな意思決定をしており、より AI エージェントが生産的に活動できるような体制を整えています。 一方で、 改善の余地がある点 も明確になりました。特に双方のセッションで強調されていた「機械的検査とフィードバック」の継続的な改善、すなわち Linter については開発初期から現在に至るまであまり積極的に更新できていないと痛感しています。AI に与えるフィードバックをその場での単発のもので終わらせるのではなく、(人間と同じように)再発防止策を考えさせるように Linter を定期的にアップデートし、より決定的で高速に AI エージェントが機能するような環境をより一層整備していきます。 さいごに TypeScript の可能性を肌で感じ、熱意ある仲間たちと出会い、技術への情熱を改めて確認できた素晴らしいイベントでした。 メドレーは今後も TSKaigi だけでなく、他の技術イベントやコミュニティの発展を積極的に支援し、参加、貢献していきます。 過去にスポンサーとして協賛した技術カンファレンスの参加レポート記事はこちら! RubyKaigi 2025 参加レポート - Platinum Sponsor として協賛しました! | MEDLEY Developer Portal はじめに こんにちは! 人材プラットフォーム本部プロダクト開発室 第一開発グループ所属の山下です。 メドレーには今年2月に入社したエンジニアで、日本最大級の医療介護求人サイト ジョブメドレー の開発を担当しています。 メドレーは 4 月 1... developer.medley.jp pmconf 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。医療プラットフォーム本部で Product Manager をしている佐藤です。2024 年 5 月にメドレーにジョインし、医療機関向けプロダクト開発に奔走しています。社内では ”papa”、家では”おじさん”と呼ばれ可愛がら... developer.medley.jp JSConf JP 2024に プレミアムスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは!人材プラットフォーム本部で技術広報兼エンジニア採用をしている重田(@Shige0096)です。2024 年 11 月にメドレーにジョインし、初の社外イベントに参加してきました。 今回、メドレーは 2024/11/23 に九段坂上... developer.medley.jp DroidKaigi 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。人材プラットフォーム本部でエンジニアをしている山河です。2023 年 4 月に新卒として入社し、徐々に業務の幅を広げています! さて、メドレーは 2024/9/11 〜 9/13 の 3 日間にベルサール渋谷ガーデンにて開催され... developer.medley.jp TSKaigi 2025 の運営スタッフの皆さん、登壇者の皆さん、一緒に盛り上がった参加者の皆さん、本当にありがとうございました! メドレーではエンジニアを積極採用中です! メドレーでは領域を問わず、TypeScript を積極的に活用して医療ヘルスケアの未来をつくるプロダクトを開発しています。 TypeScript を活用した医療ヘルスケア領域の課題解決に興味がある方は、ぜひお気軽にご連絡ください! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp ※カジュアル面談ご希望の際は、<その他> にてその旨をご記載ください
はじめに こんにちは! 人材プラットフォーム本部プロダクト統括部プロダクト開発部アカデミー開発グループ所属の城間(シロマ)です。 私は 2024 年 4 月に新卒エンジニアとして入社し、現在はオンライン動画研修サービス「 ジョブメドレーアカデミー 」の新規プロダクト開発に携わっています。 先日、5 月 23 日、24 日に東京都千代田区のベルサール神田にて開催された TSKaigi 2025 にメドレーは Silver Sponsor として協賛しました! TypeScript をテーマにしたこの大規模なカンファレンスには、多様なバックグラウンドを持つ TypeScript エンジニアが全国から集結。 惜しくもブースの抽選には外れてしまいましたが、私も含め弊社からは数名のエンジニアが現地参加し、多くの素晴らしい出会いと学びがありました。 今回は、当日の会場の様子や印象に残ったセッション、そして今、私が感じていることを中心にご紹介します! 会場の熱気と「型にとらわれない」エンジニアたちの交流 今年の TSKaigi 2025 は 2 日間とも天候に恵まれ、 61 もの企業スポンサーと「TypeScript を扱う型にとらわれないエンジニア」が集結し、会場は終日、熱気と活気に満ち溢れていました。 場内の様子 スポンサーボード 2 日間に渡り、合計 3 会場でセッションや LT が盛んに行われ、時には立ち見の人が出るほどに盛況していました。 参加者同士でテーマに対してトークする OST(Open Space Technology) や、夜の豪華な懇親会では、セッションでは聞けないような深掘りした技術の話や、各社の開発文化に関する交流が盛んに行われ、当日はおよそ 50 名以上のエンジニアの方々と色々なお話をさせていただきました。 OST のテーマ 懇親会の様子 TypeScript ケーキ また、今回参加者に配布された公式グッズは、TSKaigi のオリジナル T シャツやトートバッグに加え、各企業が工夫を凝らした遊び心あふれるグッズで盛りだくさんでした。 特に、今回ブース出展のあった 19 社全てを周るスタンプラリー では、全てのスタンプを集めることで豪華景品が当たる抽選に参加でき、会場の賑わいに一役買っていました。 公式ノベルティ(一部) スタンプラリー 印象に残ったセッション:AI エージェント全盛の時代と TypeScript の可能性 どのセッションも非常に興味深く、時には理解が追いつかないほど最先端の内容でしたが、特に今の私が行う開発と照らし合わせて印象に残ったセッションをいくつかご紹介します。 Day1: AI Coding Agent Enablement in TypeScript 引用元: AI Coding Agents Enablement in TypeScript - Speaker Deck 概要 このセッションでは、人間による介入を最小限に抑え、AI エージェントが大規模な作業をどのように「自走」して実行させるかについて深く掘り下げられました。 なぜ自走が難しいのか?その主な要因は AI エージェントが「任意の TypeScript」のようなあまりにも広い「解空間」で動くため、精度が低くなってしまうためです。 すなわち AI エージェントの精度を高めるためには 「可能な限り解空間を絞る」 ことが基本方針であり、会社やプロジェクト固有の規約、ドメイン知識、デザインなどに基づいて適切に制約を与える重要性が何度も強調されていました。 その解空間を絞る具体的なアプローチとして 「コンテキスト注入」 と 「機械的検査とフィードバック」 の 2 つが挙げられました。 コンテキスト注入 : ドキュメント、規約、ドメイン知識などを LLM にインプットし、「解空間の定義」を与えること。特に TypeScript コード自体をドキュメントとして育てることが、コンテキスト注入に役立つ。 機械的検査とフィードバック : LLM が生成したコードが定義された解空間から外れていないか、型チェック、Linter、自動テストといった古典的な手法で検査し、NG の場合はエージェントにフィードバックして解空間へと押し戻すように促す。Linter は次にやるべきアクションを提示しやすく、決定的である点が非常に有効である。 また、TypeScript 開発における型の役割についても興味深い見解がありました。現状、型によってコード生成の精度が上がるという定量的な根拠はまだないものの、「解空間に押し戻す」ためのエージェントへの入力(フィードバック)としては役立つ可能性は大いにあるとのこと。AI はまだ高度な型解決が苦手なので、人間がドメインモデルや関数のシグネチャ(型定義)をしっかり書き、その後の実装をエージェントに任せるアプローチが有効であるという話は、非常に現実的で納得感がありました。 最後に、 「エコシステムの未来 - Speed is King」 という言葉が印象的でした。これは、AI エージェントのコード生成が加速すると、開発プロセス全体の速度がボトルネックとなることを示唆しています。具体的には、人間がコードを 30 分かけて書く時代には 1 分程度の静的解析は許容範囲でしたが、AI が同じコードをわずか 30 秒で書くようになると同じ 1 分間の静的解析は AI の作業時間に対して相対的に非常に長く、無視できないボトルネックになるということです。さらにクラウド型エージェントではチャットごとにコンテナが作成される仕組みが主流であり、その際にパッケージマネージャの速度がボトルネックになります。また、AI エージェントによるコード生産量が爆発的に増加すれば、デプロイの機会も増えるためビルド(バンドラー)の速度もボトルネックとして顕在化し、加えて将来的に LLM がコードを生成するタイミング(デコーディング時)ごとに型制約を守ろうとする「制約付きデコーディング」のようなアプローチが進めば、型チェッカーがボトルネックになる可能性も指摘されていました。故に、ツールチェイン全体の高速化が今後より重要になるという示唆は、将来的な開発環境を考える上で非常に重要な視点だと感じました。 Day2: TS 特化 Cline プログラミング 引用元: tskaigi.mizchi.workers.dev 概要 このセッションでは、LLM を活用した「Cline Agentic Coding」の全般について、特に TypeScript に特化した実践的なプロンプトのコツが紹介されました。 まず冒頭に うまくいくうまくいくプロンプトのコツ として、以下が挙げられていました。 書きすぎない(再現性ある範囲で詠唱破棄) 執拗に出力例を例示する 両立条件の矛盾を避ける 規模感に合わせて厳しくする また特に強調されていたのは、 テスト駆動開発(Test Driven Development) の重要性です。コード生成時に対応するユニットテストを常に生成し、コード修正時にはテストがパスすることを確認することで、AI エージェントの自己修復能力を高め、放置しても完成する高品質なコードに繋がるとのことでした。 その他にも、次のような実践的なプロンプトの一例が紹介されていました。 コメントによる自己記述 : 各ファイルの冒頭にコメントで仕様を記述することで、再修正時のコード解釈の一貫性を保つ In Source Testing : 実装と同じファイルにユニットテストを書くことで、コメント・実装・テストを三位一体で管理する types.ts にドメイン型を集約 : 中規模以上のプロジェクトでは src/types.ts にドメインモデルを集約し、SSoT(Single Source of Truth)とすることで、ファイル間の整合性を保ち read_file の頻度を減らす TS + 関数型ドメインモデリング : 状態の発散を抑えるために class を使わず関数による実装を優先し、代数的データ型でドメインをモデリングする ファイル配置規則の明記 : モノレポなどのファイル配置規則を明記することで、タスクごとのエージェントの推測コストを減らす 詳細指示を docs/*.md に分割 : 大規模プロジェクトでは無関係な指示によるノイズを減らすために、詳細な指示をドキュメントファイルに分割し、必要に応じて参照させる カバレッジに基づくテストの自動生成 : vitest でカバレッジを計測し、最もカバレッジが上がるテストコードを AI に考察・追加させる 機械的なマイグレーション : lodash の削除や類似 API を持つライブラリへの置き換えなど、面倒なマイグレーション作業を AI に自動化させる URL を読む能力 : URL の内容を読み込ませ、要約・保存させることで、Deep Research 的な挙動を可能にする 一方で、 うまくいかないプロンプト のパターンもいくつか示唆されました。 型だけで設計しようとする : ほぼ確実に無視され、抽象的な設計能力は期待できない 非同期例外処理が下手 : 思考停止気味に try-catch で握り潰しがちで、大規模開発で破綻の原因となる 環境構築が下手 : ゼロショットでの環境構築は発散しやすく、手数が仇となり環境を破壊する可能性がある モジュールインターフェースが発散 : 実装次第で全て export してしまい、モジュール間の契約が肥大化・破綻する 「ある」のがよくない(チェーホフの銃の法則) : 無関係なリソースを読み込ませると、それを使うことに固執し、大規模コードではノイズになる デバッグログを食いすぎる : 自身が生成したプリントデバッグでコンテキストウィンドウを消費し、デバッグコードを放置しがちである 結論として、現状の LLM はコーディングが下手であり、低品質なコードで設計が破綻し自滅する傾向があるとの見解でした。特に、リファクタリングの指針がなく、不要コードの判定やモジュール視点での API 設計が苦手で、ユーザー側でリファクタリングしても元の低品質なコードに書き戻すこともあるという少し厳しい評価が下されていました。 しかし TypeScript と LLM の組み合わせには良い点も多く、GitHub の公開コードが豊富で学習量が多いこと、安全性よりも表現力を選んだピーキーな型システムが自然言語と対応した型のモデリングをしやすいこと、豊富な静的解析手段とユーザー層の厚さがあることなどが挙げられていました。 現時点でのベストプラクティスとしては、PoC/プロトタイプのコード生成(1 ファイル完結 800 行以内が目安)に活用し、人間によるインターフェース設計、失敗パターンのプロンプトへの反映、成功するパターンのドキュメント化、そしてこちらでも Lint ルールの整備についての言及がありました。 そして最後にテスト駆動開発や LLM の得意領域・発達段階を予測する技術、プロンプトエンジニアリングの重要性の増加など Agentic Coding によるプログラミング自体の変質と不変である点について説明され締めくくられました。 感想 これらのセッションを通して、私が携わる新規プロダクト開発において、 実践できている部分 と 改善の余地がある部分 が明確になりました。 実践できている点として、全社的に積極的に利用している AI エディタ「Cursor」のプロジェクトルールや自立型 AI エージェント「Devin」の knowledge には、プロジェクトごとに解空間を明確にするコンテキストを与えています。これは、「AI Coding Agent Enablement in TypeScript」で述べられていた「コンテキスト注入」と「解空間を絞る」という考え方に合致していると感じました。また、コードと仕様を記述したドキュメントを同じリポジトリ内で管理し、開発を進めている点も、AI へのコンテキスト注入に貢献していると言えるでしょう。 開発フローにおいても、AI の特性を踏まえた工夫を凝らしています。「AI Coding Agent Enablement in TypeScript」で言及があった「3 回くらいループした末に any とかキャストで誤魔化しがち」という点に対し、例えば API 開発では、まず仕様やテストケースのドキュメントをコードベースと同じファイル内に記述し、次に Request や Response Body を zod を用いて厳密に型を記述します。これらをエンジニアがレビューした後、テストデータのセットアップや記法などをエンジニアが整え、残りを AI で実装を進めます。さらにアプリケーションコードの実装においても、用意したユニットテストが通過するという制約の元 AI に実装させることにより、型解決などに AI エージェントが費やす時間が最小化され、 実装フェーズの開発工数を大幅に短縮することに成功 しています。 mizchi さんのセッションで言及のあったテスト駆動開発(Test Driven Development)のスキルは今後もより重要になっていくでしょう。しかし、もう少し先の未来では、弊社メドレーが大切にしている価値観(Our Essential)の一つである「 ドキュメントドリブン 」に倣い、 ドキュメントドリブン開発(Document Driven Development) のスキルがより重要になっていくのではないか、と私は考えています。 具体的なイメージ さらに「Speed is King」の思想を体現するために、Linter と Formatter には Biome、パッケージマネージャーには Bun を採用するなどツールチェインの速度にはかなりシビアな意思決定をしており、より AI エージェントが生産的に活動できるような体制を整えています。 一方で、 改善の余地がある点 も明確になりました。特に双方のセッションで強調されていた「機械的検査とフィードバック」の継続的な改善、すなわち Linter については開発初期から現在に至るまであまり積極的に更新できていないと痛感しています。AI に与えるフィードバックをその場での単発のもので終わらせるのではなく、(人間と同じように)再発防止策を考えさせるように Linter を定期的にアップデートし、より決定的で高速に AI エージェントが機能するような環境をより一層整備していきます。 さいごに TypeScript の可能性を肌で感じ、熱意ある仲間たちと出会い、技術への情熱を改めて確認できた素晴らしいイベントでした。 メドレーは今後も TSKaigi だけでなく、他の技術イベントやコミュニティの発展を積極的に支援し、参加、貢献していきます。 過去にスポンサーとして協賛した技術カンファレンスの参加レポート記事はこちら! RubyKaigi 2025 参加レポート - Platinum Sponsor として協賛しました! | MEDLEY Developer Portal はじめに こんにちは! 人材プラットフォーム本部プロダクト開発室 第一開発グループ所属の山下です。 メドレーには今年2月に入社したエンジニアで、日本最大級の医療介護求人サイト ジョブメドレー の開発を担当しています。 メドレーは 4 月 1... developer.medley.jp pmconf 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。医療プラットフォーム本部で Product Manager をしている佐藤です。2024 年 5 月にメドレーにジョインし、医療機関向けプロダクト開発に奔走しています。社内では ”papa”、家では”おじさん”と呼ばれ可愛がら... developer.medley.jp JSConf JP 2024に プレミアムスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは!人材プラットフォーム本部で技術広報兼エンジニア採用をしている重田(@Shige0096)です。2024 年 11 月にメドレーにジョインし、初の社外イベントに参加してきました。 今回、メドレーは 2024/11/23 に九段坂上... developer.medley.jp DroidKaigi 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。人材プラットフォーム本部でエンジニアをしている山河です。2023 年 4 月に新卒として入社し、徐々に業務の幅を広げています! さて、メドレーは 2024/9/11 〜 9/13 の 3 日間にベルサール渋谷ガーデンにて開催され... developer.medley.jp TSKaigi 2025 の運営スタッフの皆さん、登壇者の皆さん、一緒に盛り上がった参加者の皆さん、本当にありがとうございました! メドレーではエンジニアを積極採用中です! メドレーでは領域を問わず、TypeScript を積極的に活用して医療ヘルスケアの未来をつくるプロダクトを開発しています。 TypeScript を活用した医療ヘルスケア領域の課題解決に興味がある方は、ぜひお気軽にご連絡ください! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp ※カジュアル面談ご希望の際は、<その他> にてその旨をご記載ください
はじめに こんにちは! 人材プラットフォーム本部プロダクト統括部プロダクト開発部アカデミー開発グループ所属の城間(シロマ)です。 私は 2024 年 4 月に新卒エンジニアとして入社し、現在はオンライン動画研修サービス「 ジョブメドレーアカデミー 」の新規プロダクト開発に携わっています。 先日、5 月 23 日、24 日に東京都千代田区のベルサール神田にて開催された TSKaigi 2025 にメドレーは Silver Sponsor として協賛しました! TypeScript をテーマにしたこの大規模なカンファレンスには、多様なバックグラウンドを持つ TypeScript エンジニアが全国から集結。 惜しくもブースの抽選には外れてしまいましたが、私も含め弊社からは数名のエンジニアが現地参加し、多くの素晴らしい出会いと学びがありました。 今回は、当日の会場の様子や印象に残ったセッション、そして今、私が感じていることを中心にご紹介します! 会場の熱気と「型にとらわれない」エンジニアたちの交流 今年の TSKaigi 2025 は 2 日間とも天候に恵まれ、 61 もの企業スポンサーと「TypeScript を扱う型にとらわれないエンジニア」が集結し、会場は終日、熱気と活気に満ち溢れていました。 場内の様子 スポンサーボード 2 日間に渡り、合計 3 会場でセッションや LT が盛んに行われ、時には立ち見の人が出るほどに盛況していました。 参加者同士でテーマに対してトークする OST(Open Space Technology) や、夜の豪華な懇親会では、セッションでは聞けないような深掘りした技術の話や、各社の開発文化に関する交流が盛んに行われ、当日はおよそ 50 名以上のエンジニアの方々と色々なお話をさせていただきました。 OST のテーマ 懇親会の様子 TypeScript ケーキ また、今回参加者に配布された公式グッズは、TSKaigi のオリジナル T シャツやトートバッグに加え、各企業が工夫を凝らした遊び心あふれるグッズで盛りだくさんでした。 特に、今回ブース出展のあった 19 社全てを周るスタンプラリー では、全てのスタンプを集めることで豪華景品が当たる抽選に参加でき、会場の賑わいに一役買っていました。 公式ノベルティ(一部) スタンプラリー 印象に残ったセッション:AI エージェント全盛の時代と TypeScript の可能性 どのセッションも非常に興味深く、時には理解が追いつかないほど最先端の内容でしたが、特に今の私が行う開発と照らし合わせて印象に残ったセッションをいくつかご紹介します。 Day1: AI Coding Agent Enablement in TypeScript 引用元: AI Coding Agents Enablement in TypeScript - Speaker Deck 概要 このセッションでは、人間による介入を最小限に抑え、AI エージェントが大規模な作業をどのように「自走」して実行させるかについて深く掘り下げられました。 なぜ自走が難しいのか?その主な要因は AI エージェントが「任意の TypeScript」のようなあまりにも広い「解空間」で動くため、精度が低くなってしまうためです。 すなわち AI エージェントの精度を高めるためには 「可能な限り解空間を絞る」 ことが基本方針であり、会社やプロジェクト固有の規約、ドメイン知識、デザインなどに基づいて適切に制約を与える重要性が何度も強調されていました。 その解空間を絞る具体的なアプローチとして 「コンテキスト注入」 と 「機械的検査とフィードバック」 の 2 つが挙げられました。 コンテキスト注入 : ドキュメント、規約、ドメイン知識などを LLM にインプットし、「解空間の定義」を与えること。特に TypeScript コード自体をドキュメントとして育てることが、コンテキスト注入に役立つ。 機械的検査とフィードバック : LLM が生成したコードが定義された解空間から外れていないか、型チェック、Linter、自動テストといった古典的な手法で検査し、NG の場合はエージェントにフィードバックして解空間へと押し戻すように促す。Linter は次にやるべきアクションを提示しやすく、決定的である点が非常に有効である。 また、TypeScript 開発における型の役割についても興味深い見解がありました。現状、型によってコード生成の精度が上がるという定量的な根拠はまだないものの、「解空間に押し戻す」ためのエージェントへの入力(フィードバック)としては役立つ可能性は大いにあるとのこと。AI はまだ高度な型解決が苦手なので、人間がドメインモデルや関数のシグネチャ(型定義)をしっかり書き、その後の実装をエージェントに任せるアプローチが有効であるという話は、非常に現実的で納得感がありました。 最後に、 「エコシステムの未来 - Speed is King」 という言葉が印象的でした。これは、AI エージェントのコード生成が加速すると、開発プロセス全体の速度がボトルネックとなることを示唆しています。具体的には、人間がコードを 30 分かけて書く時代には 1 分程度の静的解析は許容範囲でしたが、AI が同じコードをわずか 30 秒で書くようになると同じ 1 分間の静的解析は AI の作業時間に対して相対的に非常に長く、無視できないボトルネックになるということです。さらにクラウド型エージェントではチャットごとにコンテナが作成される仕組みが主流であり、その際にパッケージマネージャの速度がボトルネックになります。また、AI エージェントによるコード生産量が爆発的に増加すれば、デプロイの機会も増えるためビルド(バンドラー)の速度もボトルネックとして顕在化し、加えて将来的に LLM がコードを生成するタイミング(デコーディング時)ごとに型制約を守ろうとする「制約付きデコーディング」のようなアプローチが進めば、型チェッカーがボトルネックになる可能性も指摘されていました。故に、ツールチェイン全体の高速化が今後より重要になるという示唆は、将来的な開発環境を考える上で非常に重要な視点だと感じました。 Day2: TS 特化 Cline プログラミング 引用元: tskaigi.mizchi.workers.dev 概要 このセッションでは、LLM を活用した「Cline Agentic Coding」の全般について、特に TypeScript に特化した実践的なプロンプトのコツが紹介されました。 まず冒頭に うまくいくうまくいくプロンプトのコツ として、以下が挙げられていました。 書きすぎない(再現性ある範囲で詠唱破棄) 執拗に出力例を例示する 両立条件の矛盾を避ける 規模感に合わせて厳しくする また特に強調されていたのは、 テスト駆動開発(Test Driven Development) の重要性です。コード生成時に対応するユニットテストを常に生成し、コード修正時にはテストがパスすることを確認することで、AI エージェントの自己修復能力を高め、放置しても完成する高品質なコードに繋がるとのことでした。 その他にも、次のような実践的なプロンプトの一例が紹介されていました。 コメントによる自己記述 : 各ファイルの冒頭にコメントで仕様を記述することで、再修正時のコード解釈の一貫性を保つ In Source Testing : 実装と同じファイルにユニットテストを書くことで、コメント・実装・テストを三位一体で管理する types.ts にドメイン型を集約 : 中規模以上のプロジェクトでは src/types.ts にドメインモデルを集約し、SSoT(Single Source of Truth)とすることで、ファイル間の整合性を保ち read_file の頻度を減らす TS + 関数型ドメインモデリング : 状態の発散を抑えるために class を使わず関数による実装を優先し、代数的データ型でドメインをモデリングする ファイル配置規則の明記 : モノレポなどのファイル配置規則を明記することで、タスクごとのエージェントの推測コストを減らす 詳細指示を docs/*.md に分割 : 大規模プロジェクトでは無関係な指示によるノイズを減らすために、詳細な指示をドキュメントファイルに分割し、必要に応じて参照させる カバレッジに基づくテストの自動生成 : vitest でカバレッジを計測し、最もカバレッジが上がるテストコードを AI に考察・追加させる 機械的なマイグレーション : lodash の削除や類似 API を持つライブラリへの置き換えなど、面倒なマイグレーション作業を AI に自動化させる URL を読む能力 : URL の内容を読み込ませ、要約・保存させることで、Deep Research 的な挙動を可能にする 一方で、 うまくいかないプロンプト のパターンもいくつか示唆されました。 型だけで設計しようとする : ほぼ確実に無視され、抽象的な設計能力は期待できない 非同期例外処理が下手 : 思考停止気味に try-catch で握り潰しがちで、大規模開発で破綻の原因となる 環境構築が下手 : ゼロショットでの環境構築は発散しやすく、手数が仇となり環境を破壊する可能性がある モジュールインターフェースが発散 : 実装次第で全て export してしまい、モジュール間の契約が肥大化・破綻する 「ある」のがよくない(チェーホフの銃の法則) : 無関係なリソースを読み込ませると、それを使うことに固執し、大規模コードではノイズになる デバッグログを食いすぎる : 自身が生成したプリントデバッグでコンテキストウィンドウを消費し、デバッグコードを放置しがちである 結論として、現状の LLM はコーディングが下手であり、低品質なコードで設計が破綻し自滅する傾向があるとの見解でした。特に、リファクタリングの指針がなく、不要コードの判定やモジュール視点での API 設計が苦手で、ユーザー側でリファクタリングしても元の低品質なコードに書き戻すこともあるという少し厳しい評価が下されていました。 しかし TypeScript と LLM の組み合わせには良い点も多く、GitHub の公開コードが豊富で学習量が多いこと、安全性よりも表現力を選んだピーキーな型システムが自然言語と対応した型のモデリングをしやすいこと、豊富な静的解析手段とユーザー層の厚さがあることなどが挙げられていました。 現時点でのベストプラクティスとしては、PoC/プロトタイプのコード生成(1 ファイル完結 800 行以内が目安)に活用し、人間によるインターフェース設計、失敗パターンのプロンプトへの反映、成功するパターンのドキュメント化、そしてこちらでも Lint ルールの整備についての言及がありました。 そして最後にテスト駆動開発や LLM の得意領域・発達段階を予測する技術、プロンプトエンジニアリングの重要性の増加など Agentic Coding によるプログラミング自体の変質と不変である点について説明され締めくくられました。 感想 これらのセッションを通して、私が携わる新規プロダクト開発において、 実践できている部分 と 改善の余地がある部分 が明確になりました。 実践できている点として、全社的に積極的に利用している AI エディタ「Cursor」のプロジェクトルールや自立型 AI エージェント「Devin」の knowledge には、プロジェクトごとに解空間を明確にするコンテキストを与えています。これは、「AI Coding Agent Enablement in TypeScript」で述べられていた「コンテキスト注入」と「解空間を絞る」という考え方に合致していると感じました。また、コードと仕様を記述したドキュメントを同じリポジトリ内で管理し、開発を進めている点も、AI へのコンテキスト注入に貢献していると言えるでしょう。 開発フローにおいても、AI の特性を踏まえた工夫を凝らしています。「AI Coding Agent Enablement in TypeScript」で言及があった「3 回くらいループした末に any とかキャストで誤魔化しがち」という点に対し、例えば API 開発では、まず仕様やテストケースのドキュメントをコードベースと同じファイル内に記述し、次に Request や Response Body を zod を用いて厳密に型を記述します。これらをエンジニアがレビューした後、テストデータのセットアップや記法などをエンジニアが整え、残りを AI で実装を進めます。さらにアプリケーションコードの実装においても、用意したユニットテストが通過するという制約の元 AI に実装させることにより、型解決などに AI エージェントが費やす時間が最小化され、 実装フェーズの開発工数を大幅に短縮することに成功 しています。 mizchi さんのセッションで言及のあったテスト駆動開発(Test Driven Development)のスキルは今後もより重要になっていくでしょう。しかし、もう少し先の未来では、弊社メドレーが大切にしている価値観(Our Essential)の一つである「 ドキュメントドリブン 」に倣い、 ドキュメントドリブン開発(Document Driven Development) のスキルがより重要になっていくのではないか、と私は考えています。 具体的なイメージ さらに「Speed is King」の思想を体現するために、Linter と Formatter には Biome、パッケージマネージャーには Bun を採用するなどツールチェインの速度にはかなりシビアな意思決定をしており、より AI エージェントが生産的に活動できるような体制を整えています。 一方で、 改善の余地がある点 も明確になりました。特に双方のセッションで強調されていた「機械的検査とフィードバック」の継続的な改善、すなわち Linter については開発初期から現在に至るまであまり積極的に更新できていないと痛感しています。AI に与えるフィードバックをその場での単発のもので終わらせるのではなく、(人間と同じように)再発防止策を考えさせるように Linter を定期的にアップデートし、より決定的で高速に AI エージェントが機能するような環境をより一層整備していきます。 さいごに TypeScript の可能性を肌で感じ、熱意ある仲間たちと出会い、技術への情熱を改めて確認できた素晴らしいイベントでした。 メドレーは今後も TSKaigi だけでなく、他の技術イベントやコミュニティの発展を積極的に支援し、参加、貢献していきます。 過去にスポンサーとして協賛した技術カンファレンスの参加レポート記事はこちら! RubyKaigi 2025 参加レポート - Platinum Sponsor として協賛しました! | MEDLEY Developer Portal はじめに こんにちは! 人材プラットフォーム本部プロダクト開発室 第一開発グループ所属の山下です。 メドレーには今年2月に入社したエンジニアで、日本最大級の医療介護求人サイト ジョブメドレー の開発を担当しています。 メドレーは 4 月 1... developer.medley.jp pmconf 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。医療プラットフォーム本部で Product Manager をしている佐藤です。2024 年 5 月にメドレーにジョインし、医療機関向けプロダクト開発に奔走しています。社内では ”papa”、家では”おじさん”と呼ばれ可愛がら... developer.medley.jp JSConf JP 2024に プレミアムスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは!人材プラットフォーム本部で技術広報兼エンジニア採用をしている重田(@Shige0096)です。2024 年 11 月にメドレーにジョインし、初の社外イベントに参加してきました。 今回、メドレーは 2024/11/23 に九段坂上... developer.medley.jp DroidKaigi 2024にゴールドスポンサーとして協賛しました! | MEDLEY Developer Portal こんにちは。人材プラットフォーム本部でエンジニアをしている山河です。2023 年 4 月に新卒として入社し、徐々に業務の幅を広げています! さて、メドレーは 2024/9/11 〜 9/13 の 3 日間にベルサール渋谷ガーデンにて開催され... developer.medley.jp TSKaigi 2025 の運営スタッフの皆さん、登壇者の皆さん、一緒に盛り上がった参加者の皆さん、本当にありがとうございました! メドレーではエンジニアを積極採用中です! メドレーでは領域を問わず、TypeScript を積極的に活用して医療ヘルスケアの未来をつくるプロダクトを開発しています。 TypeScript を活用した医療ヘルスケア領域の課題解決に興味がある方は、ぜひお気軽にご連絡ください! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp ※カジュアル面談ご希望の際は、<その他> にてその旨をご記載ください
はじめに こんにちは。医療プラットフォーム本部 CLINICS 開発グループの吉岡( @yuya333_ )と村上( @yuporonM )です。 吉岡は 2022 年に、村上は 2024 年に新卒でエンジニアとしてメドレーに入社しました。 私たちは、医科診療所向けの電子カルテである CLINICS カルテ を開発しています。CLINICS カルテは 2025 年 3 月に iPad 手書き機能をリリースしました。iPad 手書き機能とは、iPad を使って主訴・所見やシェーマ(身体の部位の絵)を手書きできる機能です。 主訴・所見の手書き シェーマの手書き 本記事では iPad 手書き機能を開発するにあたって工夫した点を紹介します。 ライブラリ選定 手書き機能を開発するにあたって、手書き処理すべてを実装するのは工数の観点から現実的ではありませんでした。 そのため、要件を満たせるかつメンテナンス面から以下のライブラリが候補となりました。 fabric konva メドレーでは、歯科診療所向けに電子カルテ Dentis を開発しており、Dentis では既に手書き機能を提供しています。そこで、手書き機能の実装に関して知見を持っている Dentis のエンジニアにライブラリ選定について相談したところ、以下の理由で fabric を採用することに決めました。 konva では線が荒くなってしまうことがある https://github.com/konvajs/konva/issues/1144 で解消方法も紹介されていましたが、細かく線を書きたいケースに対応できない konva では線を描くたびに再レンダリングが発生してしまう mousemove や touchevent のデータをキャッシュすることで解決できるが、遅延が気になる また、fabric を使用した手書き機能の実装方法に関しては、2024 年 12 月にメドレーのアドベントカレンダーで吉岡が書いた以下の記事を参考にしてみてください。 React+fabric.jsで作る手書きアプリ zenn.dev 手書き機能で工夫したポイント 4 選 工夫 1. 手を画面につけながらペンで手書きできるようにした 画面に手をつけながらペンで書いたときに、以下のように線が乱れてしまうことがありました。 画面に接触した手はペンや指で書いたときよりも接触面積が大きくなるので、接触部分が一定よりも大きくなった場合には線を書けないように実装しました。具体的には、 radiusX プロパティを使用して、radiusX がある値より大きいときには、 isDrawingMode が false になるようにしました。 canvas . on ( "mouse:down:before" , ( e ) => { if ( "TouchEvent" in window && e . e instanceof TouchEvent ) { const touch = e . e . touches . item ( 0 ); if (! touch ) { return ; } // 接触面積がペンや指よりも大きいときに線を書けなくする if ( touch . radiusX > 35 ) { canvas . isDrawingMode = false ; } } }); // 画面から手を離す度にisDrawingModeをリセット canvas . on ( "mouse:up:before" , () => { canvas . isDrawingMode = true ; }); 工夫 2. 消しゴムで線を消すときのラグを解消した 消しゴム機能は erase2d ライブラリを使って実装しました。 fabric v5 までは消しゴム機能が内包されていましたが、fabric v6 から erase2d に切り出されました。 erase2d を使って消しゴムを実装すると、次の動画のようにユーザビリティを大きく損なうほどのラグが発生していました。 このラグは PC では発生せず、iPad でのみ発生していました。 この現象について原因を調査したところ、以下の 2 点が原因であることが分かりました。 原因 1. Retina ディスプレイによるピクセル密度の違い iPad は Retina ディスプレイを搭載しており、ピクセル密度が通常のディスプレイよりも高くなっていました。 検証に利用していた 11 インチ iPad Air (M2) では、PC と比較してピクセル密度が 4 倍にもなっていました。 原因 2. ピクセル密度に依存した消しゴム機能特有の処理 消しゴム機能は、ペン機能と比較して複雑な処理が行われていました。 ペンは 1 つのキャンバスに線を描くだけであるのに対して、消しゴムは複数のキャンバスを扱い、それらを合成する処理が必要でした。 具体的には、消しゴムの軌跡を一時的なキャンバスに描画し、それをメインキャンバスに合成するといった処理等が行われていました。 これは、ピクセル密度に依存しており、ピクセル密度が高い環境では負荷が高くなっていました。 // https://github.com/ShaMan123/erase2d/blob/7ee2b789b4c9161d662ea8c8aaf87af4c37bab13/packages/core/src/erase.ts#L15-L40 export const erase = ( destination : CanvasRenderingContext2D , // メインキャンバス source : CanvasRenderingContext2D , // 消しゴムの軌跡が描かれたキャンバス erasingEffect ?: CanvasRenderingContext2D // 消したくないものを保護するためのキャンバス ) => { // メインキャンバスから、消しゴムの軌跡部分を消去 drawImage ( destination , source , "destination-out" ); if ( erasingEffect ) { // 消しゴムの軌跡に、消したくないものがあれば、消さずに保護 drawImage ( source , erasingEffect , "source-in" ); } else { // 消しゴムの軌跡を描いたキャンバスを初期化 source . save (); source . resetTransform (); source . clearRect ( 0 , 0 , source . canvas . width , source . canvas . height ); source . restore (); } }; これらの原因によって、Retina ディスプレイの高解像度環境では消しゴムで線を消すときにラグが発生していました。 解決策として Canvas の初期化時に enableRetinaScaling オプションを false に設定し、Retina ディスプレイの高解像度環境においても同じピクセル密度で処理を行うようにしました。 const canvas = new fabric . Canvas ( canvasElement , { enableRetinaScaling: false , }); この設定により、一定の解像度を担保しつつ、iPad でも滑らかな消しゴム操作を実現できました。 工夫 3. iPad で手書きして保存した画像を PC に即時反映されるようにした iPad で手書きしながら PC でカルテを使うということを想定しているため、iPad で保存した手書き画像は PC に即時反映される必要があります。こちらを実現するために、以下が候補に挙げられました。 SSE(Server-Sent Events) WebSocket Firebase Realtime Database Firebase Realtime Database を既に使用しており、今回保存するのは更新時間のみで、保存容量が大幅に増えることもないため、今回は Firebase Realtime Database を使用することにしました。 以下が、Realtime Database で変更の監視及び通知を行う実装です。 import { initializeApp } from "firebase/app" ; import { getDatabase , off , onValue , ref , set } from "firebase/database" ; const app = initializeApp ( { /* 省略’*/ }, "firebaseapp" ); const db = () => { return getDatabase ( app ); }; const PATH = "medicalRecordChiefComplaintImages" ; // refを生成 const medicalRecordChiefComplaintImagesRef = ( medicalRecordId : string ) => { return ref ( db (), `/ ${ PATH } / ${ medicalRecordId } ` ); }; // 主訴所見の手書き画像が更新されたことを通知する export const updateMedicalRecordChiefComplaintImageIds = async ({ medicalRecordId , }: { medicalRecordId : string ; }) => { await set ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), Date . now ()); }; // 主訴所見の手書き画像の変更を監視する export const watchMedicalRecordChiefComplaintImages = ({ medicalRecordId , refreshMedicalRecordChiefComplaintImages , }: { medicalRecordId : string ; refreshMedicalRecordChiefComplaintImages : () => void ; }) => { onValue ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), () => { // Realtime Databaseが更新されたときに実行する処理 refreshMedicalRecordChiefComplaintImages (); }); }; // 主訴所見の手書き画像の変更の監視を解除する export const unwatchMedicalRecordChiefComplaintImages = ({ medicalRecordId , }: { medicalRecordId : string ; }) => { off ( medicalRecordChiefComplaintImagesRef ( medicalRecordId )); }; フロントエンドでは、データフェッチに TanStack Query を使用しています。以下のように Realtime Database によって手書き画像の更新を監視して refetch しています。 export const useMedicalChiefComplaintImagesWatchQuery = ({ medicalRecordId , initialChiefComplaintImages , }: UseMedicalChiefComplaintImagesWatchQueryProps ) => { const medicalChiefComplaintImagesQuery = useQuery ({ queryKey: medicalChiefComplaintImageKey . list ( medicalRecordId ), queryFn : () => query . get ( request . get ( medicalRecordId )), }); useEffect (() => { watchMedicalRecordChiefComplaintImages ({ medicalRecordId: medicalRecordId , // Realtime Databaseの更新時にrefetchする refreshMedicalRecordChiefComplaintImages: medicalChiefComplaintImagesQuery . refetch , }); return () => unwatchMedicalRecordChiefComplaintImages ({ medicalRecordId }); }, [ medicalRecordId , medicalChiefComplaintImagesQuery . refetch ]); return medicalChiefComplaintImagesQuery ; }; 最後に、iPad で手書き画像を更新したタイミングで updateMedicalRecordChiefComplaintImageIds を呼び出せば、以下のように iPad での変更が PC に即時に反映されるようになります。 工夫 4. 手書きツールバーを自由に配置できるようにした 手書き機能では太さや色の変更、ペンと消しゴムの切り替え、履歴管理ができるようにツールバーを実装しています。ツールバーは頻繁に使われるため、ユーザーが使用しやすい位置に移動できるようにしました。こちらは dnd-kit を使用して実装しました。 動画にあるようにツールバーが親要素からはみ出ないようになっています。こちらは restrictToParentElement を使用することで実現できます。 また、CSS ライブラリに emotion を使用しており、今回の Draggable 対応では動的にスタイルを変更する必要があるため、 style prop を使うように注意する必要があります( https://emotion.sh/docs/best-practices#use-the-style-prop-for-dynamic-styles )。 ライブラリの不具合 上記のようにユーザーが使いやすいように手書き機能を開発しましたが、消しゴムで線を消すと消えて欲しくない背景画像まで消えてしまうという不具合がライブラリに存在していました。こちらは同じ fabric を使用している Dentis のエンジニア大岡に相談したところ、ライブラリに PR を出して解消していただけました! fix(): backgroundImage/overlayImage erasable by yasupeke · Pull Request #49 · ShaMan123/erase2d I always appreciate the convenience of using this tool. I have fixed an issue where the erasable setting for backgroundImage/overlayImage was not being applied. Please review the changes. github.com まとめ 本記事では、Web でユーザビリティの高い手書きアプリケーションを実装するための工夫点について紹介しました。手書き機能の実装においては、実装速度とユーザビリティを両立するために適切なライブラリ選定が重要です。私達は fabric を採用することで滑らかな手書きアプリケーションを実現できました。 実装にあたり、以下の 4 つを工夫しました。 手を画面に置きながら書ける機能: 接触面積の大きさを検出してペン入力と区別することで、自然な書き心地を実現しました 消しゴム操作のラグ解消: Retina ディスプレイ対応を最適化し、高負荷な消しゴム処理でもスムーズな操作感を実現しました リアルタイム同期: Firebase Realtime Database を活用し、複数デバイス間での即時反映を実現しました 柔軟な UI: dnd-kit によるドラッグ可能なツールバーで、ユーザー体験を向上しました また、ライブラリの不具合に対してはプロダクトを横断したコミュニケーションやコミュニティと協力することで解決しました。これらの工夫により、医科診療所で快適に使用できる手書き機能を開発することができました。 今後もユーザビリティを重視しながら、より使いやすい機能の開発を続けていきます。 We’re hiring メドレーでは一緒に働く仲間を大募集しています! カジュアル面談も実施しておりますので、「お話だけでも聞いてみたい!」「ちょっと雑談してみたい!」でも構いませんので、お気軽にお問い合わせください! 募集の一覧 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp 医療エンジニアリング領域盛り上がっています!メドレーについてお話します! メドレーの人材プラットフォーム事業についてお話しします! 「メドレーの人材プラットフォーム事業についてお話しします!」- 倉林 昭和さん pitta.me メドレーの開発チームについて知りたい方!ぜひお話ししましょう! メドレーの開発チームについて知りたい方!ぜひお話ししましょう! 「メドレーの開発チームについて知りたい方!ぜひお話ししましょう!」- 德永 諒介さん pitta.me
はじめに こんにちは。医療プラットフォーム本部 CLINICS 開発グループの吉岡( @yuya333_ )と村上( @yuporonM )です。 吉岡は 2022 年に、村上は 2024 年に新卒でエンジニアとしてメドレーに入社しました。 私たちは、医科診療所向けの電子カルテである CLINICS カルテ を開発しています。CLINICS カルテは 2025 年 3 月に iPad 手書き機能をリリースしました。iPad 手書き機能とは、iPad を使って主訴・所見やシェーマ(身体の部位の絵)を手書きできる機能です。 主訴・所見の手書き シェーマの手書き 本記事では iPad 手書き機能を開発するにあたって工夫した点を紹介します。 ライブラリ選定 手書き機能を開発するにあたって、手書き処理すべてを実装するのは工数の観点から現実的ではありませんでした。 そのため、要件を満たせるかつメンテナンス面から以下のライブラリが候補となりました。 fabric konva メドレーでは、歯科診療所向けに電子カルテ Dentis を開発しており、Dentis では既に手書き機能を提供しています。そこで、手書き機能の実装に関して知見を持っている Dentis のエンジニアにライブラリ選定について相談したところ、以下の理由で fabric を採用することに決めました。 konva では線が荒くなってしまうことがある https://github.com/konvajs/konva/issues/1144 で解消方法も紹介されていましたが、細かく線を書きたいケースに対応できない konva では線を描くたびに再レンダリングが発生してしまう mousemove や touchevent のデータをキャッシュすることで解決できるが、遅延が気になる また、fabric を使用した手書き機能の実装方法に関しては、2024 年 12 月にメドレーのアドベントカレンダーで吉岡が書いた以下の記事を参考にしてみてください。 React+fabric.jsで作る手書きアプリ zenn.dev 手書き機能で工夫したポイント 4 選 工夫 1. 手を画面につけながらペンで手書きできるようにした 画面に手をつけながらペンで書いたときに、以下のように線が乱れてしまうことがありました。 画面に接触した手はペンや指で書いたときよりも接触面積が大きくなるので、接触部分が一定よりも大きくなった場合には線を書けないように実装しました。具体的には、 radiusX プロパティを使用して、radiusX がある値より大きいときには、 isDrawingMode が false になるようにしました。 canvas . on ( "mouse:down:before" , ( e ) => { if ( "TouchEvent" in window && e . e instanceof TouchEvent ) { const touch = e . e . touches . item ( 0 ); if (! touch ) { return ; } // 接触面積がペンや指よりも大きいときに線を書けなくする if ( touch . radiusX > 35 ) { canvas . isDrawingMode = false ; } } }); // 画面から手を離す度にisDrawingModeをリセット canvas . on ( "mouse:up:before" , () => { canvas . isDrawingMode = true ; }); 工夫 2. 消しゴムで線を消すときのラグを解消した 消しゴム機能は erase2d ライブラリを使って実装しました。 fabric v5 までは消しゴム機能が内包されていましたが、fabric v6 から erase2d に切り出されました。 erase2d を使って消しゴムを実装すると、次の動画のようにユーザビリティを大きく損なうほどのラグが発生していました。 このラグは PC では発生せず、iPad でのみ発生していました。 この現象について原因を調査したところ、以下の 2 点が原因であることが分かりました。 原因 1. Retina ディスプレイによるピクセル密度の違い iPad は Retina ディスプレイを搭載しており、ピクセル密度が通常のディスプレイよりも高くなっていました。 検証に利用していた 11 インチ iPad Air (M2) では、PC と比較してピクセル密度が 4 倍にもなっていました。 原因 2. ピクセル密度に依存した消しゴム機能特有の処理 消しゴム機能は、ペン機能と比較して複雑な処理が行われていました。 ペンは 1 つのキャンバスに線を描くだけであるのに対して、消しゴムは複数のキャンバスを扱い、それらを合成する処理が必要でした。 具体的には、消しゴムの軌跡を一時的なキャンバスに描画し、それをメインキャンバスに合成するといった処理等が行われていました。 これは、ピクセル密度に依存しており、ピクセル密度が高い環境では負荷が高くなっていました。 // https://github.com/ShaMan123/erase2d/blob/7ee2b789b4c9161d662ea8c8aaf87af4c37bab13/packages/core/src/erase.ts#L15-L40 export const erase = ( destination : CanvasRenderingContext2D , // メインキャンバス source : CanvasRenderingContext2D , // 消しゴムの軌跡が描かれたキャンバス erasingEffect ?: CanvasRenderingContext2D // 消したくないものを保護するためのキャンバス ) => { // メインキャンバスから、消しゴムの軌跡部分を消去 drawImage ( destination , source , "destination-out" ); if ( erasingEffect ) { // 消しゴムの軌跡に、消したくないものがあれば、消さずに保護 drawImage ( source , erasingEffect , "source-in" ); } else { // 消しゴムの軌跡を描いたキャンバスを初期化 source . save (); source . resetTransform (); source . clearRect ( 0 , 0 , source . canvas . width , source . canvas . height ); source . restore (); } }; これらの原因によって、Retina ディスプレイの高解像度環境では消しゴムで線を消すときにラグが発生していました。 解決策として Canvas の初期化時に enableRetinaScaling オプションを false に設定し、Retina ディスプレイの高解像度環境においても同じピクセル密度で処理を行うようにしました。 const canvas = new fabric . Canvas ( canvasElement , { enableRetinaScaling: false , }); この設定により、一定の解像度を担保しつつ、iPad でも滑らかな消しゴム操作を実現できました。 工夫 3. iPad で手書きして保存した画像を PC に即時反映されるようにした iPad で手書きしながら PC でカルテを使うということを想定しているため、iPad で保存した手書き画像は PC に即時反映される必要があります。こちらを実現するために、以下が候補に挙げられました。 SSE(Server-Sent Events) WebSocket Firebase Realtime Database Firebase Realtime Database を既に使用しており、今回保存するのは更新時間のみで、保存容量が大幅に増えることもないため、今回は Firebase Realtime Database を使用することにしました。 以下が、Realtime Database で変更の監視及び通知を行う実装です。 import { initializeApp } from "firebase/app" ; import { getDatabase , off , onValue , ref , set } from "firebase/database" ; const app = initializeApp ( { /* 省略’*/ }, "firebaseapp" ); const db = () => { return getDatabase ( app ); }; const PATH = "medicalRecordChiefComplaintImages" ; // refを生成 const medicalRecordChiefComplaintImagesRef = ( medicalRecordId : string ) => { return ref ( db (), `/ ${ PATH } / ${ medicalRecordId } ` ); }; // 主訴所見の手書き画像が更新されたことを通知する export const updateMedicalRecordChiefComplaintImageIds = async ({ medicalRecordId , }: { medicalRecordId : string ; }) => { await set ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), Date . now ()); }; // 主訴所見の手書き画像の変更を監視する export const watchMedicalRecordChiefComplaintImages = ({ medicalRecordId , refreshMedicalRecordChiefComplaintImages , }: { medicalRecordId : string ; refreshMedicalRecordChiefComplaintImages : () => void ; }) => { onValue ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), () => { // Realtime Databaseが更新されたときに実行する処理 refreshMedicalRecordChiefComplaintImages (); }); }; // 主訴所見の手書き画像の変更の監視を解除する export const unwatchMedicalRecordChiefComplaintImages = ({ medicalRecordId , }: { medicalRecordId : string ; }) => { off ( medicalRecordChiefComplaintImagesRef ( medicalRecordId )); }; フロントエンドでは、データフェッチに TanStack Query を使用しています。以下のように Realtime Database によって手書き画像の更新を監視して refetch しています。 export const useMedicalChiefComplaintImagesWatchQuery = ({ medicalRecordId , initialChiefComplaintImages , }: UseMedicalChiefComplaintImagesWatchQueryProps ) => { const medicalChiefComplaintImagesQuery = useQuery ({ queryKey: medicalChiefComplaintImageKey . list ( medicalRecordId ), queryFn : () => query . get ( request . get ( medicalRecordId )), }); useEffect (() => { watchMedicalRecordChiefComplaintImages ({ medicalRecordId: medicalRecordId , // Realtime Databaseの更新時にrefetchする refreshMedicalRecordChiefComplaintImages: medicalChiefComplaintImagesQuery . refetch , }); return () => unwatchMedicalRecordChiefComplaintImages ({ medicalRecordId }); }, [ medicalRecordId , medicalChiefComplaintImagesQuery . refetch ]); return medicalChiefComplaintImagesQuery ; }; 最後に、iPad で手書き画像を更新したタイミングで updateMedicalRecordChiefComplaintImageIds を呼び出せば、以下のように iPad での変更が PC に即時に反映されるようになります。 工夫 4. 手書きツールバーを自由に配置できるようにした 手書き機能では太さや色の変更、ペンと消しゴムの切り替え、履歴管理ができるようにツールバーを実装しています。ツールバーは頻繁に使われるため、ユーザーが使用しやすい位置に移動できるようにしました。こちらは dnd-kit を使用して実装しました。 動画にあるようにツールバーが親要素からはみ出ないようになっています。こちらは restrictToParentElement を使用することで実現できます。 また、CSS ライブラリに emotion を使用しており、今回の Draggable 対応では動的にスタイルを変更する必要があるため、 style prop を使うように注意する必要があります( https://emotion.sh/docs/best-practices#use-the-style-prop-for-dynamic-styles )。 ライブラリの不具合 上記のようにユーザーが使いやすいように手書き機能を開発しましたが、消しゴムで線を消すと消えて欲しくない背景画像まで消えてしまうという不具合がライブラリに存在していました。こちらは同じ fabric を使用している Dentis のエンジニア大岡に相談したところ、ライブラリに PR を出して解消していただけました! fix(): backgroundImage/overlayImage erasable by yasupeke · Pull Request #49 · ShaMan123/erase2d I always appreciate the convenience of using this tool. I have fixed an issue where the erasable setting for backgroundImage/overlayImage was not being applied. Please review the changes. github.com まとめ 本記事では、Web でユーザビリティの高い手書きアプリケーションを実装するための工夫点について紹介しました。手書き機能の実装においては、実装速度とユーザビリティを両立するために適切なライブラリ選定が重要です。私達は fabric を採用することで滑らかな手書きアプリケーションを実現できました。 実装にあたり、以下の 4 つを工夫しました。 手を画面に置きながら書ける機能: 接触面積の大きさを検出してペン入力と区別することで、自然な書き心地を実現しました 消しゴム操作のラグ解消: Retina ディスプレイ対応を最適化し、高負荷な消しゴム処理でもスムーズな操作感を実現しました リアルタイム同期: Firebase Realtime Database を活用し、複数デバイス間での即時反映を実現しました 柔軟な UI: dnd-kit によるドラッグ可能なツールバーで、ユーザー体験を向上しました また、ライブラリの不具合に対してはプロダクトを横断したコミュニケーションやコミュニティと協力することで解決しました。これらの工夫により、医科診療所で快適に使用できる手書き機能を開発することができました。 今後もユーザビリティを重視しながら、より使いやすい機能の開発を続けていきます。 We’re hiring メドレーでは一緒に働く仲間を大募集しています! カジュアル面談も実施しておりますので、「お話だけでも聞いてみたい!」「ちょっと雑談してみたい!」でも構いませんので、お気軽にお問い合わせください! 募集の一覧 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp 医療エンジニアリング領域盛り上がっています!メドレーについてお話します! メドレーの人材プラットフォーム事業についてお話しします! 「メドレーの人材プラットフォーム事業についてお話しします!」- 倉林 昭和さん pitta.me メドレーの開発チームについて知りたい方!ぜひお話ししましょう! メドレーの開発チームについて知りたい方!ぜひお話ししましょう! 「メドレーの開発チームについて知りたい方!ぜひお話ししましょう!」- 德永 諒介さん pitta.me
はじめに こんにちは。医療プラットフォーム本部 CLINICS 開発グループの吉岡( @yuya333_ )と村上( @yuporonM )です。 吉岡は 2022 年に、村上は 2024 年に新卒でエンジニアとしてメドレーに入社しました。 私たちは、医科診療所向けの電子カルテである CLINICS カルテ を開発しています。CLINICS カルテは 2025 年 3 月に iPad 手書き機能をリリースしました。iPad 手書き機能とは、iPad を使って主訴・所見やシェーマ(身体の部位の絵)を手書きできる機能です。 主訴・所見の手書き シェーマの手書き 本記事では iPad 手書き機能を開発するにあたって工夫した点を紹介します。 ライブラリ選定 手書き機能を開発するにあたって、手書き処理すべてを実装するのは工数の観点から現実的ではありませんでした。 そのため、要件を満たせるかつメンテナンス面から以下のライブラリが候補となりました。 fabric konva メドレーでは、歯科診療所向けに電子カルテ Dentis を開発しており、Dentis では既に手書き機能を提供しています。そこで、手書き機能の実装に関して知見を持っている Dentis のエンジニアにライブラリ選定について相談したところ、以下の理由で fabric を採用することに決めました。 konva では線が荒くなってしまうことがある https://github.com/konvajs/konva/issues/1144 で解消方法も紹介されていましたが、細かく線を書きたいケースに対応できない konva では線を描くたびに再レンダリングが発生してしまう mousemove や touchevent のデータをキャッシュすることで解決できるが、遅延が気になる また、fabric を使用した手書き機能の実装方法に関しては、2024 年 12 月にメドレーのアドベントカレンダーで吉岡が書いた以下の記事を参考にしてみてください。 React+fabric.jsで作る手書きアプリ zenn.dev 手書き機能で工夫したポイント 4 選 工夫 1. 手を画面につけながらペンで手書きできるようにした 画面に手をつけながらペンで書いたときに、以下のように線が乱れてしまうことがありました。 画面に接触した手はペンや指で書いたときよりも接触面積が大きくなるので、接触部分が一定よりも大きくなった場合には線を書けないように実装しました。具体的には、 radiusX プロパティを使用して、radiusX がある値より大きいときには、 isDrawingMode が false になるようにしました。 canvas . on ( "mouse:down:before" , ( e ) => { if ( "TouchEvent" in window && e . e instanceof TouchEvent ) { const touch = e . e . touches . item ( 0 ); if (! touch ) { return ; } // 接触面積がペンや指よりも大きいときに線を書けなくする if ( touch . radiusX > 35 ) { canvas . isDrawingMode = false ; } } }); // 画面から手を離す度にisDrawingModeをリセット canvas . on ( "mouse:up:before" , () => { canvas . isDrawingMode = true ; }); 工夫 2. 消しゴムで線を消すときのラグを解消した 消しゴム機能は erase2d ライブラリを使って実装しました。 fabric v5 までは消しゴム機能が内包されていましたが、fabric v6 から erase2d に切り出されました。 erase2d を使って消しゴムを実装すると、次の動画のようにユーザビリティを大きく損なうほどのラグが発生していました。 このラグは PC では発生せず、iPad でのみ発生していました。 この現象について原因を調査したところ、以下の 2 点が原因であることが分かりました。 原因 1. Retina ディスプレイによるピクセル密度の違い iPad は Retina ディスプレイを搭載しており、ピクセル密度が通常のディスプレイよりも高くなっていました。 検証に利用していた 11 インチ iPad Air (M2) では、PC と比較してピクセル密度が 4 倍にもなっていました。 原因 2. ピクセル密度に依存した消しゴム機能特有の処理 消しゴム機能は、ペン機能と比較して複雑な処理が行われていました。 ペンは 1 つのキャンバスに線を描くだけであるのに対して、消しゴムは複数のキャンバスを扱い、それらを合成する処理が必要でした。 具体的には、消しゴムの軌跡を一時的なキャンバスに描画し、それをメインキャンバスに合成するといった処理等が行われていました。 これは、ピクセル密度に依存しており、ピクセル密度が高い環境では負荷が高くなっていました。 // https://github.com/ShaMan123/erase2d/blob/7ee2b789b4c9161d662ea8c8aaf87af4c37bab13/packages/core/src/erase.ts#L15-L40 export const erase = ( destination : CanvasRenderingContext2D , // メインキャンバス source : CanvasRenderingContext2D , // 消しゴムの軌跡が描かれたキャンバス erasingEffect ?: CanvasRenderingContext2D // 消したくないものを保護するためのキャンバス ) => { // メインキャンバスから、消しゴムの軌跡部分を消去 drawImage ( destination , source , "destination-out" ); if ( erasingEffect ) { // 消しゴムの軌跡に、消したくないものがあれば、消さずに保護 drawImage ( source , erasingEffect , "source-in" ); } else { // 消しゴムの軌跡を描いたキャンバスを初期化 source . save (); source . resetTransform (); source . clearRect ( 0 , 0 , source . canvas . width , source . canvas . height ); source . restore (); } }; これらの原因によって、Retina ディスプレイの高解像度環境では消しゴムで線を消すときにラグが発生していました。 解決策として Canvas の初期化時に enableRetinaScaling オプションを false に設定し、Retina ディスプレイの高解像度環境においても同じピクセル密度で処理を行うようにしました。 const canvas = new fabric . Canvas ( canvasElement , { enableRetinaScaling: false , }); この設定により、一定の解像度を担保しつつ、iPad でも滑らかな消しゴム操作を実現できました。 工夫 3. iPad で手書きして保存した画像を PC に即時反映されるようにした iPad で手書きしながら PC でカルテを使うということを想定しているため、iPad で保存した手書き画像は PC に即時反映される必要があります。こちらを実現するために、以下が候補に挙げられました。 SSE(Server-Sent Events) WebSocket Firebase Realtime Database Firebase Realtime Database を既に使用しており、今回保存するのは更新時間のみで、保存容量が大幅に増えることもないため、今回は Firebase Realtime Database を使用することにしました。 以下が、Realtime Database で変更の監視及び通知を行う実装です。 import { initializeApp } from "firebase/app" ; import { getDatabase , off , onValue , ref , set } from "firebase/database" ; const app = initializeApp ( { /* 省略’*/ }, "firebaseapp" ); const db = () => { return getDatabase ( app ); }; const PATH = "medicalRecordChiefComplaintImages" ; // refを生成 const medicalRecordChiefComplaintImagesRef = ( medicalRecordId : string ) => { return ref ( db (), `/ ${ PATH } / ${ medicalRecordId } ` ); }; // 主訴所見の手書き画像が更新されたことを通知する export const updateMedicalRecordChiefComplaintImageIds = async ({ medicalRecordId , }: { medicalRecordId : string ; }) => { await set ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), Date . now ()); }; // 主訴所見の手書き画像の変更を監視する export const watchMedicalRecordChiefComplaintImages = ({ medicalRecordId , refreshMedicalRecordChiefComplaintImages , }: { medicalRecordId : string ; refreshMedicalRecordChiefComplaintImages : () => void ; }) => { onValue ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), () => { // Realtime Databaseが更新されたときに実行する処理 refreshMedicalRecordChiefComplaintImages (); }); }; // 主訴所見の手書き画像の変更の監視を解除する export const unwatchMedicalRecordChiefComplaintImages = ({ medicalRecordId , }: { medicalRecordId : string ; }) => { off ( medicalRecordChiefComplaintImagesRef ( medicalRecordId )); }; フロントエンドでは、データフェッチに TanStack Query を使用しています。以下のように Realtime Database によって手書き画像の更新を監視して refetch しています。 export const useMedicalChiefComplaintImagesWatchQuery = ({ medicalRecordId , initialChiefComplaintImages , }: UseMedicalChiefComplaintImagesWatchQueryProps ) => { const medicalChiefComplaintImagesQuery = useQuery ({ queryKey: medicalChiefComplaintImageKey . list ( medicalRecordId ), queryFn : () => query . get ( request . get ( medicalRecordId )), }); useEffect (() => { watchMedicalRecordChiefComplaintImages ({ medicalRecordId: medicalRecordId , // Realtime Databaseの更新時にrefetchする refreshMedicalRecordChiefComplaintImages: medicalChiefComplaintImagesQuery . refetch , }); return () => unwatchMedicalRecordChiefComplaintImages ({ medicalRecordId }); }, [ medicalRecordId , medicalChiefComplaintImagesQuery . refetch ]); return medicalChiefComplaintImagesQuery ; }; 最後に、iPad で手書き画像を更新したタイミングで updateMedicalRecordChiefComplaintImageIds を呼び出せば、以下のように iPad での変更が PC に即時に反映されるようになります。 工夫 4. 手書きツールバーを自由に配置できるようにした 手書き機能では太さや色の変更、ペンと消しゴムの切り替え、履歴管理ができるようにツールバーを実装しています。ツールバーは頻繁に使われるため、ユーザーが使用しやすい位置に移動できるようにしました。こちらは dnd-kit を使用して実装しました。 動画にあるようにツールバーが親要素からはみ出ないようになっています。こちらは restrictToParentElement を使用することで実現できます。 また、CSS ライブラリに emotion を使用しており、今回の Draggable 対応では動的にスタイルを変更する必要があるため、 style prop を使うように注意する必要があります( https://emotion.sh/docs/best-practices#use-the-style-prop-for-dynamic-styles )。 ライブラリの不具合 上記のようにユーザーが使いやすいように手書き機能を開発しましたが、消しゴムで線を消すと消えて欲しくない背景画像まで消えてしまうという不具合がライブラリに存在していました。こちらは同じ fabric を使用している Dentis のエンジニア大岡に相談したところ、ライブラリに PR を出して解消していただけました! fix(): backgroundImage/overlayImage erasable by yasupeke · Pull Request #49 · ShaMan123/erase2d I always appreciate the convenience of using this tool. I have fixed an issue where the erasable setting for backgroundImage/overlayImage was not being applied. Please review the changes. github.com まとめ 本記事では、Web でユーザビリティの高い手書きアプリケーションを実装するための工夫点について紹介しました。手書き機能の実装においては、実装速度とユーザビリティを両立するために適切なライブラリ選定が重要です。私達は fabric を採用することで滑らかな手書きアプリケーションを実現できました。 実装にあたり、以下の 4 つを工夫しました。 手を画面に置きながら書ける機能: 接触面積の大きさを検出してペン入力と区別することで、自然な書き心地を実現しました 消しゴム操作のラグ解消: Retina ディスプレイ対応を最適化し、高負荷な消しゴム処理でもスムーズな操作感を実現しました リアルタイム同期: Firebase Realtime Database を活用し、複数デバイス間での即時反映を実現しました 柔軟な UI: dnd-kit によるドラッグ可能なツールバーで、ユーザー体験を向上しました また、ライブラリの不具合に対してはプロダクトを横断したコミュニケーションやコミュニティと協力することで解決しました。これらの工夫により、医科診療所で快適に使用できる手書き機能を開発することができました。 今後もユーザビリティを重視しながら、より使いやすい機能の開発を続けていきます。 We’re hiring メドレーでは一緒に働く仲間を大募集しています! カジュアル面談も実施しておりますので、「お話だけでも聞いてみたい!」「ちょっと雑談してみたい!」でも構いませんので、お気軽にお問い合わせください! 募集の一覧 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp 医療エンジニアリング領域盛り上がっています!メドレーについてお話します! メドレーの人材プラットフォーム事業についてお話しします! 「メドレーの人材プラットフォーム事業についてお話しします!」- 倉林 昭和さん pitta.me メドレーの開発チームについて知りたい方!ぜひお話ししましょう! メドレーの開発チームについて知りたい方!ぜひお話ししましょう! 「メドレーの開発チームについて知りたい方!ぜひお話ししましょう!」- 德永 諒介さん pitta.me
はじめに こんにちは。医療プラットフォーム本部 CLINICS 開発グループの吉岡( @yuya333_ )と村上( @yuporonM )です。 吉岡は 2022 年に、村上は 2024 年に新卒でエンジニアとしてメドレーに入社しました。 私たちは、医科診療所向けの電子カルテである CLINICS カルテ を開発しています。CLINICS カルテは 2025 年 3 月に iPad 手書き機能をリリースしました。iPad 手書き機能とは、iPad を使って主訴・所見やシェーマ(身体の部位の絵)を手書きできる機能です。 主訴・所見の手書き シェーマの手書き 本記事では iPad 手書き機能を開発するにあたって工夫した点を紹介します。 ライブラリ選定 手書き機能を開発するにあたって、手書き処理すべてを実装するのは工数の観点から現実的ではありませんでした。 そのため、要件を満たせるかつメンテナンス面から以下のライブラリが候補となりました。 fabric konva メドレーでは、歯科診療所向けに電子カルテ Dentis を開発しており、Dentis では既に手書き機能を提供しています。そこで、手書き機能の実装に関して知見を持っている Dentis のエンジニアにライブラリ選定について相談したところ、以下の理由で fabric を採用することに決めました。 konva では線が荒くなってしまうことがある https://github.com/konvajs/konva/issues/1144 で解消方法も紹介されていましたが、細かく線を書きたいケースに対応できない konva では線を描くたびに再レンダリングが発生してしまう mousemove や touchevent のデータをキャッシュすることで解決できるが、遅延が気になる また、fabric を使用した手書き機能の実装方法に関しては、2024 年 12 月にメドレーのアドベントカレンダーで吉岡が書いた以下の記事を参考にしてみてください。 React+fabric.jsで作る手書きアプリ zenn.dev 手書き機能で工夫したポイント 4 選 工夫 1. 手を画面につけながらペンで手書きできるようにした 画面に手をつけながらペンで書いたときに、以下のように線が乱れてしまうことがありました。 画面に接触した手はペンや指で書いたときよりも接触面積が大きくなるので、接触部分が一定よりも大きくなった場合には線を書けないように実装しました。具体的には、 radiusX プロパティを使用して、radiusX がある値より大きいときには、 isDrawingMode が false になるようにしました。 canvas . on ( "mouse:down:before" , ( e ) => { if ( "TouchEvent" in window && e . e instanceof TouchEvent ) { const touch = e . e . touches . item ( 0 ); if (! touch ) { return ; } // 接触面積がペンや指よりも大きいときに線を書けなくする if ( touch . radiusX > 35 ) { canvas . isDrawingMode = false ; } } }); // 画面から手を離す度にisDrawingModeをリセット canvas . on ( "mouse:up:before" , () => { canvas . isDrawingMode = true ; }); 工夫 2. 消しゴムで線を消すときのラグを解消した 消しゴム機能は erase2d ライブラリを使って実装しました。 fabric v5 までは消しゴム機能が内包されていましたが、fabric v6 から erase2d に切り出されました。 erase2d を使って消しゴムを実装すると、次の動画のようにユーザビリティを大きく損なうほどのラグが発生していました。 このラグは PC では発生せず、iPad でのみ発生していました。 この現象について原因を調査したところ、以下の 2 点が原因であることが分かりました。 原因 1. Retina ディスプレイによるピクセル密度の違い iPad は Retina ディスプレイを搭載しており、ピクセル密度が通常のディスプレイよりも高くなっていました。 検証に利用していた 11 インチ iPad Air (M2) では、PC と比較してピクセル密度が 4 倍にもなっていました。 原因 2. ピクセル密度に依存した消しゴム機能特有の処理 消しゴム機能は、ペン機能と比較して複雑な処理が行われていました。 ペンは 1 つのキャンバスに線を描くだけであるのに対して、消しゴムは複数のキャンバスを扱い、それらを合成する処理が必要でした。 具体的には、消しゴムの軌跡を一時的なキャンバスに描画し、それをメインキャンバスに合成するといった処理等が行われていました。 これは、ピクセル密度に依存しており、ピクセル密度が高い環境では負荷が高くなっていました。 // https://github.com/ShaMan123/erase2d/blob/7ee2b789b4c9161d662ea8c8aaf87af4c37bab13/packages/core/src/erase.ts#L15-L40 export const erase = ( destination : CanvasRenderingContext2D , // メインキャンバス source : CanvasRenderingContext2D , // 消しゴムの軌跡が描かれたキャンバス erasingEffect ?: CanvasRenderingContext2D // 消したくないものを保護するためのキャンバス ) => { // メインキャンバスから、消しゴムの軌跡部分を消去 drawImage ( destination , source , "destination-out" ); if ( erasingEffect ) { // 消しゴムの軌跡に、消したくないものがあれば、消さずに保護 drawImage ( source , erasingEffect , "source-in" ); } else { // 消しゴムの軌跡を描いたキャンバスを初期化 source . save (); source . resetTransform (); source . clearRect ( 0 , 0 , source . canvas . width , source . canvas . height ); source . restore (); } }; これらの原因によって、Retina ディスプレイの高解像度環境では消しゴムで線を消すときにラグが発生していました。 解決策として Canvas の初期化時に enableRetinaScaling オプションを false に設定し、Retina ディスプレイの高解像度環境においても同じピクセル密度で処理を行うようにしました。 const canvas = new fabric . Canvas ( canvasElement , { enableRetinaScaling: false , }); この設定により、一定の解像度を担保しつつ、iPad でも滑らかな消しゴム操作を実現できました。 工夫 3. iPad で手書きして保存した画像を PC に即時反映されるようにした iPad で手書きしながら PC でカルテを使うということを想定しているため、iPad で保存した手書き画像は PC に即時反映される必要があります。こちらを実現するために、以下が候補に挙げられました。 SSE(Server-Sent Events) WebSocket Firebase Realtime Database Firebase Realtime Database を既に使用しており、今回保存するのは更新時間のみで、保存容量が大幅に増えることもないため、今回は Firebase Realtime Database を使用することにしました。 以下が、Realtime Database で変更の監視及び通知を行う実装です。 import { initializeApp } from "firebase/app" ; import { getDatabase , off , onValue , ref , set } from "firebase/database" ; const app = initializeApp ( { /* 省略’*/ }, "firebaseapp" ); const db = () => { return getDatabase ( app ); }; const PATH = "medicalRecordChiefComplaintImages" ; // refを生成 const medicalRecordChiefComplaintImagesRef = ( medicalRecordId : string ) => { return ref ( db (), `/ ${ PATH } / ${ medicalRecordId } ` ); }; // 主訴所見の手書き画像が更新されたことを通知する export const updateMedicalRecordChiefComplaintImageIds = async ({ medicalRecordId , }: { medicalRecordId : string ; }) => { await set ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), Date . now ()); }; // 主訴所見の手書き画像の変更を監視する export const watchMedicalRecordChiefComplaintImages = ({ medicalRecordId , refreshMedicalRecordChiefComplaintImages , }: { medicalRecordId : string ; refreshMedicalRecordChiefComplaintImages : () => void ; }) => { onValue ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), () => { // Realtime Databaseが更新されたときに実行する処理 refreshMedicalRecordChiefComplaintImages (); }); }; // 主訴所見の手書き画像の変更の監視を解除する export const unwatchMedicalRecordChiefComplaintImages = ({ medicalRecordId , }: { medicalRecordId : string ; }) => { off ( medicalRecordChiefComplaintImagesRef ( medicalRecordId )); }; フロントエンドでは、データフェッチに TanStack Query を使用しています。以下のように Realtime Database によって手書き画像の更新を監視して refetch しています。 export const useMedicalChiefComplaintImagesWatchQuery = ({ medicalRecordId , initialChiefComplaintImages , }: UseMedicalChiefComplaintImagesWatchQueryProps ) => { const medicalChiefComplaintImagesQuery = useQuery ({ queryKey: medicalChiefComplaintImageKey . list ( medicalRecordId ), queryFn : () => query . get ( request . get ( medicalRecordId )), }); useEffect (() => { watchMedicalRecordChiefComplaintImages ({ medicalRecordId: medicalRecordId , // Realtime Databaseの更新時にrefetchする refreshMedicalRecordChiefComplaintImages: medicalChiefComplaintImagesQuery . refetch , }); return () => unwatchMedicalRecordChiefComplaintImages ({ medicalRecordId }); }, [ medicalRecordId , medicalChiefComplaintImagesQuery . refetch ]); return medicalChiefComplaintImagesQuery ; }; 最後に、iPad で手書き画像を更新したタイミングで updateMedicalRecordChiefComplaintImageIds を呼び出せば、以下のように iPad での変更が PC に即時に反映されるようになります。 工夫 4. 手書きツールバーを自由に配置できるようにした 手書き機能では太さや色の変更、ペンと消しゴムの切り替え、履歴管理ができるようにツールバーを実装しています。ツールバーは頻繁に使われるため、ユーザーが使用しやすい位置に移動できるようにしました。こちらは dnd-kit を使用して実装しました。 動画にあるようにツールバーが親要素からはみ出ないようになっています。こちらは restrictToParentElement を使用することで実現できます。 また、CSS ライブラリに emotion を使用しており、今回の Draggable 対応では動的にスタイルを変更する必要があるため、 style prop を使うように注意する必要があります( https://emotion.sh/docs/best-practices#use-the-style-prop-for-dynamic-styles )。 ライブラリの不具合 上記のようにユーザーが使いやすいように手書き機能を開発しましたが、消しゴムで線を消すと消えて欲しくない背景画像まで消えてしまうという不具合がライブラリに存在していました。こちらは同じ fabric を使用している Dentis のエンジニア大岡に相談したところ、ライブラリに PR を出して解消していただけました! fix(): backgroundImage/overlayImage erasable by yasupeke · Pull Request #49 · ShaMan123/erase2d I always appreciate the convenience of using this tool. I have fixed an issue where the erasable setting for backgroundImage/overlayImage was not being applied. Please review the changes. github.com まとめ 本記事では、Web でユーザビリティの高い手書きアプリケーションを実装するための工夫点について紹介しました。手書き機能の実装においては、実装速度とユーザビリティを両立するために適切なライブラリ選定が重要です。私達は fabric を採用することで滑らかな手書きアプリケーションを実現できました。 実装にあたり、以下の 4 つを工夫しました。 手を画面に置きながら書ける機能: 接触面積の大きさを検出してペン入力と区別することで、自然な書き心地を実現しました 消しゴム操作のラグ解消: Retina ディスプレイ対応を最適化し、高負荷な消しゴム処理でもスムーズな操作感を実現しました リアルタイム同期: Firebase Realtime Database を活用し、複数デバイス間での即時反映を実現しました 柔軟な UI: dnd-kit によるドラッグ可能なツールバーで、ユーザー体験を向上しました また、ライブラリの不具合に対してはプロダクトを横断したコミュニケーションやコミュニティと協力することで解決しました。これらの工夫により、医科診療所で快適に使用できる手書き機能を開発することができました。 今後もユーザビリティを重視しながら、より使いやすい機能の開発を続けていきます。 We’re hiring メドレーでは一緒に働く仲間を大募集しています! カジュアル面談も実施しておりますので、「お話だけでも聞いてみたい!」「ちょっと雑談してみたい!」でも構いませんので、お気軽にお問い合わせください! 募集の一覧 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp 医療エンジニアリング領域盛り上がっています!メドレーについてお話します! メドレーの人材プラットフォーム事業についてお話しします! 「メドレーの人材プラットフォーム事業についてお話しします!」- 倉林 昭和さん pitta.me メドレーの開発チームについて知りたい方!ぜひお話ししましょう! メドレーの開発チームについて知りたい方!ぜひお話ししましょう! 「メドレーの開発チームについて知りたい方!ぜひお話ししましょう!」- 德永 諒介さん pitta.me
はじめに こんにちは。医療プラットフォーム本部 CLINICS 開発グループの吉岡( @yuya333_ )と村上( @yuporonM )です。 吉岡は 2022 年に、村上は 2024 年に新卒でエンジニアとしてメドレーに入社しました。 私たちは、医科診療所向けの電子カルテである CLINICS カルテ を開発しています。CLINICS カルテは 2025 年 3 月に iPad 手書き機能をリリースしました。iPad 手書き機能とは、iPad を使って主訴・所見やシェーマ(身体の部位の絵)を手書きできる機能です。 主訴・所見の手書き シェーマの手書き 本記事では iPad 手書き機能を開発するにあたって工夫した点を紹介します。 ライブラリ選定 手書き機能を開発するにあたって、手書き処理すべてを実装するのは工数の観点から現実的ではありませんでした。 そのため、要件を満たせるかつメンテナンス面から以下のライブラリが候補となりました。 fabric konva メドレーでは、歯科診療所向けに電子カルテ Dentis を開発しており、Dentis では既に手書き機能を提供しています。そこで、手書き機能の実装に関して知見を持っている Dentis のエンジニアにライブラリ選定について相談したところ、以下の理由で fabric を採用することに決めました。 konva では線が荒くなってしまうことがある https://github.com/konvajs/konva/issues/1144 で解消方法も紹介されていましたが、細かく線を書きたいケースに対応できない konva では線を描くたびに再レンダリングが発生してしまう mousemove や touchevent のデータをキャッシュすることで解決できるが、遅延が気になる また、fabric を使用した手書き機能の実装方法に関しては、2024 年 12 月にメドレーのアドベントカレンダーで吉岡が書いた以下の記事を参考にしてみてください。 React+fabric.jsで作る手書きアプリ zenn.dev 手書き機能で工夫したポイント 4 選 工夫 1. 手を画面につけながらペンで手書きできるようにした 画面に手をつけながらペンで書いたときに、以下のように線が乱れてしまうことがありました。 画面に接触した手はペンや指で書いたときよりも接触面積が大きくなるので、接触部分が一定よりも大きくなった場合には線を書けないように実装しました。具体的には、 radiusX プロパティを使用して、radiusX がある値より大きいときには、 isDrawingMode が false になるようにしました。 canvas . on ( "mouse:down:before" , ( e ) => { if ( "TouchEvent" in window && e . e instanceof TouchEvent ) { const touch = e . e . touches . item ( 0 ); if (! touch ) { return ; } // 接触面積がペンや指よりも大きいときに線を書けなくする if ( touch . radiusX > 35 ) { canvas . isDrawingMode = false ; } } }); // 画面から手を離す度にisDrawingModeをリセット canvas . on ( "mouse:up:before" , () => { canvas . isDrawingMode = true ; }); 工夫 2. 消しゴムで線を消すときのラグを解消した 消しゴム機能は erase2d ライブラリを使って実装しました。 fabric v5 までは消しゴム機能が内包されていましたが、fabric v6 から erase2d に切り出されました。 erase2d を使って消しゴムを実装すると、次の動画のようにユーザビリティを大きく損なうほどのラグが発生していました。 このラグは PC では発生せず、iPad でのみ発生していました。 この現象について原因を調査したところ、以下の 2 点が原因であることが分かりました。 原因 1. Retina ディスプレイによるピクセル密度の違い iPad は Retina ディスプレイを搭載しており、ピクセル密度が通常のディスプレイよりも高くなっていました。 検証に利用していた 11 インチ iPad Air (M2) では、PC と比較してピクセル密度が 4 倍にもなっていました。 原因 2. ピクセル密度に依存した消しゴム機能特有の処理 消しゴム機能は、ペン機能と比較して複雑な処理が行われていました。 ペンは 1 つのキャンバスに線を描くだけであるのに対して、消しゴムは複数のキャンバスを扱い、それらを合成する処理が必要でした。 具体的には、消しゴムの軌跡を一時的なキャンバスに描画し、それをメインキャンバスに合成するといった処理等が行われていました。 これは、ピクセル密度に依存しており、ピクセル密度が高い環境では負荷が高くなっていました。 // https://github.com/ShaMan123/erase2d/blob/7ee2b789b4c9161d662ea8c8aaf87af4c37bab13/packages/core/src/erase.ts#L15-L40 export const erase = ( destination : CanvasRenderingContext2D , // メインキャンバス source : CanvasRenderingContext2D , // 消しゴムの軌跡が描かれたキャンバス erasingEffect ?: CanvasRenderingContext2D // 消したくないものを保護するためのキャンバス ) => { // メインキャンバスから、消しゴムの軌跡部分を消去 drawImage ( destination , source , "destination-out" ); if ( erasingEffect ) { // 消しゴムの軌跡に、消したくないものがあれば、消さずに保護 drawImage ( source , erasingEffect , "source-in" ); } else { // 消しゴムの軌跡を描いたキャンバスを初期化 source . save (); source . resetTransform (); source . clearRect ( 0 , 0 , source . canvas . width , source . canvas . height ); source . restore (); } }; これらの原因によって、Retina ディスプレイの高解像度環境では消しゴムで線を消すときにラグが発生していました。 解決策として Canvas の初期化時に enableRetinaScaling オプションを false に設定し、Retina ディスプレイの高解像度環境においても同じピクセル密度で処理を行うようにしました。 const canvas = new fabric . Canvas ( canvasElement , { enableRetinaScaling: false , }); この設定により、一定の解像度を担保しつつ、iPad でも滑らかな消しゴム操作を実現できました。 工夫 3. iPad で手書きして保存した画像を PC に即時反映されるようにした iPad で手書きしながら PC でカルテを使うということを想定しているため、iPad で保存した手書き画像は PC に即時反映される必要があります。こちらを実現するために、以下が候補に挙げられました。 SSE(Server-Sent Events) WebSocket Firebase Realtime Database Firebase Realtime Database を既に使用しており、今回保存するのは更新時間のみで、保存容量が大幅に増えることもないため、今回は Firebase Realtime Database を使用することにしました。 以下が、Realtime Database で変更の監視及び通知を行う実装です。 import { initializeApp } from "firebase/app" ; import { getDatabase , off , onValue , ref , set } from "firebase/database" ; const app = initializeApp ( { /* 省略’*/ }, "firebaseapp" ); const db = () => { return getDatabase ( app ); }; const PATH = "medicalRecordChiefComplaintImages" ; // refを生成 const medicalRecordChiefComplaintImagesRef = ( medicalRecordId : string ) => { return ref ( db (), `/ ${ PATH } / ${ medicalRecordId } ` ); }; // 主訴所見の手書き画像が更新されたことを通知する export const updateMedicalRecordChiefComplaintImageIds = async ({ medicalRecordId , }: { medicalRecordId : string ; }) => { await set ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), Date . now ()); }; // 主訴所見の手書き画像の変更を監視する export const watchMedicalRecordChiefComplaintImages = ({ medicalRecordId , refreshMedicalRecordChiefComplaintImages , }: { medicalRecordId : string ; refreshMedicalRecordChiefComplaintImages : () => void ; }) => { onValue ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), () => { // Realtime Databaseが更新されたときに実行する処理 refreshMedicalRecordChiefComplaintImages (); }); }; // 主訴所見の手書き画像の変更の監視を解除する export const unwatchMedicalRecordChiefComplaintImages = ({ medicalRecordId , }: { medicalRecordId : string ; }) => { off ( medicalRecordChiefComplaintImagesRef ( medicalRecordId )); }; フロントエンドでは、データフェッチに TanStack Query を使用しています。以下のように Realtime Database によって手書き画像の更新を監視して refetch しています。 export const useMedicalChiefComplaintImagesWatchQuery = ({ medicalRecordId , initialChiefComplaintImages , }: UseMedicalChiefComplaintImagesWatchQueryProps ) => { const medicalChiefComplaintImagesQuery = useQuery ({ queryKey: medicalChiefComplaintImageKey . list ( medicalRecordId ), queryFn : () => query . get ( request . get ( medicalRecordId )), }); useEffect (() => { watchMedicalRecordChiefComplaintImages ({ medicalRecordId: medicalRecordId , // Realtime Databaseの更新時にrefetchする refreshMedicalRecordChiefComplaintImages: medicalChiefComplaintImagesQuery . refetch , }); return () => unwatchMedicalRecordChiefComplaintImages ({ medicalRecordId }); }, [ medicalRecordId , medicalChiefComplaintImagesQuery . refetch ]); return medicalChiefComplaintImagesQuery ; }; 最後に、iPad で手書き画像を更新したタイミングで updateMedicalRecordChiefComplaintImageIds を呼び出せば、以下のように iPad での変更が PC に即時に反映されるようになります。 工夫 4. 手書きツールバーを自由に配置できるようにした 手書き機能では太さや色の変更、ペンと消しゴムの切り替え、履歴管理ができるようにツールバーを実装しています。ツールバーは頻繁に使われるため、ユーザーが使用しやすい位置に移動できるようにしました。こちらは dnd-kit を使用して実装しました。 動画にあるようにツールバーが親要素からはみ出ないようになっています。こちらは restrictToParentElement を使用することで実現できます。 また、CSS ライブラリに emotion を使用しており、今回の Draggable 対応では動的にスタイルを変更する必要があるため、 style prop を使うように注意する必要があります( https://emotion.sh/docs/best-practices#use-the-style-prop-for-dynamic-styles )。 ライブラリの不具合 上記のようにユーザーが使いやすいように手書き機能を開発しましたが、消しゴムで線を消すと消えて欲しくない背景画像まで消えてしまうという不具合がライブラリに存在していました。こちらは同じ fabric を使用している Dentis のエンジニア大岡に相談したところ、ライブラリに PR を出して解消していただけました! fix(): backgroundImage/overlayImage erasable by yasupeke · Pull Request #49 · ShaMan123/erase2d I always appreciate the convenience of using this tool. I have fixed an issue where the erasable setting for backgroundImage/overlayImage was not being applied. Please review the changes. github.com まとめ 本記事では、Web でユーザビリティの高い手書きアプリケーションを実装するための工夫点について紹介しました。手書き機能の実装においては、実装速度とユーザビリティを両立するために適切なライブラリ選定が重要です。私達は fabric を採用することで滑らかな手書きアプリケーションを実現できました。 実装にあたり、以下の 4 つを工夫しました。 手を画面に置きながら書ける機能: 接触面積の大きさを検出してペン入力と区別することで、自然な書き心地を実現しました 消しゴム操作のラグ解消: Retina ディスプレイ対応を最適化し、高負荷な消しゴム処理でもスムーズな操作感を実現しました リアルタイム同期: Firebase Realtime Database を活用し、複数デバイス間での即時反映を実現しました 柔軟な UI: dnd-kit によるドラッグ可能なツールバーで、ユーザー体験を向上しました また、ライブラリの不具合に対してはプロダクトを横断したコミュニケーションやコミュニティと協力することで解決しました。これらの工夫により、医科診療所で快適に使用できる手書き機能を開発することができました。 今後もユーザビリティを重視しながら、より使いやすい機能の開発を続けていきます。 We’re hiring メドレーでは一緒に働く仲間を大募集しています! カジュアル面談も実施しておりますので、「お話だけでも聞いてみたい!」「ちょっと雑談してみたい!」でも構いませんので、お気軽にお問い合わせください! 募集の一覧 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp 医療エンジニアリング領域盛り上がっています!メドレーについてお話します! メドレーの人材プラットフォーム事業についてお話しします! 「メドレーの人材プラットフォーム事業についてお話しします!」- 倉林 昭和さん pitta.me メドレーの開発チームについて知りたい方!ぜひお話ししましょう! メドレーの開発チームについて知りたい方!ぜひお話ししましょう! 「メドレーの開発チームについて知りたい方!ぜひお話ししましょう!」- 德永 諒介さん pitta.me
はじめに こんにちは。医療プラットフォーム本部 CLINICS 開発グループの吉岡( @yuya333_ )と村上( @yuporonM )です。 吉岡は 2022 年に、村上は 2024 年に新卒でエンジニアとしてメドレーに入社しました。 私たちは、医科診療所向けの電子カルテである CLINICS カルテ を開発しています。CLINICS カルテは 2025 年 3 月に iPad 手書き機能をリリースしました。iPad 手書き機能とは、iPad を使って主訴・所見やシェーマ(身体の部位の絵)を手書きできる機能です。 主訴・所見の手書き シェーマの手書き 本記事では iPad 手書き機能を開発するにあたって工夫した点を紹介します。 ライブラリ選定 手書き機能を開発するにあたって、手書き処理すべてを実装するのは工数の観点から現実的ではありませんでした。 そのため、要件を満たせるかつメンテナンス面から以下のライブラリが候補となりました。 fabric konva メドレーでは、歯科診療所向けに電子カルテ Dentis を開発しており、Dentis では既に手書き機能を提供しています。そこで、手書き機能の実装に関して知見を持っている Dentis のエンジニアにライブラリ選定について相談したところ、以下の理由で fabric を採用することに決めました。 konva では線が荒くなってしまうことがある https://github.com/konvajs/konva/issues/1144 で解消方法も紹介されていましたが、細かく線を書きたいケースに対応できない konva では線を描くたびに再レンダリングが発生してしまう mousemove や touchevent のデータをキャッシュすることで解決できるが、遅延が気になる また、fabric を使用した手書き機能の実装方法に関しては、2024 年 12 月にメドレーのアドベントカレンダーで吉岡が書いた以下の記事を参考にしてみてください。 React+fabric.jsで作る手書きアプリ zenn.dev 手書き機能で工夫したポイント 4 選 工夫 1. 手を画面につけながらペンで手書きできるようにした 画面に手をつけながらペンで書いたときに、以下のように線が乱れてしまうことがありました。 画面に接触した手はペンや指で書いたときよりも接触面積が大きくなるので、接触部分が一定よりも大きくなった場合には線を書けないように実装しました。具体的には、 radiusX プロパティを使用して、radiusX がある値より大きいときには、 isDrawingMode が false になるようにしました。 canvas . on ( "mouse:down:before" , ( e ) => { if ( "TouchEvent" in window && e . e instanceof TouchEvent ) { const touch = e . e . touches . item ( 0 ); if (! touch ) { return ; } // 接触面積がペンや指よりも大きいときに線を書けなくする if ( touch . radiusX > 35 ) { canvas . isDrawingMode = false ; } } }); // 画面から手を離す度にisDrawingModeをリセット canvas . on ( "mouse:up:before" , () => { canvas . isDrawingMode = true ; }); 工夫 2. 消しゴムで線を消すときのラグを解消した 消しゴム機能は erase2d ライブラリを使って実装しました。 fabric v5 までは消しゴム機能が内包されていましたが、fabric v6 から erase2d に切り出されました。 erase2d を使って消しゴムを実装すると、次の動画のようにユーザビリティを大きく損なうほどのラグが発生していました。 このラグは PC では発生せず、iPad でのみ発生していました。 この現象について原因を調査したところ、以下の 2 点が原因であることが分かりました。 原因 1. Retina ディスプレイによるピクセル密度の違い iPad は Retina ディスプレイを搭載しており、ピクセル密度が通常のディスプレイよりも高くなっていました。 検証に利用していた 11 インチ iPad Air (M2) では、PC と比較してピクセル密度が 4 倍にもなっていました。 原因 2. ピクセル密度に依存した消しゴム機能特有の処理 消しゴム機能は、ペン機能と比較して複雑な処理が行われていました。 ペンは 1 つのキャンバスに線を描くだけであるのに対して、消しゴムは複数のキャンバスを扱い、それらを合成する処理が必要でした。 具体的には、消しゴムの軌跡を一時的なキャンバスに描画し、それをメインキャンバスに合成するといった処理等が行われていました。 これは、ピクセル密度に依存しており、ピクセル密度が高い環境では負荷が高くなっていました。 // https://github.com/ShaMan123/erase2d/blob/7ee2b789b4c9161d662ea8c8aaf87af4c37bab13/packages/core/src/erase.ts#L15-L40 export const erase = ( destination : CanvasRenderingContext2D , // メインキャンバス source : CanvasRenderingContext2D , // 消しゴムの軌跡が描かれたキャンバス erasingEffect ?: CanvasRenderingContext2D // 消したくないものを保護するためのキャンバス ) => { // メインキャンバスから、消しゴムの軌跡部分を消去 drawImage ( destination , source , "destination-out" ); if ( erasingEffect ) { // 消しゴムの軌跡に、消したくないものがあれば、消さずに保護 drawImage ( source , erasingEffect , "source-in" ); } else { // 消しゴムの軌跡を描いたキャンバスを初期化 source . save (); source . resetTransform (); source . clearRect ( 0 , 0 , source . canvas . width , source . canvas . height ); source . restore (); } }; これらの原因によって、Retina ディスプレイの高解像度環境では消しゴムで線を消すときにラグが発生していました。 解決策として Canvas の初期化時に enableRetinaScaling オプションを false に設定し、Retina ディスプレイの高解像度環境においても同じピクセル密度で処理を行うようにしました。 const canvas = new fabric . Canvas ( canvasElement , { enableRetinaScaling: false , }); この設定により、一定の解像度を担保しつつ、iPad でも滑らかな消しゴム操作を実現できました。 工夫 3. iPad で手書きして保存した画像を PC に即時反映されるようにした iPad で手書きしながら PC でカルテを使うということを想定しているため、iPad で保存した手書き画像は PC に即時反映される必要があります。こちらを実現するために、以下が候補に挙げられました。 SSE(Server-Sent Events) WebSocket Firebase Realtime Database Firebase Realtime Database を既に使用しており、今回保存するのは更新時間のみで、保存容量が大幅に増えることもないため、今回は Firebase Realtime Database を使用することにしました。 以下が、Realtime Database で変更の監視及び通知を行う実装です。 import { initializeApp } from "firebase/app" ; import { getDatabase , off , onValue , ref , set } from "firebase/database" ; const app = initializeApp ( { /* 省略’*/ }, "firebaseapp" ); const db = () => { return getDatabase ( app ); }; const PATH = "medicalRecordChiefComplaintImages" ; // refを生成 const medicalRecordChiefComplaintImagesRef = ( medicalRecordId : string ) => { return ref ( db (), `/ ${ PATH } / ${ medicalRecordId } ` ); }; // 主訴所見の手書き画像が更新されたことを通知する export const updateMedicalRecordChiefComplaintImageIds = async ({ medicalRecordId , }: { medicalRecordId : string ; }) => { await set ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), Date . now ()); }; // 主訴所見の手書き画像の変更を監視する export const watchMedicalRecordChiefComplaintImages = ({ medicalRecordId , refreshMedicalRecordChiefComplaintImages , }: { medicalRecordId : string ; refreshMedicalRecordChiefComplaintImages : () => void ; }) => { onValue ( medicalRecordChiefComplaintImagesRef ( medicalRecordId ), () => { // Realtime Databaseが更新されたときに実行する処理 refreshMedicalRecordChiefComplaintImages (); }); }; // 主訴所見の手書き画像の変更の監視を解除する export const unwatchMedicalRecordChiefComplaintImages = ({ medicalRecordId , }: { medicalRecordId : string ; }) => { off ( medicalRecordChiefComplaintImagesRef ( medicalRecordId )); }; フロントエンドでは、データフェッチに TanStack Query を使用しています。以下のように Realtime Database によって手書き画像の更新を監視して refetch しています。 export const useMedicalChiefComplaintImagesWatchQuery = ({ medicalRecordId , initialChiefComplaintImages , }: UseMedicalChiefComplaintImagesWatchQueryProps ) => { const medicalChiefComplaintImagesQuery = useQuery ({ queryKey: medicalChiefComplaintImageKey . list ( medicalRecordId ), queryFn : () => query . get ( request . get ( medicalRecordId )), }); useEffect (() => { watchMedicalRecordChiefComplaintImages ({ medicalRecordId: medicalRecordId , // Realtime Databaseの更新時にrefetchする refreshMedicalRecordChiefComplaintImages: medicalChiefComplaintImagesQuery . refetch , }); return () => unwatchMedicalRecordChiefComplaintImages ({ medicalRecordId }); }, [ medicalRecordId , medicalChiefComplaintImagesQuery . refetch ]); return medicalChiefComplaintImagesQuery ; }; 最後に、iPad で手書き画像を更新したタイミングで updateMedicalRecordChiefComplaintImageIds を呼び出せば、以下のように iPad での変更が PC に即時に反映されるようになります。 工夫 4. 手書きツールバーを自由に配置できるようにした 手書き機能では太さや色の変更、ペンと消しゴムの切り替え、履歴管理ができるようにツールバーを実装しています。ツールバーは頻繁に使われるため、ユーザーが使用しやすい位置に移動できるようにしました。こちらは dnd-kit を使用して実装しました。 動画にあるようにツールバーが親要素からはみ出ないようになっています。こちらは restrictToParentElement を使用することで実現できます。 また、CSS ライブラリに emotion を使用しており、今回の Draggable 対応では動的にスタイルを変更する必要があるため、 style prop を使うように注意する必要があります( https://emotion.sh/docs/best-practices#use-the-style-prop-for-dynamic-styles )。 ライブラリの不具合 上記のようにユーザーが使いやすいように手書き機能を開発しましたが、消しゴムで線を消すと消えて欲しくない背景画像まで消えてしまうという不具合がライブラリに存在していました。こちらは同じ fabric を使用している Dentis のエンジニア大岡に相談したところ、ライブラリに PR を出して解消していただけました! fix(): backgroundImage/overlayImage erasable by yasupeke · Pull Request #49 · ShaMan123/erase2d I always appreciate the convenience of using this tool. I have fixed an issue where the erasable setting for backgroundImage/overlayImage was not being applied. Please review the changes. github.com まとめ 本記事では、Web でユーザビリティの高い手書きアプリケーションを実装するための工夫点について紹介しました。手書き機能の実装においては、実装速度とユーザビリティを両立するために適切なライブラリ選定が重要です。私達は fabric を採用することで滑らかな手書きアプリケーションを実現できました。 実装にあたり、以下の 4 つを工夫しました。 手を画面に置きながら書ける機能: 接触面積の大きさを検出してペン入力と区別することで、自然な書き心地を実現しました 消しゴム操作のラグ解消: Retina ディスプレイ対応を最適化し、高負荷な消しゴム処理でもスムーズな操作感を実現しました リアルタイム同期: Firebase Realtime Database を活用し、複数デバイス間での即時反映を実現しました 柔軟な UI: dnd-kit によるドラッグ可能なツールバーで、ユーザー体験を向上しました また、ライブラリの不具合に対してはプロダクトを横断したコミュニケーションやコミュニティと協力することで解決しました。これらの工夫により、医科診療所で快適に使用できる手書き機能を開発することができました。 今後もユーザビリティを重視しながら、より使いやすい機能の開発を続けていきます。 We’re hiring メドレーでは一緒に働く仲間を大募集しています! カジュアル面談も実施しておりますので、「お話だけでも聞いてみたい!」「ちょっと雑談してみたい!」でも構いませんので、お気軽にお問い合わせください! 募集の一覧 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp 医療エンジニアリング領域盛り上がっています!メドレーについてお話します! メドレーの人材プラットフォーム事業についてお話しします! 「メドレーの人材プラットフォーム事業についてお話しします!」- 倉林 昭和さん pitta.me メドレーの開発チームについて知りたい方!ぜひお話ししましょう! メドレーの開発チームについて知りたい方!ぜひお話ししましょう! 「メドレーの開発チームについて知りたい方!ぜひお話ししましょう!」- 德永 諒介さん pitta.me