TECH PLAY

株式会社メドレー

株式会社メドレー の技術ブログ

1406

はじめに みなさん、こんにちは。エンジニアの山田です。今回はジョブメドレーアカデミー(以下、アカデミー)の開発の皆さんに集まってもらい、 TypeScript と NestJS を使ったバックエンド開発をどのように行なっているのかをインタビューしました。 以前、アカデミーがリニューアルした際にチームメンバーにインタビューした note もあるので、未読の方はぜひそちらもご覧いただければと思います。 インタビュイー紹介 岸田さん SES 会社で業務システム開発、バングラデシュに支社があるオフショア開発をしている会社でブリッジエンジニアという経験を経て、2019 年メドレー入社。 その後ジョブメドレーで開発をリード。現在はアカデミーのプロダクト責任者を務める。 熊本さん 2019 年メドレーに新卒入社。半年の研修を経てジョブメドレーの開発に携わる。現在はアカデミーの開発に携わる。大学では機械学習を用いた自動車の自動運転の研究をしていた。 牧野さん 2021 年 11 月にメドレー入社。不動産業界の SaaS を開発している会社でのマネージャー・エンジニアを経験。現在はアカデミーの開発に携わる。 徳永さん 2022 年メドレーに新卒入社。 OJT 期間中からアカデミーの開発に携わる。大学では、対話 AI システム・ AI との対話用プラットフォームの研究開発をしていた。 アカデミーのアーキテクチャ 山田 : 早速ですが、現在のアカデミーのアーキテクチャについて話を伺いたいと思います。全体の構成としては現在どんな形になっているんでしょうか。 岸田 : 以前話をしたところから大幅に変わったところはないですが、大まかにはこんな感じになっています。 山田 : ありがとうございます、分かりやすいですね。 (手前)岸田さん・(奥)牧野さん 山田 : Web フロントエンドに関しては Next.js とグローバルの状態管理として Apollo Client を使っていると伺っています。バックエンドは色々あると思うのですが、DB に関しては PostgreSQL を使っているんですよね? 岸田 : はい、ORM としては Prisma を使っています。 山田 : こうした開発環境はどのように決まったんでしょうか?特に、開発言語を全て TypeScript で統一する、という意思決定が先にあってバックエンド側のフレームワークは NestJS にしようとなったのか、逆に NestJS を使おうとして TypeScript になったのかをお聞きできれば。 岸田 : 細かいところまでは記憶にないのですが、TypeScript を使って型で開発をしたいというのが一番大きい理由でした。 山田 : まずは TypeScript からだったんですね。そこから Web フレームワークとしては Express など他の物もあったと思いますが、NestJS を採用した理由は何だったのでしょうか? 岸田 : そうですね、色々選択肢はあったと思うんですが、現実的にサービスを運用するとなると Express と NestJS のどちらかかな、という感じになっていました。 どちらも実開発での経験を持っているメンバーはいなかったんですが、Web 開発で良く使われる Web フレームワークは、基本的な機能やコンセプトについては共通する部分が多いことに加えて、アカデミーも特殊な仕様があるプロダクトではないので、どちらを選んだとしてもメンバーが困ることはないだろうと考えました。 その上で細かい使い勝手などを検討したり GraphQL との親和性の高さや Express という枯れた技術をベースにしていて、 フレームワーク自体の信頼感もあるので NestJS に決めた という感じでした。 熊本 : 絞り込んだ段階で各メンバーが NestJS を試験的に触り、使用感を見てみましたが、大きく問題になる点もなかったので全員一致で決まった感じですね。 岸田 : 前述のように Express をベースにしているので何か困ったら最悪 Express にすれば良いなというのもありました。 牧野 : ここまで実際大きな問題というのは無かったです。 山田 : 今は GraphQL を使って API を作っていると思いますが、REST で作るという選択肢は最初から無かったんですか? 岸田 : はい、GraphQL を使えるメンバーが揃っているという状況であえて REST にするメリットは無いなという事で考えていませんでした。外部公開する API があるとか、パフォーマンスがすごく重要な重い API 呼び出しというのがあったら別だったでしょうけども。 山田 : そもそもの話になってしまうんですが、弊社のプロダクト開発でよく使われている Ruby on Rails を使う選択肢は無かったんでしょうか? 岸田 : 選択肢としてはあるにはあったんですが、プロダクトのフェイズとして現在既に成長をしているサービスであることや、メンバーのスキルセットとして Rails に慣れたメンバーがそこまで多くなかったことを考えると、いかに技術的負債を少なく保ちつつ、機能開発をしていけるかという点が重要になるので、現在の技術構成にしました。 これが既存サービスのリニューアルではなくて、新規で一から全部作っていかないといけないという状況であれば Rails を使うかもしれませんが、やっぱり型があってある程度開発効率が高くなるというのは魅力的だったので、 これからどんどんと機能開発をするには今の構成が良い と判断しました。 (手前)徳永さん・(奥)熊本さん 山田 : DB に関して PostgreSQL ですが、 MySQL などにするということは考えなかったですか? 熊本 : そうですね、リニューアル前のアカデミーが使っていたということがあり、大きく変えるメリットはあまり無かったのが大きいです。 岸田 : あとこれは自分の感覚なのですが、PostgreSQL の方がスマートに処理できることが多いという印象があるというのもありました。 山田 : 元々サービスで使っていたというのは、やっぱり大きいですね。ORM は Prisma ですよね。例えば他に TypeORM などの選択肢もあったと思いますが、こちらはどういった経緯で使うことになりましたか? 熊本 : 色々と触ってみた結果、Prisma が 自分たちの使い方にマッチしてた のが大きかったです。がっつりと SQL を自動生成するなどの手厚い機能は、自分達の使い方では必要もなかったですし。キャッチアップのためにペアプロなどもしましたが、 メンバーも使いやすい ということが確認できました。 山田 : 今ペアプロの話が出ましたが、どのような目的でやっていたんでしょうか。 熊本 : 初期の頃はメンバー間の知識などに偏りもあったので、そういったものを実際にコードを触りながら埋めていく という目的でした。実際に、GraphQL を使ったことがなかったメンバーも早く慣れてくれました。 山田 : なるほど。ある種学習も兼ねていたんですね。最終的に現在このアーキテクチャの使い勝手としては感想としてどうでしょうか? 岸田 : 言葉は違うかもしれないですが「 普通に使い勝手が良い 」という感じです。 もちろんメンバーが習熟してきたので、色々とコード的に改善しようという部分はあったりもしますが、今のところ本当に困った問題というのは無いので、非常に良い選択だったかと思います。 NestJS のテストコードについて 山田 : テストコードは基本書いているんですよね? 熊本 : はい、バックエンドは基本的に Jest で全部書いています。フロントエンドに関しては今まさに E2E テストをどのように実施していくかを試行錯誤しているところですね。 山田 : NestJS でテストコード書くのに何か困ったこととかありましたか? 岸田 : 困っているというわけではないですが、Rails でほぼ必ず使われている Factorybot に相当する機能が欲しいですね…。 山田 : 確かに Node.js だとそういったライブラリを見かけないですね…。 岸田 : 皆さんあんまり困ってないのかな?ということで今は簡易的なものを自作して使っています。 山田 : OSS で作ったら需要があるかもしれませんね。 現状の NestJS での開発の課題感 山田 : ここまで NestJS での開発について聞いてきましたが、プロダクト開発での課題は何かあったりしますか? 岸田 : 先ほども少し話をしたメンバーの習熟度が高くなったことによるコード改善の他には、NestJS というか Node.js が Rails に比べると定番ライブラリが少なめなのがあります。 例えば、 Sidekiq のようなライブラリって便利だと思うんですが、相当するライブラリでデファクトスタンダードというようなものが Node.js だと少ない気がしています。ライブラリが一杯あるのですが、デファクトまでいかないというものが多いという感想です。 山田 : 確かに Node.js だと同じ目的のライブラリが複数あって機能がちょっとずつ違うという感じがありますね。 牧野 : あとは機能開発にリソースを取っているので、細かい部分でのライブラリのアップデートが遅れ気味というのが課題感としてあります。 今のところはとてつもなく遅れているというわけではないのですが、積り積っていくとこういった部分が負債になっていくので、対応したいと思っています。 岸田 : こういったものも含めてプロダクト開発を円滑に回すために、自分も含めた各メンバーのスキルを広げて深くしていかないといけないなというのもありますね。 チームへのオンボーディングについて 山田 : さて、話が変わって現在アカデミーで一番最後にジョインしたのが徳永さんだと思いますが、オンボーディングなどはどのような感じだったんでしょうか。学生時代に NestJS はがっつり触っていたんでしたっけ? 徳永 : いえ、学生時代はチュートリアルをしたくらいで、深く使っていたということはなかったです。ですので、最初は全然プロダクトのコードが分からないというレベルで、ほとんど未経験でした。 山田 : なるほど。ほぼ未経験で新卒研修を経て、アカデミーの開発チームにジョインして困った部分などはありましたか? 徳永 : TypeScript を使っているというのは凄く自分にとって良くて、言語でつまづくということはほとんどありませんでした。 やはり 公式ドキュメントがすごく充実していますし、コミュニティの質の高い情報も多い ので。プロダクトに関しては過去の Pull Request や Issue 上のやり取りなどで、 どういう意図で実装されたものなのかを参照できたのが大きかった です。一方でバックエンドのデザインパターンなどは経験が少なく苦労した部分がありました。その部分はドキュメントで学習したり、周りのメンバーに直接質問しながらキャッチアップしていきました。 山田 : 今年は入社後研修も TypeScript で書く割合が Ruby よりも多かったので、それがもし逆だったら、もっと苦労していたかもしれないですね。 徳永 : そうですね、その可能性は十分にあり得ます。 山田 : あとアカデミーチームは火水木に勉強会を開催していますよね。ジョブメドレーの開発チームでも噂になっています。 岸田 : これは全員で集まる勉強会というわけではなく、 月によって各自テーマを決めて火水木 1 時間ずつスキルアップのために時間を確保し、学んだことなどをアウトプットしていく という形式です。アウトプットしたものは、お互いに確認して知識を深めていこうというものですね。 山田 : それは素敵な取り組みですね。あとは Figma で開発ドキュメントをまとめているのが印象的ですよね。 牧野 : 最初は岸田さんが率先して、開発した部分のドキュメントなんかを書いていたんですが、最近はチームで共有するようなものもほとんど Figma でまとめるようになっています。 熊本 : もちろん永続化しないといけないドキュメントは README なりコンフルエンスなりに書いていくのですが、ちょっとしたものを気軽に Figma にまとめられるのはすごく良いです。 岸田 : 他の人がやっていることなんかも絶対に目につく ので、そういった意味でもドキュメントが散らばらなくて今は良く機能していると思っています。 現状の課題感とアカデミーの開発に向く人 山田 : 最後になりますが、アカデミーに来てほしい人材はどういった方でしょうか? 徳永 : この プロダクトにわくわくできる方に来てほしい と思います。福祉業界に明るい方なんかは大歓迎です! 山田 : なるほど。プロダクトのビジネス面の理解をして楽しんで開発できる方ですね。技術的な部分では何かありますか? 岸田 : 今まで TypeScript と NestJS を中心に話していましたが、実はこれらができる人というのは必須条件ではありません。どの言語でもフレームワークでも ちゃんと Web 開発が分かっている方 であればという感じです。 他には 情報感度が高い人が良い と思います。言語化が難しいのですが、開発環境や言語、ライブラリなどできちんと最新の情報が取捨選択できる方がいると、チームがより活性化すると考えています。 バックエンドでいうとベース部分などもきちんと手を入れることができて、なおかつ人に教えるのが好きな方が入ってもらえると、チームへの知識伝播などもより上手くいくと考えています。 あるとより良いなという所だと、現在アプリは React Native で作られているんですが、React Native でのアプリ開発に強い方だと嬉しいですね。ユーザーに一番使われているのはアプリなので、さらにそのユーザビリティなどを高めるためには、アプリ開発に強い方に入ってもらえるとありがたいです。 山田 : よりアカデミーというプロダクトをドライブさせてくれる方にぜひ来てほしいところですね。本日はありがとうございました! おわりに メドレーの開発チームの中でも野心的なアーキテクチャで開発をしているアカデミーチームについてお伝えしました。 サービスを伸ばすために、適切に言語やフレームワークを選択していき、柔軟性を持って開発していってるんだなというのが伝わってきたインタビューでした。少しでも興味が出た方はぜひ、下のリンクからお話できればと思います! https://www.medley.jp/jobs
はじめに みなさん、こんにちは。エンジニアの山田です。今回はジョブメドレーアカデミー(以下、アカデミー)の開発の皆さんに集まってもらい、 TypeScript と NestJS を使ったバックエンド開発をどのように行なっているのかをインタビューしました。 以前、アカデミーがリニューアルした際にチームメンバーにインタビューした note もあるので、未読の方はぜひそちらもご覧いただければと思います。 インタビュイー紹介 岸田さん SES 会社で業務システム開発、バングラデシュに支社があるオフショア開発をしている会社でブリッジエンジニアという経験を経て、2019 年メドレー入社。 その後ジョブメドレーで開発をリード。現在はアカデミーのプロダクト責任者を務める。 熊本さん 2019 年メドレーに新卒入社。半年の研修を経てジョブメドレーの開発に携わる。現在はアカデミーの開発に携わる。大学では機械学習を用いた自動車の自動運転の研究をしていた。 牧野さん 2021 年 11 月にメドレー入社。不動産業界の SaaS を開発している会社でのマネージャー・エンジニアを経験。現在はアカデミーの開発に携わる。 徳永さん 2022 年メドレーに新卒入社。 OJT 期間中からアカデミーの開発に携わる。大学では、対話 AI システム・ AI との対話用プラットフォームの研究開発をしていた。 アカデミーのアーキテクチャ 山田 : 早速ですが、現在のアカデミーのアーキテクチャについて話を伺いたいと思います。全体の構成としては現在どんな形になっているんでしょうか。 岸田 : 以前話をしたところから大幅に変わったところはないですが、大まかにはこんな感じになっています。 山田 : ありがとうございます、分かりやすいですね。 (手前)岸田さん・(奥)牧野さん 山田 : Web フロントエンドに関しては Next.js とグローバルの状態管理として Apollo Client を使っていると伺っています。バックエンドは色々あると思うのですが、DB に関しては PostgreSQL を使っているんですよね? 岸田 : はい、ORM としては Prisma を使っています。 山田 : こうした開発環境はどのように決まったんでしょうか?特に、開発言語を全て TypeScript で統一する、という意思決定が先にあってバックエンド側のフレームワークは NestJS にしようとなったのか、逆に NestJS を使おうとして TypeScript になったのかをお聞きできれば。 岸田 : 細かいところまでは記憶にないのですが、TypeScript を使って型で開発をしたいというのが一番大きい理由でした。 山田 : まずは TypeScript からだったんですね。そこから Web フレームワークとしては Express など他の物もあったと思いますが、NestJS を採用した理由は何だったのでしょうか? 岸田 : そうですね、色々選択肢はあったと思うんですが、現実的にサービスを運用するとなると Express と NestJS のどちらかかな、という感じになっていました。 どちらも実開発での経験を持っているメンバーはいなかったんですが、Web 開発で良く使われる Web フレームワークは、基本的な機能やコンセプトについては共通する部分が多いことに加えて、アカデミーも特殊な仕様があるプロダクトではないので、どちらを選んだとしてもメンバーが困ることはないだろうと考えました。 その上で細かい使い勝手などを検討したり GraphQL との親和性の高さや Express という枯れた技術をベースにしていて、 フレームワーク自体の信頼感もあるので NestJS に決めた という感じでした。 熊本 : 絞り込んだ段階で各メンバーが NestJS を試験的に触り、使用感を見てみましたが、大きく問題になる点もなかったので全員一致で決まった感じですね。 岸田 : 前述のように Express をベースにしているので何か困ったら最悪 Express にすれば良いなというのもありました。 牧野 : ここまで実際大きな問題というのは無かったです。 山田 : 今は GraphQL を使って API を作っていると思いますが、REST で作るという選択肢は最初から無かったんですか? 岸田 : はい、GraphQL を使えるメンバーが揃っているという状況であえて REST にするメリットは無いなという事で考えていませんでした。外部公開する API があるとか、パフォーマンスがすごく重要な重い API 呼び出しというのがあったら別だったでしょうけども。 山田 : そもそもの話になってしまうんですが、弊社のプロダクト開発でよく使われている Ruby on Rails を使う選択肢は無かったんでしょうか? 岸田 : 選択肢としてはあるにはあったんですが、プロダクトのフェイズとして現在既に成長をしているサービスであることや、メンバーのスキルセットとして Rails に慣れたメンバーがそこまで多くなかったことを考えると、いかに技術的負債を少なく保ちつつ、機能開発をしていけるかという点が重要になるので、現在の技術構成にしました。 これが既存サービスのリニューアルではなくて、新規で一から全部作っていかないといけないという状況であれば Rails を使うかもしれませんが、やっぱり型があってある程度開発効率が高くなるというのは魅力的だったので、 これからどんどんと機能開発をするには今の構成が良い と判断しました。 (手前)徳永さん・(奥)熊本さん 山田 : DB に関して PostgreSQL ですが、 MySQL などにするということは考えなかったですか? 熊本 : そうですね、リニューアル前のアカデミーが使っていたということがあり、大きく変えるメリットはあまり無かったのが大きいです。 岸田 : あとこれは自分の感覚なのですが、PostgreSQL の方がスマートに処理できることが多いという印象があるというのもありました。 山田 : 元々サービスで使っていたというのは、やっぱり大きいですね。ORM は Prisma ですよね。例えば他に TypeORM などの選択肢もあったと思いますが、こちらはどういった経緯で使うことになりましたか? 熊本 : 色々と触ってみた結果、Prisma が 自分たちの使い方にマッチしてた のが大きかったです。がっつりと SQL を自動生成するなどの手厚い機能は、自分達の使い方では必要もなかったですし。キャッチアップのためにペアプロなどもしましたが、 メンバーも使いやすい ということが確認できました。 山田 : 今ペアプロの話が出ましたが、どのような目的でやっていたんでしょうか。 熊本 : 初期の頃はメンバー間の知識などに偏りもあったので、そういったものを実際にコードを触りながら埋めていく という目的でした。実際に、GraphQL を使ったことがなかったメンバーも早く慣れてくれました。 山田 : なるほど。ある種学習も兼ねていたんですね。最終的に現在このアーキテクチャの使い勝手としては感想としてどうでしょうか? 岸田 : 言葉は違うかもしれないですが「 普通に使い勝手が良い 」という感じです。 もちろんメンバーが習熟してきたので、色々とコード的に改善しようという部分はあったりもしますが、今のところ本当に困った問題というのは無いので、非常に良い選択だったかと思います。 NestJS のテストコードについて 山田 : テストコードは基本書いているんですよね? 熊本 : はい、バックエンドは基本的に Jest で全部書いています。フロントエンドに関しては今まさに E2E テストをどのように実施していくかを試行錯誤しているところですね。 山田 : NestJS でテストコード書くのに何か困ったこととかありましたか? 岸田 : 困っているというわけではないですが、Rails でほぼ必ず使われている Factorybot に相当する機能が欲しいですね…。 山田 : 確かに Node.js だとそういったライブラリを見かけないですね…。 岸田 : 皆さんあんまり困ってないのかな?ということで今は簡易的なものを自作して使っています。 山田 : OSS で作ったら需要があるかもしれませんね。 現状の NestJS での開発の課題感 山田 : ここまで NestJS での開発について聞いてきましたが、プロダクト開発での課題は何かあったりしますか? 岸田 : 先ほども少し話をしたメンバーの習熟度が高くなったことによるコード改善の他には、NestJS というか Node.js が Rails に比べると定番ライブラリが少なめなのがあります。 例えば、 Sidekiq のようなライブラリって便利だと思うんですが、相当するライブラリでデファクトスタンダードというようなものが Node.js だと少ない気がしています。ライブラリが一杯あるのですが、デファクトまでいかないというものが多いという感想です。 山田 : 確かに Node.js だと同じ目的のライブラリが複数あって機能がちょっとずつ違うという感じがありますね。 牧野 : あとは機能開発にリソースを取っているので、細かい部分でのライブラリのアップデートが遅れ気味というのが課題感としてあります。 今のところはとてつもなく遅れているというわけではないのですが、積り積っていくとこういった部分が負債になっていくので、対応したいと思っています。 岸田 : こういったものも含めてプロダクト開発を円滑に回すために、自分も含めた各メンバーのスキルを広げて深くしていかないといけないなというのもありますね。 チームへのオンボーディングについて 山田 : さて、話が変わって現在アカデミーで一番最後にジョインしたのが徳永さんだと思いますが、オンボーディングなどはどのような感じだったんでしょうか。学生時代に NestJS はがっつり触っていたんでしたっけ? 徳永 : いえ、学生時代はチュートリアルをしたくらいで、深く使っていたということはなかったです。ですので、最初は全然プロダクトのコードが分からないというレベルで、ほとんど未経験でした。 山田 : なるほど。ほぼ未経験で新卒研修を経て、アカデミーの開発チームにジョインして困った部分などはありましたか? 徳永 : TypeScript を使っているというのは凄く自分にとって良くて、言語でつまづくということはほとんどありませんでした。 やはり 公式ドキュメントがすごく充実していますし、コミュニティの質の高い情報も多い ので。プロダクトに関しては過去の Pull Request や Issue 上のやり取りなどで、 どういう意図で実装されたものなのかを参照できたのが大きかった です。一方でバックエンドのデザインパターンなどは経験が少なく苦労した部分がありました。その部分はドキュメントで学習したり、周りのメンバーに直接質問しながらキャッチアップしていきました。 山田 : 今年は入社後研修も TypeScript で書く割合が Ruby よりも多かったので、それがもし逆だったら、もっと苦労していたかもしれないですね。 徳永 : そうですね、その可能性は十分にあり得ます。 山田 : あとアカデミーチームは火水木に勉強会を開催していますよね。ジョブメドレーの開発チームでも噂になっています。 岸田 : これは全員で集まる勉強会というわけではなく、 月によって各自テーマを決めて火水木 1 時間ずつスキルアップのために時間を確保し、学んだことなどをアウトプットしていく という形式です。アウトプットしたものは、お互いに確認して知識を深めていこうというものですね。 山田 : それは素敵な取り組みですね。あとは Figma で開発ドキュメントをまとめているのが印象的ですよね。 牧野 : 最初は岸田さんが率先して、開発した部分のドキュメントなんかを書いていたんですが、最近はチームで共有するようなものもほとんど Figma でまとめるようになっています。 熊本 : もちろん永続化しないといけないドキュメントは README なりコンフルエンスなりに書いていくのですが、ちょっとしたものを気軽に Figma にまとめられるのはすごく良いです。 岸田 : 他の人がやっていることなんかも絶対に目につく ので、そういった意味でもドキュメントが散らばらなくて今は良く機能していると思っています。 現状の課題感とアカデミーの開発に向く人 山田 : 最後になりますが、アカデミーに来てほしい人材はどういった方でしょうか? 徳永 : この プロダクトにわくわくできる方に来てほしい と思います。福祉業界に明るい方なんかは大歓迎です! 山田 : なるほど。プロダクトのビジネス面の理解をして楽しんで開発できる方ですね。技術的な部分では何かありますか? 岸田 : 今まで TypeScript と NestJS を中心に話していましたが、実はこれらができる人というのは必須条件ではありません。どの言語でもフレームワークでも ちゃんと Web 開発が分かっている方 であればという感じです。 他には 情報感度が高い人が良い と思います。言語化が難しいのですが、開発環境や言語、ライブラリなどできちんと最新の情報が取捨選択できる方がいると、チームがより活性化すると考えています。 バックエンドでいうとベース部分などもきちんと手を入れることができて、なおかつ人に教えるのが好きな方が入ってもらえると、チームへの知識伝播などもより上手くいくと考えています。 あるとより良いなという所だと、現在アプリは React Native で作られているんですが、React Native でのアプリ開発に強い方だと嬉しいですね。ユーザーに一番使われているのはアプリなので、さらにそのユーザビリティなどを高めるためには、アプリ開発に強い方に入ってもらえるとありがたいです。 山田 : よりアカデミーというプロダクトをドライブさせてくれる方にぜひ来てほしいところですね。本日はありがとうございました! おわりに メドレーの開発チームの中でも野心的なアーキテクチャで開発をしているアカデミーチームについてお伝えしました。 サービスを伸ばすために、適切に言語やフレームワークを選択していき、柔軟性を持って開発していってるんだなというのが伝わってきたインタビューでした。少しでも興味が出た方はぜひ、下のリンクからお話できればと思います! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに みなさん、こんにちは。エンジニアの山田です。今回はジョブメドレーアカデミー(以下、アカデミー)の開発の皆さんに集まってもらい、 TypeScript と NestJS を使ったバックエンド開発をどのように行なっているのかをインタビューしました。 以前、アカデミーがリニューアルした際にチームメンバーにインタビューした note もあるので、未読の方はぜひそちらもご覧いただければと思います。 インタビュイー紹介 岸田さん SES 会社で業務システム開発、バングラデシュに支社があるオフショア開発をしている会社でブリッジエンジニアという経験を経て、2019 年メドレー入社。 その後ジョブメドレーで開発をリード。現在はアカデミーのプロダクト責任者を務める。 熊本さん 2019 年メドレーに新卒入社。半年の研修を経てジョブメドレーの開発に携わる。現在はアカデミーの開発に携わる。大学では機械学習を用いた自動車の自動運転の研究をしていた。 牧野さん 2021 年 11 月にメドレー入社。不動産業界の SaaS を開発している会社でのマネージャー・エンジニアを経験。現在はアカデミーの開発に携わる。 徳永さん 2022 年メドレーに新卒入社。 OJT 期間中からアカデミーの開発に携わる。大学では、対話 AI システム・ AI との対話用プラットフォームの研究開発をしていた。 アカデミーのアーキテクチャ 山田 : 早速ですが、現在のアカデミーのアーキテクチャについて話を伺いたいと思います。全体の構成としては現在どんな形になっているんでしょうか。 岸田 : 以前話をしたところから大幅に変わったところはないですが、大まかにはこんな感じになっています。 山田 : ありがとうございます、分かりやすいですね。 (手前)岸田さん・(奥)牧野さん 山田 : Web フロントエンドに関しては Next.js とグローバルの状態管理として Apollo Client を使っていると伺っています。バックエンドは色々あると思うのですが、DB に関しては PostgreSQL を使っているんですよね? 岸田 : はい、ORM としては Prisma を使っています。 山田 : こうした開発環境はどのように決まったんでしょうか?特に、開発言語を全て TypeScript で統一する、という意思決定が先にあってバックエンド側のフレームワークは NestJS にしようとなったのか、逆に NestJS を使おうとして TypeScript になったのかをお聞きできれば。 岸田 : 細かいところまでは記憶にないのですが、TypeScript を使って型で開発をしたいというのが一番大きい理由でした。 山田 : まずは TypeScript からだったんですね。そこから Web フレームワークとしては Express など他の物もあったと思いますが、NestJS を採用した理由は何だったのでしょうか? 岸田 : そうですね、色々選択肢はあったと思うんですが、現実的にサービスを運用するとなると Express と NestJS のどちらかかな、という感じになっていました。 どちらも実開発での経験を持っているメンバーはいなかったんですが、Web 開発で良く使われる Web フレームワークは、基本的な機能やコンセプトについては共通する部分が多いことに加えて、アカデミーも特殊な仕様があるプロダクトではないので、どちらを選んだとしてもメンバーが困ることはないだろうと考えました。 その上で細かい使い勝手などを検討したり GraphQL との親和性の高さや Express という枯れた技術をベースにしていて、 フレームワーク自体の信頼感もあるので NestJS に決めた という感じでした。 熊本 : 絞り込んだ段階で各メンバーが NestJS を試験的に触り、使用感を見てみましたが、大きく問題になる点もなかったので全員一致で決まった感じですね。 岸田 : 前述のように Express をベースにしているので何か困ったら最悪 Express にすれば良いなというのもありました。 牧野 : ここまで実際大きな問題というのは無かったです。 山田 : 今は GraphQL を使って API を作っていると思いますが、REST で作るという選択肢は最初から無かったんですか? 岸田 : はい、GraphQL を使えるメンバーが揃っているという状況であえて REST にするメリットは無いなという事で考えていませんでした。外部公開する API があるとか、パフォーマンスがすごく重要な重い API 呼び出しというのがあったら別だったでしょうけども。 山田 : そもそもの話になってしまうんですが、弊社のプロダクト開発でよく使われている Ruby on Rails を使う選択肢は無かったんでしょうか? 岸田 : 選択肢としてはあるにはあったんですが、プロダクトのフェイズとして現在既に成長をしているサービスであることや、メンバーのスキルセットとして Rails に慣れたメンバーがそこまで多くなかったことを考えると、いかに技術的負債を少なく保ちつつ、機能開発をしていけるかという点が重要になるので、現在の技術構成にしました。 これが既存サービスのリニューアルではなくて、新規で一から全部作っていかないといけないという状況であれば Rails を使うかもしれませんが、やっぱり型があってある程度開発効率が高くなるというのは魅力的だったので、 これからどんどんと機能開発をするには今の構成が良い と判断しました。 (手前)徳永さん・(奥)熊本さん 山田 : DB に関して PostgreSQL ですが、 MySQL などにするということは考えなかったですか? 熊本 : そうですね、リニューアル前のアカデミーが使っていたということがあり、大きく変えるメリットはあまり無かったのが大きいです。 岸田 : あとこれは自分の感覚なのですが、PostgreSQL の方がスマートに処理できることが多いという印象があるというのもありました。 山田 : 元々サービスで使っていたというのは、やっぱり大きいですね。ORM は Prisma ですよね。例えば他に TypeORM などの選択肢もあったと思いますが、こちらはどういった経緯で使うことになりましたか? 熊本 : 色々と触ってみた結果、Prisma が 自分たちの使い方にマッチしてた のが大きかったです。がっつりと SQL を自動生成するなどの手厚い機能は、自分達の使い方では必要もなかったですし。キャッチアップのためにペアプロなどもしましたが、 メンバーも使いやすい ということが確認できました。 山田 : 今ペアプロの話が出ましたが、どのような目的でやっていたんでしょうか。 熊本 : 初期の頃はメンバー間の知識などに偏りもあったので、そういったものを実際にコードを触りながら埋めていく という目的でした。実際に、GraphQL を使ったことがなかったメンバーも早く慣れてくれました。 山田 : なるほど。ある種学習も兼ねていたんですね。最終的に現在このアーキテクチャの使い勝手としては感想としてどうでしょうか? 岸田 : 言葉は違うかもしれないですが「 普通に使い勝手が良い 」という感じです。 もちろんメンバーが習熟してきたので、色々とコード的に改善しようという部分はあったりもしますが、今のところ本当に困った問題というのは無いので、非常に良い選択だったかと思います。 NestJS のテストコードについて 山田 : テストコードは基本書いているんですよね? 熊本 : はい、バックエンドは基本的に Jest で全部書いています。フロントエンドに関しては今まさに E2E テストをどのように実施していくかを試行錯誤しているところですね。 山田 : NestJS でテストコード書くのに何か困ったこととかありましたか? 岸田 : 困っているというわけではないですが、Rails でほぼ必ず使われている Factorybot に相当する機能が欲しいですね…。 山田 : 確かに Node.js だとそういったライブラリを見かけないですね…。 岸田 : 皆さんあんまり困ってないのかな?ということで今は簡易的なものを自作して使っています。 山田 : OSS で作ったら需要があるかもしれませんね。 現状の NestJS での開発の課題感 山田 : ここまで NestJS での開発について聞いてきましたが、プロダクト開発での課題は何かあったりしますか? 岸田 : 先ほども少し話をしたメンバーの習熟度が高くなったことによるコード改善の他には、NestJS というか Node.js が Rails に比べると定番ライブラリが少なめなのがあります。 例えば、 Sidekiq のようなライブラリって便利だと思うんですが、相当するライブラリでデファクトスタンダードというようなものが Node.js だと少ない気がしています。ライブラリが一杯あるのですが、デファクトまでいかないというものが多いという感想です。 山田 : 確かに Node.js だと同じ目的のライブラリが複数あって機能がちょっとずつ違うという感じがありますね。 牧野 : あとは機能開発にリソースを取っているので、細かい部分でのライブラリのアップデートが遅れ気味というのが課題感としてあります。 今のところはとてつもなく遅れているというわけではないのですが、積り積っていくとこういった部分が負債になっていくので、対応したいと思っています。 岸田 : こういったものも含めてプロダクト開発を円滑に回すために、自分も含めた各メンバーのスキルを広げて深くしていかないといけないなというのもありますね。 チームへのオンボーディングについて 山田 : さて、話が変わって現在アカデミーで一番最後にジョインしたのが徳永さんだと思いますが、オンボーディングなどはどのような感じだったんでしょうか。学生時代に NestJS はがっつり触っていたんでしたっけ? 徳永 : いえ、学生時代はチュートリアルをしたくらいで、深く使っていたということはなかったです。ですので、最初は全然プロダクトのコードが分からないというレベルで、ほとんど未経験でした。 山田 : なるほど。ほぼ未経験で新卒研修を経て、アカデミーの開発チームにジョインして困った部分などはありましたか? 徳永 : TypeScript を使っているというのは凄く自分にとって良くて、言語でつまづくということはほとんどありませんでした。 やはり 公式ドキュメントがすごく充実していますし、コミュニティの質の高い情報も多い ので。プロダクトに関しては過去の Pull Request や Issue 上のやり取りなどで、 どういう意図で実装されたものなのかを参照できたのが大きかった です。一方でバックエンドのデザインパターンなどは経験が少なく苦労した部分がありました。その部分はドキュメントで学習したり、周りのメンバーに直接質問しながらキャッチアップしていきました。 山田 : 今年は入社後研修も TypeScript で書く割合が Ruby よりも多かったので、それがもし逆だったら、もっと苦労していたかもしれないですね。 徳永 : そうですね、その可能性は十分にあり得ます。 山田 : あとアカデミーチームは火水木に勉強会を開催していますよね。ジョブメドレーの開発チームでも噂になっています。 岸田 : これは全員で集まる勉強会というわけではなく、 月によって各自テーマを決めて火水木 1 時間ずつスキルアップのために時間を確保し、学んだことなどをアウトプットしていく という形式です。アウトプットしたものは、お互いに確認して知識を深めていこうというものですね。 山田 : それは素敵な取り組みですね。あとは Figma で開発ドキュメントをまとめているのが印象的ですよね。 牧野 : 最初は岸田さんが率先して、開発した部分のドキュメントなんかを書いていたんですが、最近はチームで共有するようなものもほとんど Figma でまとめるようになっています。 熊本 : もちろん永続化しないといけないドキュメントは README なりコンフルエンスなりに書いていくのですが、ちょっとしたものを気軽に Figma にまとめられるのはすごく良いです。 岸田 : 他の人がやっていることなんかも絶対に目につく ので、そういった意味でもドキュメントが散らばらなくて今は良く機能していると思っています。 現状の課題感とアカデミーの開発に向く人 山田 : 最後になりますが、アカデミーに来てほしい人材はどういった方でしょうか? 徳永 : この プロダクトにわくわくできる方に来てほしい と思います。福祉業界に明るい方なんかは大歓迎です! 山田 : なるほど。プロダクトのビジネス面の理解をして楽しんで開発できる方ですね。技術的な部分では何かありますか? 岸田 : 今まで TypeScript と NestJS を中心に話していましたが、実はこれらができる人というのは必須条件ではありません。どの言語でもフレームワークでも ちゃんと Web 開発が分かっている方 であればという感じです。 他には 情報感度が高い人が良い と思います。言語化が難しいのですが、開発環境や言語、ライブラリなどできちんと最新の情報が取捨選択できる方がいると、チームがより活性化すると考えています。 バックエンドでいうとベース部分などもきちんと手を入れることができて、なおかつ人に教えるのが好きな方が入ってもらえると、チームへの知識伝播などもより上手くいくと考えています。 あるとより良いなという所だと、現在アプリは React Native で作られているんですが、React Native でのアプリ開発に強い方だと嬉しいですね。ユーザーに一番使われているのはアプリなので、さらにそのユーザビリティなどを高めるためには、アプリ開発に強い方に入ってもらえるとありがたいです。 山田 : よりアカデミーというプロダクトをドライブさせてくれる方にぜひ来てほしいところですね。本日はありがとうございました! おわりに メドレーの開発チームの中でも野心的なアーキテクチャで開発をしているアカデミーチームについてお伝えしました。 サービスを伸ばすために、適切に言語やフレームワークを選択していき、柔軟性を持って開発していってるんだなというのが伝わってきたインタビューでした。少しでも興味が出た方はぜひ、下のリンクからお話できればと思います! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに みなさん、こんにちは。エンジニアの山田です。今回はジョブメドレーアカデミー(以下、アカデミー)の開発の皆さんに集まってもらい、 TypeScript と NestJS を使ったバックエンド開発をどのように行なっているのかをインタビューしました。 以前、アカデミーがリニューアルした際にチームメンバーにインタビューした note もあるので、未読の方はぜひそちらもご覧いただければと思います。 インタビュイー紹介 岸田さん SES 会社で業務システム開発、バングラデシュに支社があるオフショア開発をしている会社でブリッジエンジニアという経験を経て、2019 年メドレー入社。 その後ジョブメドレーで開発をリード。現在はアカデミーのプロダクト責任者を務める。 熊本さん 2019 年メドレーに新卒入社。半年の研修を経てジョブメドレーの開発に携わる。現在はアカデミーの開発に携わる。大学では機械学習を用いた自動車の自動運転の研究をしていた。 牧野さん 2021 年 11 月にメドレー入社。不動産業界の SaaS を開発している会社でのマネージャー・エンジニアを経験。現在はアカデミーの開発に携わる。 徳永さん 2022 年メドレーに新卒入社。 OJT 期間中からアカデミーの開発に携わる。大学では、対話 AI システム・ AI との対話用プラットフォームの研究開発をしていた。 アカデミーのアーキテクチャ 山田 : 早速ですが、現在のアカデミーのアーキテクチャについて話を伺いたいと思います。全体の構成としては現在どんな形になっているんでしょうか。 岸田 : 以前話をしたところから大幅に変わったところはないですが、大まかにはこんな感じになっています。 山田 : ありがとうございます、分かりやすいですね。 (手前)岸田さん・(奥)牧野さん 山田 : Web フロントエンドに関しては Next.js とグローバルの状態管理として Apollo Client を使っていると伺っています。バックエンドは色々あると思うのですが、DB に関しては PostgreSQL を使っているんですよね? 岸田 : はい、ORM としては Prisma を使っています。 山田 : こうした開発環境はどのように決まったんでしょうか?特に、開発言語を全て TypeScript で統一する、という意思決定が先にあってバックエンド側のフレームワークは NestJS にしようとなったのか、逆に NestJS を使おうとして TypeScript になったのかをお聞きできれば。 岸田 : 細かいところまでは記憶にないのですが、TypeScript を使って型で開発をしたいというのが一番大きい理由でした。 山田 : まずは TypeScript からだったんですね。そこから Web フレームワークとしては Express など他の物もあったと思いますが、NestJS を採用した理由は何だったのでしょうか? 岸田 : そうですね、色々選択肢はあったと思うんですが、現実的にサービスを運用するとなると Express と NestJS のどちらかかな、という感じになっていました。 どちらも実開発での経験を持っているメンバーはいなかったんですが、Web 開発で良く使われる Web フレームワークは、基本的な機能やコンセプトについては共通する部分が多いことに加えて、アカデミーも特殊な仕様があるプロダクトではないので、どちらを選んだとしてもメンバーが困ることはないだろうと考えました。 その上で細かい使い勝手などを検討したり GraphQL との親和性の高さや Express という枯れた技術をベースにしていて、 フレームワーク自体の信頼感もあるので NestJS に決めた という感じでした。 熊本 : 絞り込んだ段階で各メンバーが NestJS を試験的に触り、使用感を見てみましたが、大きく問題になる点もなかったので全員一致で決まった感じですね。 岸田 : 前述のように Express をベースにしているので何か困ったら最悪 Express にすれば良いなというのもありました。 牧野 : ここまで実際大きな問題というのは無かったです。 山田 : 今は GraphQL を使って API を作っていると思いますが、REST で作るという選択肢は最初から無かったんですか? 岸田 : はい、GraphQL を使えるメンバーが揃っているという状況であえて REST にするメリットは無いなという事で考えていませんでした。外部公開する API があるとか、パフォーマンスがすごく重要な重い API 呼び出しというのがあったら別だったでしょうけども。 山田 : そもそもの話になってしまうんですが、弊社のプロダクト開発でよく使われている Ruby on Rails を使う選択肢は無かったんでしょうか? 岸田 : 選択肢としてはあるにはあったんですが、プロダクトのフェイズとして現在既に成長をしているサービスであることや、メンバーのスキルセットとして Rails に慣れたメンバーがそこまで多くなかったことを考えると、いかに技術的負債を少なく保ちつつ、機能開発をしていけるかという点が重要になるので、現在の技術構成にしました。 これが既存サービスのリニューアルではなくて、新規で一から全部作っていかないといけないという状況であれば Rails を使うかもしれませんが、やっぱり型があってある程度開発効率が高くなるというのは魅力的だったので、 これからどんどんと機能開発をするには今の構成が良い と判断しました。 (手前)徳永さん・(奥)熊本さん 山田 : DB に関して PostgreSQL ですが、 MySQL などにするということは考えなかったですか? 熊本 : そうですね、リニューアル前のアカデミーが使っていたということがあり、大きく変えるメリットはあまり無かったのが大きいです。 岸田 : あとこれは自分の感覚なのですが、PostgreSQL の方がスマートに処理できることが多いという印象があるというのもありました。 山田 : 元々サービスで使っていたというのは、やっぱり大きいですね。ORM は Prisma ですよね。例えば他に TypeORM などの選択肢もあったと思いますが、こちらはどういった経緯で使うことになりましたか? 熊本 : 色々と触ってみた結果、Prisma が 自分たちの使い方にマッチしてた のが大きかったです。がっつりと SQL を自動生成するなどの手厚い機能は、自分達の使い方では必要もなかったですし。キャッチアップのためにペアプロなどもしましたが、 メンバーも使いやすい ということが確認できました。 山田 : 今ペアプロの話が出ましたが、どのような目的でやっていたんでしょうか。 熊本 : 初期の頃はメンバー間の知識などに偏りもあったので、そういったものを実際にコードを触りながら埋めていく という目的でした。実際に、GraphQL を使ったことがなかったメンバーも早く慣れてくれました。 山田 : なるほど。ある種学習も兼ねていたんですね。最終的に現在このアーキテクチャの使い勝手としては感想としてどうでしょうか? 岸田 : 言葉は違うかもしれないですが「 普通に使い勝手が良い 」という感じです。 もちろんメンバーが習熟してきたので、色々とコード的に改善しようという部分はあったりもしますが、今のところ本当に困った問題というのは無いので、非常に良い選択だったかと思います。 NestJS のテストコードについて 山田 : テストコードは基本書いているんですよね? 熊本 : はい、バックエンドは基本的に Jest で全部書いています。フロントエンドに関しては今まさに E2E テストをどのように実施していくかを試行錯誤しているところですね。 山田 : NestJS でテストコード書くのに何か困ったこととかありましたか? 岸田 : 困っているというわけではないですが、Rails でほぼ必ず使われている Factorybot に相当する機能が欲しいですね…。 山田 : 確かに Node.js だとそういったライブラリを見かけないですね…。 岸田 : 皆さんあんまり困ってないのかな?ということで今は簡易的なものを自作して使っています。 山田 : OSS で作ったら需要があるかもしれませんね。 現状の NestJS での開発の課題感 山田 : ここまで NestJS での開発について聞いてきましたが、プロダクト開発での課題は何かあったりしますか? 岸田 : 先ほども少し話をしたメンバーの習熟度が高くなったことによるコード改善の他には、NestJS というか Node.js が Rails に比べると定番ライブラリが少なめなのがあります。 例えば、 Sidekiq のようなライブラリって便利だと思うんですが、相当するライブラリでデファクトスタンダードというようなものが Node.js だと少ない気がしています。ライブラリが一杯あるのですが、デファクトまでいかないというものが多いという感想です。 山田 : 確かに Node.js だと同じ目的のライブラリが複数あって機能がちょっとずつ違うという感じがありますね。 牧野 : あとは機能開発にリソースを取っているので、細かい部分でのライブラリのアップデートが遅れ気味というのが課題感としてあります。 今のところはとてつもなく遅れているというわけではないのですが、積り積っていくとこういった部分が負債になっていくので、対応したいと思っています。 岸田 : こういったものも含めてプロダクト開発を円滑に回すために、自分も含めた各メンバーのスキルを広げて深くしていかないといけないなというのもありますね。 チームへのオンボーディングについて 山田 : さて、話が変わって現在アカデミーで一番最後にジョインしたのが徳永さんだと思いますが、オンボーディングなどはどのような感じだったんでしょうか。学生時代に NestJS はがっつり触っていたんでしたっけ? 徳永 : いえ、学生時代はチュートリアルをしたくらいで、深く使っていたということはなかったです。ですので、最初は全然プロダクトのコードが分からないというレベルで、ほとんど未経験でした。 山田 : なるほど。ほぼ未経験で新卒研修を経て、アカデミーの開発チームにジョインして困った部分などはありましたか? 徳永 : TypeScript を使っているというのは凄く自分にとって良くて、言語でつまづくということはほとんどありませんでした。 やはり 公式ドキュメントがすごく充実していますし、コミュニティの質の高い情報も多い ので。プロダクトに関しては過去の Pull Request や Issue 上のやり取りなどで、 どういう意図で実装されたものなのかを参照できたのが大きかった です。一方でバックエンドのデザインパターンなどは経験が少なく苦労した部分がありました。その部分はドキュメントで学習したり、周りのメンバーに直接質問しながらキャッチアップしていきました。 山田 : 今年は入社後研修も TypeScript で書く割合が Ruby よりも多かったので、それがもし逆だったら、もっと苦労していたかもしれないですね。 徳永 : そうですね、その可能性は十分にあり得ます。 山田 : あとアカデミーチームは火水木に勉強会を開催していますよね。ジョブメドレーの開発チームでも噂になっています。 岸田 : これは全員で集まる勉強会というわけではなく、 月によって各自テーマを決めて火水木 1 時間ずつスキルアップのために時間を確保し、学んだことなどをアウトプットしていく という形式です。アウトプットしたものは、お互いに確認して知識を深めていこうというものですね。 山田 : それは素敵な取り組みですね。あとは Figma で開発ドキュメントをまとめているのが印象的ですよね。 牧野 : 最初は岸田さんが率先して、開発した部分のドキュメントなんかを書いていたんですが、最近はチームで共有するようなものもほとんど Figma でまとめるようになっています。 熊本 : もちろん永続化しないといけないドキュメントは README なりコンフルエンスなりに書いていくのですが、ちょっとしたものを気軽に Figma にまとめられるのはすごく良いです。 岸田 : 他の人がやっていることなんかも絶対に目につく ので、そういった意味でもドキュメントが散らばらなくて今は良く機能していると思っています。 現状の課題感とアカデミーの開発に向く人 山田 : 最後になりますが、アカデミーに来てほしい人材はどういった方でしょうか? 徳永 : この プロダクトにわくわくできる方に来てほしい と思います。福祉業界に明るい方なんかは大歓迎です! 山田 : なるほど。プロダクトのビジネス面の理解をして楽しんで開発できる方ですね。技術的な部分では何かありますか? 岸田 : 今まで TypeScript と NestJS を中心に話していましたが、実はこれらができる人というのは必須条件ではありません。どの言語でもフレームワークでも ちゃんと Web 開発が分かっている方 であればという感じです。 他には 情報感度が高い人が良い と思います。言語化が難しいのですが、開発環境や言語、ライブラリなどできちんと最新の情報が取捨選択できる方がいると、チームがより活性化すると考えています。 バックエンドでいうとベース部分などもきちんと手を入れることができて、なおかつ人に教えるのが好きな方が入ってもらえると、チームへの知識伝播などもより上手くいくと考えています。 あるとより良いなという所だと、現在アプリは React Native で作られているんですが、React Native でのアプリ開発に強い方だと嬉しいですね。ユーザーに一番使われているのはアプリなので、さらにそのユーザビリティなどを高めるためには、アプリ開発に強い方に入ってもらえるとありがたいです。 山田 : よりアカデミーというプロダクトをドライブさせてくれる方にぜひ来てほしいところですね。本日はありがとうございました! おわりに メドレーの開発チームの中でも野心的なアーキテクチャで開発をしているアカデミーチームについてお伝えしました。 サービスを伸ばすために、適切に言語やフレームワークを選択していき、柔軟性を持って開発していってるんだなというのが伝わってきたインタビューでした。少しでも興味が出た方はぜひ、下のリンクからお話できればと思います! 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
こんにちは。医療プラットフォーム本部プロダクト開発室エンジニアの中畑です。主にオンライン診療・服薬指導アプリ CLINICS の開発を担当しています。 今回は CLINICS アプリ内で扱う処方箋プレビューに透かし(watermark)を入れた話を紹介したいと思います。なぜ実施したのか、実装方法、パフォーマンスチューニングの 3 本立てでお送りしたいと思います。 課題と解決方針 まず、なぜ処方箋プレビューに透かしを入れることにしたのか。 CLINICS では診察後に患者が希望すると、かかりつけ薬局支援システム Pharms を導入している調剤薬局にてオンライン服薬指導を受けることができます。その際に医療機関から処方箋の画像ファイルや PDF をアップロードし、患者は CLINICS アプリを通じて、オンライン服薬指導を受けたい調剤薬局に処方箋データを送る必要があります。 患者はオンライン服薬指導を予約する前に医療機関からアップロードされた処方箋画像をプレビューできます。この処方箋プレビューは原本とは扱いが異なるため、患者がアプリ内でそれぞれを明確に区別できるような対応を入れる必要があり、その手段として処方箋プレビューに透かしを入れることにしたというのが理由となります。 実現・実装方法 まずは画像に透かしを入れる方法を考え、それからシステム上でどのタイミングで透かしを入れるか検討しました。前提条件ですが、処方箋プレビュー画像は以下のような形で保存されています。 フォーマット: JPEG, PNG, GIF, PDF(画像以外もある) 保存場所: S3 画像に透かしを入れる方法 透かしは技術的には 2 枚の画像をアルファブレンド 1 を使って合成した画像となります。 アルファブレンドの実現方法は言語やライブラリによっていろいろありますが、今回は処方箋を扱うシステムが Go を利用しているので、Go の以下の標準ライブラリを使って実装しました。また、画像には向き( orientation )の情報が保持されているのですが、向きを意識しないと合成時におかしくなることがあります。端末やツールによって取り扱いが異なる部分が多いので、それを解消してくれる disintegration/imaging パッケージを利用しました。 image パッケージ : ベースライブラリ ximage パッケージ : 描画処理 disintegration/imaging パッケージ : 読み込み時に向きを補正 詳細は省きますが、以下のようなコードで合成できます func Watermark ( srcFile string ) { // 元画像 imgb , _ := os . Open ( srcFile ) var img image . Image img , _ = imaging . Decode ( imgb , imaging . AutoOrientation ( true )) defer imgb . Close () // 上に重ねる透かし画像 wmb , _ := os . Open ( "watermark.png" ) watermark , _ , _ := image . Decode ( wmb ) defer wmb . Close () // 透かしを配置する場所 (画像サイズによって異なるため、実際は計算が必要) watermarkRect := image . Rectangle { image . Point { 100 , 100 }, image . Point { 200 , 300 }} // 合成処理(ここではバイリニア補間で実施) b := img . Bounds () m := image . NewNRGBA ( b ) draw . Draw ( m , b , img , image . ZP , draw . Src ) draw . BiLinear . Scale ( m , watermarkRect , watermark , watermark . Bounds (), draw . Over , nil ) // 合成画像の出力(JPEG 出力。他のエンコーダーを利用すれば他の画像形式も可) imgw , _ := os . Create ( "/tmp/dest.jpg" ) jpeg . Encode ( imgw , m , & jpeg . Options { Quality : jpeg . DefaultQuality }) defer imgw . Close () } PDF に透かしを入れる方法 また、今回の要件には PDF ファイルにも透かしを入れる必要がありました。PDF にはレイヤーという概念があり、元の PDF に対して透かしを入れるレイヤーを追加すれば実現できます。Go には PDF を標準で扱えるパッケージがなかったので、今回は pdfcpu を利用しました。 透かしのレイヤーを追加する関数が用意されており、以下のコードだけで実現できて大変便利です。 2 wm , _ := api . ImageWatermark ( "watermark.jpg" , "sc:.8 rel, rot:0" , onTop , update , pdfcpu . POINTS ) api . AddWatermarksFile ( "src.pdf" , "dest.pdf" , nil , wm , nil ) S3 画像に透かしを入れる方法 AWS を利用していると S3 に各種ファイルを保存することも多いと思います。 基本的には S3 画像を読み込んで透かし処理をすれば実現できますが、AWS には S3 のレスポンスを加工するマネージメントサービスがいくつかあり、今回は S3 Object Lambda を利用しました。 S3 Object Lambda を利用することで S3 からファイルを取得する際に Lambda による処理を実行したうえで返却することができます。これを利用して、元の画像に Lambda で透かしを入れて返却することができます。 処方箋プレビューに透かしを入れる前の構成は以下のようなイメージです。 アプリサーバーにて処方箋プレビューの S3 presigned URL(署名付き URL)を発行 クライアントに返す クライアントは受け取った URL にアクセス。処方箋プレビューが表示される これに S3 Object Lambda を利用して透かしを入れると以下のようになります。 アプリサーバーにて処方箋プレビューの S3 Object Lambda Access Point の presigned URL(署名付き URL)を発行 クライアントは受け取った URL にアクセス S3 Object Lambda Access Point は透かし処理を行う Lambda 関数を実行。その際に S3 Access Point の署名付き URL を発行し、リクエストパラメータに設定 Lambda Function は受け取った URL にアクセスし処方箋プレビューの元画像を取得して透かしを入れて返却 透かし入りの処方箋プレビューが表示される 新たに S3 Object Lambda Access Point , Lambda Function , S3 Access Point を用意する必要があって少しややこしいですが簡単に説明すると以下のような役割のものとなります。 icon title description S3 Access Point S3 Bucket に対する alias。S3 Object Lambda は必ずこれを経由して S3 にアクセスする必要がある Lambda Function S3 のファイルに対してなんらかの処理を実施する関数 S3 Object Lambda Access Point S3 Access Point に対して Lambda 関数を実行するためのアクセスポイント 透かし処理を実際呼び出す際は、アプリケーション側からは S3 の presigned URL から S3 Object Lambda Access Point の presigned URL に切り替えるだけなので、透かしありなしの切り替えも簡単です。 S3 presigned URL: https://[bucket-name].s3.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] ↓ S3 Object Lambda Access Point presigned URL: https://[s3-object-lambda-access-point]-[account-id].s3-object-lambda.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] S3 Object Lambda を利用することで以下のようなメリットがあります。 追加のインフラ構築が不要 既存サーバーのリソース追加や、新たに透かし処理用のサーバーを用意する必要がない。既存のアプリサーバーへの影響を軽微にできる Lambda が自動的にスケールを実施してくれるので負荷対策が容易 利用した分だけの課金となる(S3 Get + Lambda + S3 Object Lambda の利用料) 透かしを入れた処方箋プレビュー画像を事前に用意する必要がない 事前に用意するとリリース前に保持した画像に対しても処理する必要が出てくる (ただし、今回は特定のユーザーのみのアクセスなので不特定多数のアクセスが見込まれる場合は事前生成したほうがよいかもしれません) もちろん画像だけではなく、テキストデータなど他のものにも利用できるので、S3 に保管した機密情報等をフィルタリングして返したい用途などにもマッチします。 AWS 公式ドキュメントに詳細な解説と様々なユースケースのチュートリアルがありますので、興味がある方は是非御覧ください。 S3 Object Lambda を使用したオブジェクトの変換 パフォーマンス・チューニング ここまでで、処方箋プレビューに透かしを入れる処理自体は完成しました。ここで気になってくるのがパフォーマンスについてです。今までは S3 の画像を表示するだけでしたが透かし処理に S3 Object Lambda を使うことになったため、速度と料金が気になるところです。 様々なチューニングポイントがありますが、今回は Lambda のメモリ設定に焦点を絞ってお話したいと思います。 Lambda の CPU パフォーマンス Lambda はメモリ設定しかできませんが、メモリ量に比例して CPU 性能が向上する仕組みとなっています。 公式ドキュメント 上では 1769MB あたり 1vCPU であるとされていて、MAX の 10240MB で 6vCPU 使えるとしています。実際に Lambda の vCPU の設定を調べたブログ ( SENTIA tech blog ) では以下のようになっていたそうです。 Memory vCPUs 128 - 3008 MB 2 3009 - 5307 MB 3 5308 - 7076 MB 4 7077 - 8845 MB 5 8846 - 10240 MB 6 ただし、メモリをいくら増やしたところで関数自体がマルチスレッド・マルチプロセス化されていない場合はパフォーマンス向上が見込めないということにもなります。適切に並列処理が実装されていれば、vCPU が増える境目で大きな性能向上が期待できます。 Lambda の料金 東京リージョン(ap-northeast-1)において、2022 年 10 月現在は以下のような料金となります。 3 無料枠: 1 ヶ月あたり 100 万リクエスト、40 万 GB-秒(仮に 1 GB で動かしたとして 40 万秒使える) 128 MB だと 320 万秒、 10240 MB だと 4 万秒 有料枠: 100 万リクエストあたり 0.20 USD, GB-秒あたり 0.00001666667 USD 1 億リクエストあたり 20 USD 128MB で 1 万秒 使うと 0.02083 USD 1024MB で 1 万秒 使うと 0.16666 USD 上記は x86(amd64) の値段、arm だと 2 割引 arm は CPU パフォーマンスも 2 割向上すると言われているので、可能であれば arm を選択すると更にコスパ向上が期待できる このように見てみると、サーバーやコンテナを利用した場合はどんなに最小構成でも冗長化を考えると数十ドルはかかるので、特に頻繁に実行されない機能は Lambda によるサーバーレス化を検討すると良さそうです。 また、時間あたりで料金がかかるということは、API などネットワークアクセスで時間を使った場合も料金がかかることに注意が必要です。外部 API のパフォーマンスに依存してしまうので、なるべく Lambda は CPU を使った処理が支配的になるとコスパよく利用することができます。 ベンチマーク Lambda のパフォーマンス計測には、 AWS Lambda Power Tuning を利用しました。AWS Lambda Power Tuning は Step Functions と Lambda を使って Lambda の速度とコストを計測できるツールです。 Step Functions と Lambda をデプロイした上で、Step Functions の state machine に計測したい Lambda の ARN, memory 設定, 実行回数, payload を入力することでベンチマーク結果を取得できます。 実行すると最終結果に URL が出力され、その URL をブラウザで表示することで、グラフで結果とスコアボードで結果が可視化されます。また 2 つの結果を比較して表示することもできますので改善後に比較することも容易です。 4 AWS Lambda Power Tuning の導入 リポジトリのドキュメント に従い導入していきます。 今回は AWS Serverless Application Repository(SAR) を使って導入しました。AWS Serverless Application Repository(SAR) とはその名の通り、サーバーレスアプリケーションを管理するリポジトリですが、組織内に限らず公開することも可能で、他の人も再利用可能になっています。また、導入する際は公開ページの Deploy ボタンをポチるだけで自身の AWS アカウント上にデプロイされ、 CloudFormation の Stack として管理されます。削除も CloudFormation の Stack を削除するだけで実施できます。 AWS Lambda Power Tuning は こちらから 自身のアカウントにデプロイできます。 また、AWS や個人だけでなく Datadog や New Relic など他のパブリッシャーもサーバーレスアプリケーションを公開しているので、他にも使いたいものがないか探してみるとよいかもしれません。 AWS Lambda Power Tuning の実行 デプロイが完了すると、AWS Step Functions 上に state machine があるので、state machine にパラメータを入力することで計測ができます。 リポジトリのドキュメント にいくつかやり方が記載されています。ここでは 実行パラメータを定義した json ファイルを用意してスクリプトから実行する方法を紹介します。 まずは、 AWS Lambda Power Tuning リポジトリ を git clone し、 scripts/sample-execution-input.json を編集します。 { "lambdaARN" : "arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-performance-tuning" , "powerValues" : [ 128 , 256 , 512 , 832 , 1024 , 1536 , 1769 , 1770 , 2048 , 3008 ], "num" : 100 , "payload" : { "body" : "{ \" id \" : \" 123 \" , \" name \" : \" performance \" }" , "path" : "/api/performance" , "httpMethod" : "GET" , "isBase64Encoded" : false , "multiValueHeaders" : { "Accept" : [ "application/json" ] } }, "parallelInvocation" : false } 入力値の詳細は以下の通りです。 5 lambdaARN: Lambda Function の ARN powerValues: 計測したい Lambda のメモリを指定 num: メモリごとの Lambda 実行回数 payload: Lambda Function にわたす Event JSON の内容 parallelInvocation: ベンチマークを並列実行する 次に、 scripts/execute.sh を実行すると、ベンチマークが開始され、最後に結果 URL が出力されます。デプロイ時の設定によって、CloudFormation の Stack Name が違う場合もありますので、必要に応じて書き換えてください。 $ sh ./scripts/execute.sh -n Execution started... -n . : -n . SUCCEEDED Execution output: { "power" :128, "cost" :8.4E-9, "duration" :3.6706666666666674, "stateMachine" : { " executionCost ":4.0E-4," lambdaCost ":1.0178708203125003E-4," visualization ":" https://lambda-power-tuning.show/#gAAAAQACQAMABAAG6QbqBgAIwAs = ; NOxqQP9GmkDFII5AbxKBQLErfECGXW1AWDmGQNNNjkBpSo1ARf1nQA = = ; l08QMn1jtDJ9YzQz1pCSM5dPkDNjd9gzb9AbNPzmGzR9YzQ05vRTNA = ="}} 計測方法は以上、簡単ですね 👌 AWS Lambda Power Tuning の結果確認 計測結果を確認します。関数によって傾向は異なりますが、ここでは 2 種類のパターンを紹介します。 DB や API アクセスなど外部処理が支配的なケース(Network-intensive) Lambda 内の処理が外部処理で占めている場合は、 以下のようなグラフ になります。 赤色の実行時間がどのメモリ設定でも 4ms 前後と大きな変化がなく、青色のコストだけが右肩上がりになっています。 この場合は、Lambda の最低メモリである 128MB が最適ということになります。 CPU 実行時間が支配的なケース(CPU-intensive) Lambda 内の処理が内部処理で占めている場合は、 以下のようなグラフ になります。透かし処理は画像合成処理に多くの CPU を利用するので、こちらのパターンとなりました。 メモリが少ないと速度が出ておらず、メモリを増やすに連れて速度も改善しており、コストの増加も緩やかです。ただし、今回はプログラムがマルチスレッド・マルチプロセス対応はしていないため、コア数が増える 1770MB 以降は大きな速度改善は見られず、コストが上がる結果となっています。 よって、このケースではコストだけを考えたら低メモリが有利ですが、速度も考えたら 1024MB ~ 1770MB あたりにすることを検討すると良さそうです。 まとめ 処方箋プレビューに透かしを入れることになった背景、実現方法を紹介いたしました。 透かし処理はアルファブレンド処理を実施することで実現でき、PDF については透かし用のレイヤーを重ねることで実現できます。 S3 Object Lambda を利用することで、追加のサーバー構築をすることなく、S3 のファイルに対してフィルタリングをかけることができます。 Lambda のパフォーマンス・チューニングについては、AWS Lambda Power Tuning を利用して可視化できます。外部 API などネットワーク依存(Network-intensive)ではなく、CPU 処理が中心(CPU-intensive)になると、コスパよく利用することができます。 S3 のファイルに対してなんらかの処理を実施したいと考えてる方は、一度 S3 Object Lambda の利用を検討してみてはいかがでしょうか? また、Lambda のチューニングポイントは色々ありますが、AWS Summit Online で紹介された以下のセッションがおすすめなので、もっと深くチューニングしたい方は是非ご覧ください! AWS Lambda Performance Tuning Deep Dive〜本当に知りたいのは”ここ”だった〜 さいごに メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しております。医療という分野においては、機微な情報を扱ったり診療という大事な業務を止めないよう、可用性、パフォーマンス、セキュリティともに高いサービスレベルを求められます。興味がある方は是非ご連絡ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp Footnotes 元画像に直接文字や図形などを描画することももちろん可能ですが、透かしを画像データとしてデザイン、管理したいこともあり、今回はアルファブレンドを選択した。 アルファブレンド - wikipedia ↩ pdfcpu API document: watermark example ↩ ただし最初の 60 億 GB 秒/月。昨今は円安なのでドル建てで利用しているインフラコストも上がっており、USD 表記だとコストが変わらないようにみえるので要注意。 AWS Lambda 料金 ↩ この UI は AWS Lambda Power Tuning UI というツールで別途提供されており、 lambda-power-tuning.show ドメインでアクセスできるので、自前で用意する必要はない。URL は https://lambda-power-tuning.show/#AAEAAgADAAQ=;xKDwRUqaakU5+RtFcXLqRA==;UqkHOPJCBDjA6AM46DAEOA==;AAEAAgADAAQ=;aYP6RdyeeUUQKiVFuC76RA==;ZDoNOJy3DDiJrQs4zhENOA==;100;50 のような形式でデータが URL のパスパラメータにエンコードされている。 ↩ この他にも strategy など様々なパラメータがある: aws-lambda-power-tuning: README-INPUT-OUTPUT.md ↩
こんにちは。医療プラットフォーム本部プロダクト開発室エンジニアの中畑です。主にオンライン診療・服薬指導アプリ CLINICS の開発を担当しています。 今回は CLINICS アプリ内で扱う処方箋プレビューに透かし(watermark)を入れた話を紹介したいと思います。なぜ実施したのか、実装方法、パフォーマンスチューニングの 3 本立てでお送りしたいと思います。 課題と解決方針 まず、なぜ処方箋プレビューに透かしを入れることにしたのか。 CLINICS では診察後に患者が希望すると、かかりつけ薬局支援システム Pharms を導入している調剤薬局にてオンライン服薬指導を受けることができます。その際に医療機関から処方箋の画像ファイルや PDF をアップロードし、患者は CLINICS アプリを通じて、オンライン服薬指導を受けたい調剤薬局に処方箋データを送る必要があります。 患者はオンライン服薬指導を予約する前に医療機関からアップロードされた処方箋画像をプレビューできます。この処方箋プレビューは原本とは扱いが異なるため、患者がアプリ内でそれぞれを明確に区別できるような対応を入れる必要があり、その手段として処方箋プレビューに透かしを入れることにしたというのが理由となります。 実現・実装方法 まずは画像に透かしを入れる方法を考え、それからシステム上でどのタイミングで透かしを入れるか検討しました。前提条件ですが、処方箋プレビュー画像は以下のような形で保存されています。 フォーマット: JPEG, PNG, GIF, PDF(画像以外もある) 保存場所: S3 画像に透かしを入れる方法 透かしは技術的には 2 枚の画像をアルファブレンド 1 を使って合成した画像となります。 アルファブレンドの実現方法は言語やライブラリによっていろいろありますが、今回は処方箋を扱うシステムが Go を利用しているので、Go の以下の標準ライブラリを使って実装しました。また、画像には向き( orientation )の情報が保持されているのですが、向きを意識しないと合成時におかしくなることがあります。端末やツールによって取り扱いが異なる部分が多いので、それを解消してくれる disintegration/imaging パッケージを利用しました。 image パッケージ : ベースライブラリ ximage パッケージ : 描画処理 disintegration/imaging パッケージ : 読み込み時に向きを補正 詳細は省きますが、以下のようなコードで合成できます func Watermark ( srcFile string ) { // 元画像 imgb , _ := os . Open ( srcFile ) var img image . Image img , _ = imaging . Decode ( imgb , imaging . AutoOrientation ( true )) defer imgb . Close () // 上に重ねる透かし画像 wmb , _ := os . Open ( "watermark.png" ) watermark , _ , _ := image . Decode ( wmb ) defer wmb . Close () // 透かしを配置する場所 (画像サイズによって異なるため、実際は計算が必要) watermarkRect := image . Rectangle { image . Point { 100 , 100 }, image . Point { 200 , 300 }} // 合成処理(ここではバイリニア補間で実施) b := img . Bounds () m := image . NewNRGBA ( b ) draw . Draw ( m , b , img , image . ZP , draw . Src ) draw . BiLinear . Scale ( m , watermarkRect , watermark , watermark . Bounds (), draw . Over , nil ) // 合成画像の出力(JPEG 出力。他のエンコーダーを利用すれば他の画像形式も可) imgw , _ := os . Create ( "/tmp/dest.jpg" ) jpeg . Encode ( imgw , m , & jpeg . Options { Quality : jpeg . DefaultQuality }) defer imgw . Close () } PDF に透かしを入れる方法 また、今回の要件には PDF ファイルにも透かしを入れる必要がありました。PDF にはレイヤーという概念があり、元の PDF に対して透かしを入れるレイヤーを追加すれば実現できます。Go には PDF を標準で扱えるパッケージがなかったので、今回は pdfcpu を利用しました。 透かしのレイヤーを追加する関数が用意されており、以下のコードだけで実現できて大変便利です。 2 wm , _ := api . ImageWatermark ( "watermark.jpg" , "sc:.8 rel, rot:0" , onTop , update , pdfcpu . POINTS ) api . AddWatermarksFile ( "src.pdf" , "dest.pdf" , nil , wm , nil ) S3 画像に透かしを入れる方法 AWS を利用していると S3 に各種ファイルを保存することも多いと思います。 基本的には S3 画像を読み込んで透かし処理をすれば実現できますが、AWS には S3 のレスポンスを加工するマネージメントサービスがいくつかあり、今回は S3 Object Lambda を利用しました。 S3 Object Lambda を利用することで S3 からファイルを取得する際に Lambda による処理を実行したうえで返却することができます。これを利用して、元の画像に Lambda で透かしを入れて返却することができます。 処方箋プレビューに透かしを入れる前の構成は以下のようなイメージです。 アプリサーバーにて処方箋プレビューの S3 presigned URL(署名付き URL)を発行 クライアントに返す クライアントは受け取った URL にアクセス。処方箋プレビューが表示される これに S3 Object Lambda を利用して透かしを入れると以下のようになります。 アプリサーバーにて処方箋プレビューの S3 Object Lambda Access Point の presigned URL(署名付き URL)を発行 クライアントは受け取った URL にアクセス S3 Object Lambda Access Point は透かし処理を行う Lambda 関数を実行。その際に S3 Access Point の署名付き URL を発行し、リクエストパラメータに設定 Lambda Function は受け取った URL にアクセスし処方箋プレビューの元画像を取得して透かしを入れて返却 透かし入りの処方箋プレビューが表示される 新たに S3 Object Lambda Access Point , Lambda Function , S3 Access Point を用意する必要があって少しややこしいですが簡単に説明すると以下のような役割のものとなります。 icon title description S3 Access Point S3 Bucket に対する alias。S3 Object Lambda は必ずこれを経由して S3 にアクセスする必要がある Lambda Function S3 のファイルに対してなんらかの処理を実施する関数 S3 Object Lambda Access Point S3 Access Point に対して Lambda 関数を実行するためのアクセスポイント 透かし処理を実際呼び出す際は、アプリケーション側からは S3 の presigned URL から S3 Object Lambda Access Point の presigned URL に切り替えるだけなので、透かしありなしの切り替えも簡単です。 S3 presigned URL: https://[bucket-name].s3.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] ↓ S3 Object Lambda Access Point presigned URL: https://[s3-object-lambda-access-point]-[account-id].s3-object-lambda.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] S3 Object Lambda を利用することで以下のようなメリットがあります。 追加のインフラ構築が不要 既存サーバーのリソース追加や、新たに透かし処理用のサーバーを用意する必要がない。既存のアプリサーバーへの影響を軽微にできる Lambda が自動的にスケールを実施してくれるので負荷対策が容易 利用した分だけの課金となる(S3 Get + Lambda + S3 Object Lambda の利用料) 透かしを入れた処方箋プレビュー画像を事前に用意する必要がない 事前に用意するとリリース前に保持した画像に対しても処理する必要が出てくる (ただし、今回は特定のユーザーのみのアクセスなので不特定多数のアクセスが見込まれる場合は事前生成したほうがよいかもしれません) もちろん画像だけではなく、テキストデータなど他のものにも利用できるので、S3 に保管した機密情報等をフィルタリングして返したい用途などにもマッチします。 AWS 公式ドキュメントに詳細な解説と様々なユースケースのチュートリアルがありますので、興味がある方は是非御覧ください。 S3 Object Lambda を使用したオブジェクトの変換 パフォーマンス・チューニング ここまでで、処方箋プレビューに透かしを入れる処理自体は完成しました。ここで気になってくるのがパフォーマンスについてです。今までは S3 の画像を表示するだけでしたが透かし処理に S3 Object Lambda を使うことになったため、速度と料金が気になるところです。 様々なチューニングポイントがありますが、今回は Lambda のメモリ設定に焦点を絞ってお話したいと思います。 Lambda の CPU パフォーマンス Lambda はメモリ設定しかできませんが、メモリ量に比例して CPU 性能が向上する仕組みとなっています。 公式ドキュメント 上では 1769MB あたり 1vCPU であるとされていて、MAX の 10240MB で 6vCPU 使えるとしています。実際に Lambda の vCPU の設定を調べたブログ ( SENTIA tech blog ) では以下のようになっていたそうです。 Memory vCPUs 128 - 3008 MB 2 3009 - 5307 MB 3 5308 - 7076 MB 4 7077 - 8845 MB 5 8846 - 10240 MB 6 ただし、メモリをいくら増やしたところで関数自体がマルチスレッド・マルチプロセス化されていない場合はパフォーマンス向上が見込めないということにもなります。適切に並列処理が実装されていれば、vCPU が増える境目で大きな性能向上が期待できます。 Lambda の料金 東京リージョン(ap-northeast-1)において、2022 年 10 月現在は以下のような料金となります。 3 無料枠: 1 ヶ月あたり 100 万リクエスト、40 万 GB-秒(仮に 1 GB で動かしたとして 40 万秒使える) 128 MB だと 320 万秒、 10240 MB だと 4 万秒 有料枠: 100 万リクエストあたり 0.20 USD, GB-秒あたり 0.00001666667 USD 1 億リクエストあたり 20 USD 128MB で 1 万秒 使うと 0.02083 USD 1024MB で 1 万秒 使うと 0.16666 USD 上記は x86(amd64) の値段、arm だと 2 割引 arm は CPU パフォーマンスも 2 割向上すると言われているので、可能であれば arm を選択すると更にコスパ向上が期待できる このように見てみると、サーバーやコンテナを利用した場合はどんなに最小構成でも冗長化を考えると数十ドルはかかるので、特に頻繁に実行されない機能は Lambda によるサーバーレス化を検討すると良さそうです。 また、時間あたりで料金がかかるということは、API などネットワークアクセスで時間を使った場合も料金がかかることに注意が必要です。外部 API のパフォーマンスに依存してしまうので、なるべく Lambda は CPU を使った処理が支配的になるとコスパよく利用することができます。 ベンチマーク Lambda のパフォーマンス計測には、 AWS Lambda Power Tuning を利用しました。AWS Lambda Power Tuning は Step Functions と Lambda を使って Lambda の速度とコストを計測できるツールです。 Step Functions と Lambda をデプロイした上で、Step Functions の state machine に計測したい Lambda の ARN, memory 設定, 実行回数, payload を入力することでベンチマーク結果を取得できます。 実行すると最終結果に URL が出力され、その URL をブラウザで表示することで、グラフで結果とスコアボードで結果が可視化されます。また 2 つの結果を比較して表示することもできますので改善後に比較することも容易です。 4 AWS Lambda Power Tuning の導入 リポジトリのドキュメント に従い導入していきます。 今回は AWS Serverless Application Repository(SAR) を使って導入しました。AWS Serverless Application Repository(SAR) とはその名の通り、サーバーレスアプリケーションを管理するリポジトリですが、組織内に限らず公開することも可能で、他の人も再利用可能になっています。また、導入する際は公開ページの Deploy ボタンをポチるだけで自身の AWS アカウント上にデプロイされ、 CloudFormation の Stack として管理されます。削除も CloudFormation の Stack を削除するだけで実施できます。 AWS Lambda Power Tuning は こちらから 自身のアカウントにデプロイできます。 また、AWS や個人だけでなく Datadog や New Relic など他のパブリッシャーもサーバーレスアプリケーションを公開しているので、他にも使いたいものがないか探してみるとよいかもしれません。 AWS Lambda Power Tuning の実行 デプロイが完了すると、AWS Step Functions 上に state machine があるので、state machine にパラメータを入力することで計測ができます。 リポジトリのドキュメント にいくつかやり方が記載されています。ここでは 実行パラメータを定義した json ファイルを用意してスクリプトから実行する方法を紹介します。 まずは、 AWS Lambda Power Tuning リポジトリ を git clone し、 scripts/sample-execution-input.json を編集します。 { "lambdaARN" : "arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-performance-tuning" , "powerValues" : [ 128 , 256 , 512 , 832 , 1024 , 1536 , 1769 , 1770 , 2048 , 3008 ], "num" : 100 , "payload" : { "body" : "{ \" id \" : \" 123 \" , \" name \" : \" performance \" }" , "path" : "/api/performance" , "httpMethod" : "GET" , "isBase64Encoded" : false , "multiValueHeaders" : { "Accept" : [ "application/json" ] } }, "parallelInvocation" : false } 入力値の詳細は以下の通りです。 5 lambdaARN: Lambda Function の ARN powerValues: 計測したい Lambda のメモリを指定 num: メモリごとの Lambda 実行回数 payload: Lambda Function にわたす Event JSON の内容 parallelInvocation: ベンチマークを並列実行する 次に、 scripts/execute.sh を実行すると、ベンチマークが開始され、最後に結果 URL が出力されます。デプロイ時の設定によって、CloudFormation の Stack Name が違う場合もありますので、必要に応じて書き換えてください。 $ sh ./scripts/execute.sh -n Execution started... -n . : -n . SUCCEEDED Execution output: { "power" :128, "cost" :8.4E-9, "duration" :3.6706666666666674, "stateMachine" : { " executionCost ":4.0E-4," lambdaCost ":1.0178708203125003E-4," visualization ":" https://lambda-power-tuning.show/#gAAAAQACQAMABAAG6QbqBgAIwAs = ; NOxqQP9GmkDFII5AbxKBQLErfECGXW1AWDmGQNNNjkBpSo1ARf1nQA = = ; l08QMn1jtDJ9YzQz1pCSM5dPkDNjd9gzb9AbNPzmGzR9YzQ05vRTNA = ="}} 計測方法は以上、簡単ですね 👌 AWS Lambda Power Tuning の結果確認 計測結果を確認します。関数によって傾向は異なりますが、ここでは 2 種類のパターンを紹介します。 DB や API アクセスなど外部処理が支配的なケース(Network-intensive) Lambda 内の処理が外部処理で占めている場合は、 以下のようなグラフ になります。 赤色の実行時間がどのメモリ設定でも 4ms 前後と大きな変化がなく、青色のコストだけが右肩上がりになっています。 この場合は、Lambda の最低メモリである 128MB が最適ということになります。 CPU 実行時間が支配的なケース(CPU-intensive) Lambda 内の処理が内部処理で占めている場合は、 以下のようなグラフ になります。透かし処理は画像合成処理に多くの CPU を利用するので、こちらのパターンとなりました。 メモリが少ないと速度が出ておらず、メモリを増やすに連れて速度も改善しており、コストの増加も緩やかです。ただし、今回はプログラムがマルチスレッド・マルチプロセス対応はしていないため、コア数が増える 1770MB 以降は大きな速度改善は見られず、コストが上がる結果となっています。 よって、このケースではコストだけを考えたら低メモリが有利ですが、速度も考えたら 1024MB ~ 1770MB あたりにすることを検討すると良さそうです。 まとめ 処方箋プレビューに透かしを入れることになった背景、実現方法を紹介いたしました。 透かし処理はアルファブレンド処理を実施することで実現でき、PDF については透かし用のレイヤーを重ねることで実現できます。 S3 Object Lambda を利用することで、追加のサーバー構築をすることなく、S3 のファイルに対してフィルタリングをかけることができます。 Lambda のパフォーマンス・チューニングについては、AWS Lambda Power Tuning を利用して可視化できます。外部 API などネットワーク依存(Network-intensive)ではなく、CPU 処理が中心(CPU-intensive)になると、コスパよく利用することができます。 S3 のファイルに対してなんらかの処理を実施したいと考えてる方は、一度 S3 Object Lambda の利用を検討してみてはいかがでしょうか? また、Lambda のチューニングポイントは色々ありますが、AWS Summit Online で紹介された以下のセッションがおすすめなので、もっと深くチューニングしたい方は是非ご覧ください! AWS Lambda Performance Tuning Deep Dive〜本当に知りたいのは”ここ”だった〜 さいごに メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しております。医療という分野においては、機微な情報を扱ったり診療という大事な業務を止めないよう、可用性、パフォーマンス、セキュリティともに高いサービスレベルを求められます。興味がある方は是非ご連絡ください。 https://www.medley.jp/jobs/ Footnotes 元画像に直接文字や図形などを描画することももちろん可能ですが、透かしを画像データとしてデザイン、管理したいこともあり、今回はアルファブレンドを選択した。 アルファブレンド - wikipedia ↩ pdfcpu API document: watermark example ↩ ただし最初の 60 億 GB 秒/月。昨今は円安なのでドル建てで利用しているインフラコストも上がっており、USD 表記だとコストが変わらないようにみえるので要注意。 AWS Lambda 料金 ↩ この UI は AWS Lambda Power Tuning UI というツールで別途提供されており、 lambda-power-tuning.show ドメインでアクセスできるので、自前で用意する必要はない。URL は https://lambda-power-tuning.show/#AAEAAgADAAQ=;xKDwRUqaakU5+RtFcXLqRA==;UqkHOPJCBDjA6AM46DAEOA==;AAEAAgADAAQ=;aYP6RdyeeUUQKiVFuC76RA==;ZDoNOJy3DDiJrQs4zhENOA==;100;50 のような形式でデータが URL のパスパラメータにエンコードされている。 ↩ この他にも strategy など様々なパラメータがある: aws-lambda-power-tuning: README-INPUT-OUTPUT.md ↩
こんにちは。医療プラットフォーム本部プロダクト開発室エンジニアの中畑です。主にオンライン診療・服薬指導アプリ CLINICS の開発を担当しています。 今回は CLINICS アプリ内で扱う処方箋プレビューに透かし(watermark)を入れた話を紹介したいと思います。なぜ実施したのか、実装方法、パフォーマンスチューニングの 3 本立てでお送りしたいと思います。 課題と解決方針 まず、なぜ処方箋プレビューに透かしを入れることにしたのか。 CLINICS では診察後に患者が希望すると、かかりつけ薬局支援システム Pharms を導入している調剤薬局にてオンライン服薬指導を受けることができます。その際に医療機関から処方箋の画像ファイルや PDF をアップロードし、患者は CLINICS アプリを通じて、オンライン服薬指導を受けたい調剤薬局に処方箋データを送る必要があります。 患者はオンライン服薬指導を予約する前に医療機関からアップロードされた処方箋画像をプレビューできます。この処方箋プレビューは原本とは扱いが異なるため、患者がアプリ内でそれぞれを明確に区別できるような対応を入れる必要があり、その手段として処方箋プレビューに透かしを入れることにしたというのが理由となります。 実現・実装方法 まずは画像に透かしを入れる方法を考え、それからシステム上でどのタイミングで透かしを入れるか検討しました。前提条件ですが、処方箋プレビュー画像は以下のような形で保存されています。 フォーマット: JPEG, PNG, GIF, PDF(画像以外もある) 保存場所: S3 画像に透かしを入れる方法 透かしは技術的には 2 枚の画像をアルファブレンド 1 を使って合成した画像となります。 アルファブレンドの実現方法は言語やライブラリによっていろいろありますが、今回は処方箋を扱うシステムが Go を利用しているので、Go の以下の標準ライブラリを使って実装しました。また、画像には向き( orientation )の情報が保持されているのですが、向きを意識しないと合成時におかしくなることがあります。端末やツールによって取り扱いが異なる部分が多いので、それを解消してくれる disintegration/imaging パッケージを利用しました。 image パッケージ : ベースライブラリ ximage パッケージ : 描画処理 disintegration/imaging パッケージ : 読み込み時に向きを補正 詳細は省きますが、以下のようなコードで合成できます func Watermark ( srcFile string ) { // 元画像 imgb , _ := os . Open ( srcFile ) var img image . Image img , _ = imaging . Decode ( imgb , imaging . AutoOrientation ( true )) defer imgb . Close () // 上に重ねる透かし画像 wmb , _ := os . Open ( "watermark.png" ) watermark , _ , _ := image . Decode ( wmb ) defer wmb . Close () // 透かしを配置する場所 (画像サイズによって異なるため、実際は計算が必要) watermarkRect := image . Rectangle { image . Point { 100 , 100 }, image . Point { 200 , 300 }} // 合成処理(ここではバイリニア補間で実施) b := img . Bounds () m := image . NewNRGBA ( b ) draw . Draw ( m , b , img , image . ZP , draw . Src ) draw . BiLinear . Scale ( m , watermarkRect , watermark , watermark . Bounds (), draw . Over , nil ) // 合成画像の出力(JPEG 出力。他のエンコーダーを利用すれば他の画像形式も可) imgw , _ := os . Create ( "/tmp/dest.jpg" ) jpeg . Encode ( imgw , m , & jpeg . Options { Quality : jpeg . DefaultQuality }) defer imgw . Close () } PDF に透かしを入れる方法 また、今回の要件には PDF ファイルにも透かしを入れる必要がありました。PDF にはレイヤーという概念があり、元の PDF に対して透かしを入れるレイヤーを追加すれば実現できます。Go には PDF を標準で扱えるパッケージがなかったので、今回は pdfcpu を利用しました。 透かしのレイヤーを追加する関数が用意されており、以下のコードだけで実現できて大変便利です。 2 wm , _ := api . ImageWatermark ( "watermark.jpg" , "sc:.8 rel, rot:0" , onTop , update , pdfcpu . POINTS ) api . AddWatermarksFile ( "src.pdf" , "dest.pdf" , nil , wm , nil ) S3 画像に透かしを入れる方法 AWS を利用していると S3 に各種ファイルを保存することも多いと思います。 基本的には S3 画像を読み込んで透かし処理をすれば実現できますが、AWS には S3 のレスポンスを加工するマネージメントサービスがいくつかあり、今回は S3 Object Lambda を利用しました。 S3 Object Lambda を利用することで S3 からファイルを取得する際に Lambda による処理を実行したうえで返却することができます。これを利用して、元の画像に Lambda で透かしを入れて返却することができます。 処方箋プレビューに透かしを入れる前の構成は以下のようなイメージです。 アプリサーバーにて処方箋プレビューの S3 presigned URL(署名付き URL)を発行 クライアントに返す クライアントは受け取った URL にアクセス。処方箋プレビューが表示される これに S3 Object Lambda を利用して透かしを入れると以下のようになります。 アプリサーバーにて処方箋プレビューの S3 Object Lambda Access Point の presigned URL(署名付き URL)を発行 クライアントは受け取った URL にアクセス S3 Object Lambda Access Point は透かし処理を行う Lambda 関数を実行。その際に S3 Access Point の署名付き URL を発行し、リクエストパラメータに設定 Lambda Function は受け取った URL にアクセスし処方箋プレビューの元画像を取得して透かしを入れて返却 透かし入りの処方箋プレビューが表示される 新たに S3 Object Lambda Access Point , Lambda Function , S3 Access Point を用意する必要があって少しややこしいですが簡単に説明すると以下のような役割のものとなります。 icon title description S3 Access Point S3 Bucket に対する alias。S3 Object Lambda は必ずこれを経由して S3 にアクセスする必要がある Lambda Function S3 のファイルに対してなんらかの処理を実施する関数 S3 Object Lambda Access Point S3 Access Point に対して Lambda 関数を実行するためのアクセスポイント 透かし処理を実際呼び出す際は、アプリケーション側からは S3 の presigned URL から S3 Object Lambda Access Point の presigned URL に切り替えるだけなので、透かしありなしの切り替えも簡単です。 S3 presigned URL: https://[bucket-name].s3.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] ↓ S3 Object Lambda Access Point presigned URL: https://[s3-object-lambda-access-point]-[account-id].s3-object-lambda.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] S3 Object Lambda を利用することで以下のようなメリットがあります。 追加のインフラ構築が不要 既存サーバーのリソース追加や、新たに透かし処理用のサーバーを用意する必要がない。既存のアプリサーバーへの影響を軽微にできる Lambda が自動的にスケールを実施してくれるので負荷対策が容易 利用した分だけの課金となる(S3 Get + Lambda + S3 Object Lambda の利用料) 透かしを入れた処方箋プレビュー画像を事前に用意する必要がない 事前に用意するとリリース前に保持した画像に対しても処理する必要が出てくる (ただし、今回は特定のユーザーのみのアクセスなので不特定多数のアクセスが見込まれる場合は事前生成したほうがよいかもしれません) もちろん画像だけではなく、テキストデータなど他のものにも利用できるので、S3 に保管した機密情報等をフィルタリングして返したい用途などにもマッチします。 AWS 公式ドキュメントに詳細な解説と様々なユースケースのチュートリアルがありますので、興味がある方は是非御覧ください。 S3 Object Lambda を使用したオブジェクトの変換 パフォーマンス・チューニング ここまでで、処方箋プレビューに透かしを入れる処理自体は完成しました。ここで気になってくるのがパフォーマンスについてです。今までは S3 の画像を表示するだけでしたが透かし処理に S3 Object Lambda を使うことになったため、速度と料金が気になるところです。 様々なチューニングポイントがありますが、今回は Lambda のメモリ設定に焦点を絞ってお話したいと思います。 Lambda の CPU パフォーマンス Lambda はメモリ設定しかできませんが、メモリ量に比例して CPU 性能が向上する仕組みとなっています。 公式ドキュメント 上では 1769MB あたり 1vCPU であるとされていて、MAX の 10240MB で 6vCPU 使えるとしています。実際に Lambda の vCPU の設定を調べたブログ ( SENTIA tech blog ) では以下のようになっていたそうです。 Memory vCPUs 128 - 3008 MB 2 3009 - 5307 MB 3 5308 - 7076 MB 4 7077 - 8845 MB 5 8846 - 10240 MB 6 ただし、メモリをいくら増やしたところで関数自体がマルチスレッド・マルチプロセス化されていない場合はパフォーマンス向上が見込めないということにもなります。適切に並列処理が実装されていれば、vCPU が増える境目で大きな性能向上が期待できます。 Lambda の料金 東京リージョン(ap-northeast-1)において、2022 年 10 月現在は以下のような料金となります。 3 無料枠: 1 ヶ月あたり 100 万リクエスト、40 万 GB-秒(仮に 1 GB で動かしたとして 40 万秒使える) 128 MB だと 320 万秒、 10240 MB だと 4 万秒 有料枠: 100 万リクエストあたり 0.20 USD, GB-秒あたり 0.00001666667 USD 1 億リクエストあたり 20 USD 128MB で 1 万秒 使うと 0.02083 USD 1024MB で 1 万秒 使うと 0.16666 USD 上記は x86(amd64) の値段、arm だと 2 割引 arm は CPU パフォーマンスも 2 割向上すると言われているので、可能であれば arm を選択すると更にコスパ向上が期待できる このように見てみると、サーバーやコンテナを利用した場合はどんなに最小構成でも冗長化を考えると数十ドルはかかるので、特に頻繁に実行されない機能は Lambda によるサーバーレス化を検討すると良さそうです。 また、時間あたりで料金がかかるということは、API などネットワークアクセスで時間を使った場合も料金がかかることに注意が必要です。外部 API のパフォーマンスに依存してしまうので、なるべく Lambda は CPU を使った処理が支配的になるとコスパよく利用することができます。 ベンチマーク Lambda のパフォーマンス計測には、 AWS Lambda Power Tuning を利用しました。AWS Lambda Power Tuning は Step Functions と Lambda を使って Lambda の速度とコストを計測できるツールです。 Step Functions と Lambda をデプロイした上で、Step Functions の state machine に計測したい Lambda の ARN, memory 設定, 実行回数, payload を入力することでベンチマーク結果を取得できます。 実行すると最終結果に URL が出力され、その URL をブラウザで表示することで、グラフで結果とスコアボードで結果が可視化されます。また 2 つの結果を比較して表示することもできますので改善後に比較することも容易です。 4 AWS Lambda Power Tuning の導入 リポジトリのドキュメント に従い導入していきます。 今回は AWS Serverless Application Repository(SAR) を使って導入しました。AWS Serverless Application Repository(SAR) とはその名の通り、サーバーレスアプリケーションを管理するリポジトリですが、組織内に限らず公開することも可能で、他の人も再利用可能になっています。また、導入する際は公開ページの Deploy ボタンをポチるだけで自身の AWS アカウント上にデプロイされ、 CloudFormation の Stack として管理されます。削除も CloudFormation の Stack を削除するだけで実施できます。 AWS Lambda Power Tuning は こちらから 自身のアカウントにデプロイできます。 また、AWS や個人だけでなく Datadog や New Relic など他のパブリッシャーもサーバーレスアプリケーションを公開しているので、他にも使いたいものがないか探してみるとよいかもしれません。 AWS Lambda Power Tuning の実行 デプロイが完了すると、AWS Step Functions 上に state machine があるので、state machine にパラメータを入力することで計測ができます。 リポジトリのドキュメント にいくつかやり方が記載されています。ここでは 実行パラメータを定義した json ファイルを用意してスクリプトから実行する方法を紹介します。 まずは、 AWS Lambda Power Tuning リポジトリ を git clone し、 scripts/sample-execution-input.json を編集します。 { "lambdaARN" : "arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-performance-tuning" , "powerValues" : [ 128 , 256 , 512 , 832 , 1024 , 1536 , 1769 , 1770 , 2048 , 3008 ], "num" : 100 , "payload" : { "body" : "{ \" id \" : \" 123 \" , \" name \" : \" performance \" }" , "path" : "/api/performance" , "httpMethod" : "GET" , "isBase64Encoded" : false , "multiValueHeaders" : { "Accept" : [ "application/json" ] } }, "parallelInvocation" : false } 入力値の詳細は以下の通りです。 5 lambdaARN: Lambda Function の ARN powerValues: 計測したい Lambda のメモリを指定 num: メモリごとの Lambda 実行回数 payload: Lambda Function にわたす Event JSON の内容 parallelInvocation: ベンチマークを並列実行する 次に、 scripts/execute.sh を実行すると、ベンチマークが開始され、最後に結果 URL が出力されます。デプロイ時の設定によって、CloudFormation の Stack Name が違う場合もありますので、必要に応じて書き換えてください。 $ sh ./scripts/execute.sh -n Execution started... -n . : -n . SUCCEEDED Execution output: { "power" :128, "cost" :8.4E-9, "duration" :3.6706666666666674, "stateMachine" : { " executionCost ":4.0E-4," lambdaCost ":1.0178708203125003E-4," visualization ":" https://lambda-power-tuning.show/#gAAAAQACQAMABAAG6QbqBgAIwAs = ; NOxqQP9GmkDFII5AbxKBQLErfECGXW1AWDmGQNNNjkBpSo1ARf1nQA = = ; l08QMn1jtDJ9YzQz1pCSM5dPkDNjd9gzb9AbNPzmGzR9YzQ05vRTNA = ="}} 計測方法は以上、簡単ですね 👌 AWS Lambda Power Tuning の結果確認 計測結果を確認します。関数によって傾向は異なりますが、ここでは 2 種類のパターンを紹介します。 DB や API アクセスなど外部処理が支配的なケース(Network-intensive) Lambda 内の処理が外部処理で占めている場合は、 以下のようなグラフ になります。 赤色の実行時間がどのメモリ設定でも 4ms 前後と大きな変化がなく、青色のコストだけが右肩上がりになっています。 この場合は、Lambda の最低メモリである 128MB が最適ということになります。 CPU 実行時間が支配的なケース(CPU-intensive) Lambda 内の処理が内部処理で占めている場合は、 以下のようなグラフ になります。透かし処理は画像合成処理に多くの CPU を利用するので、こちらのパターンとなりました。 メモリが少ないと速度が出ておらず、メモリを増やすに連れて速度も改善しており、コストの増加も緩やかです。ただし、今回はプログラムがマルチスレッド・マルチプロセス対応はしていないため、コア数が増える 1770MB 以降は大きな速度改善は見られず、コストが上がる結果となっています。 よって、このケースではコストだけを考えたら低メモリが有利ですが、速度も考えたら 1024MB ~ 1770MB あたりにすることを検討すると良さそうです。 まとめ 処方箋プレビューに透かしを入れることになった背景、実現方法を紹介いたしました。 透かし処理はアルファブレンド処理を実施することで実現でき、PDF については透かし用のレイヤーを重ねることで実現できます。 S3 Object Lambda を利用することで、追加のサーバー構築をすることなく、S3 のファイルに対してフィルタリングをかけることができます。 Lambda のパフォーマンス・チューニングについては、AWS Lambda Power Tuning を利用して可視化できます。外部 API などネットワーク依存(Network-intensive)ではなく、CPU 処理が中心(CPU-intensive)になると、コスパよく利用することができます。 S3 のファイルに対してなんらかの処理を実施したいと考えてる方は、一度 S3 Object Lambda の利用を検討してみてはいかがでしょうか? また、Lambda のチューニングポイントは色々ありますが、AWS Summit Online で紹介された以下のセッションがおすすめなので、もっと深くチューニングしたい方は是非ご覧ください! AWS Lambda Performance Tuning Deep Dive〜本当に知りたいのは”ここ”だった〜 さいごに メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しております。医療という分野においては、機微な情報を扱ったり診療という大事な業務を止めないよう、可用性、パフォーマンス、セキュリティともに高いサービスレベルを求められます。興味がある方は是非ご連絡ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp Footnotes 元画像に直接文字や図形などを描画することももちろん可能ですが、透かしを画像データとしてデザイン、管理したいこともあり、今回はアルファブレンドを選択した。 アルファブレンド - wikipedia ↩ pdfcpu API document: watermark example ↩ ただし最初の 60 億 GB 秒/月。昨今は円安なのでドル建てで利用しているインフラコストも上がっており、USD 表記だとコストが変わらないようにみえるので要注意。 AWS Lambda 料金 ↩ この UI は AWS Lambda Power Tuning UI というツールで別途提供されており、 lambda-power-tuning.show ドメインでアクセスできるので、自前で用意する必要はない。URL は https://lambda-power-tuning.show/#AAEAAgADAAQ=;xKDwRUqaakU5+RtFcXLqRA==;UqkHOPJCBDjA6AM46DAEOA==;AAEAAgADAAQ=;aYP6RdyeeUUQKiVFuC76RA==;ZDoNOJy3DDiJrQs4zhENOA==;100;50 のような形式でデータが URL のパスパラメータにエンコードされている。 ↩ この他にも strategy など様々なパラメータがある: aws-lambda-power-tuning: README-INPUT-OUTPUT.md ↩
こんにちは。医療プラットフォーム本部プロダクト開発室エンジニアの中畑です。主にオンライン診療・服薬指導アプリ CLINICS の開発を担当しています。 今回は CLINICS アプリ内で扱う処方箋プレビューに透かし(watermark)を入れた話を紹介したいと思います。なぜ実施したのか、実装方法、パフォーマンスチューニングの 3 本立てでお送りしたいと思います。 課題と解決方針 まず、なぜ処方箋プレビューに透かしを入れることにしたのか。 CLINICS では診察後に患者が希望すると、かかりつけ薬局支援システム Pharms を導入している調剤薬局にてオンライン服薬指導を受けることができます。その際に医療機関から処方箋の画像ファイルや PDF をアップロードし、患者は CLINICS アプリを通じて、オンライン服薬指導を受けたい調剤薬局に処方箋データを送る必要があります。 患者はオンライン服薬指導を予約する前に医療機関からアップロードされた処方箋画像をプレビューできます。この処方箋プレビューは原本とは扱いが異なるため、患者がアプリ内でそれぞれを明確に区別できるような対応を入れる必要があり、その手段として処方箋プレビューに透かしを入れることにしたというのが理由となります。 実現・実装方法 まずは画像に透かしを入れる方法を考え、それからシステム上でどのタイミングで透かしを入れるか検討しました。前提条件ですが、処方箋プレビュー画像は以下のような形で保存されています。 フォーマット: JPEG, PNG, GIF, PDF(画像以外もある) 保存場所: S3 画像に透かしを入れる方法 透かしは技術的には 2 枚の画像をアルファブレンド 1 を使って合成した画像となります。 アルファブレンドの実現方法は言語やライブラリによっていろいろありますが、今回は処方箋を扱うシステムが Go を利用しているので、Go の以下の標準ライブラリを使って実装しました。また、画像には向き( orientation )の情報が保持されているのですが、向きを意識しないと合成時におかしくなることがあります。端末やツールによって取り扱いが異なる部分が多いので、それを解消してくれる disintegration/imaging パッケージを利用しました。 image パッケージ : ベースライブラリ ximage パッケージ : 描画処理 disintegration/imaging パッケージ : 読み込み時に向きを補正 詳細は省きますが、以下のようなコードで合成できます func Watermark ( srcFile string ) { // 元画像 imgb , _ := os . Open ( srcFile ) var img image . Image img , _ = imaging . Decode ( imgb , imaging . AutoOrientation ( true )) defer imgb . Close () // 上に重ねる透かし画像 wmb , _ := os . Open ( "watermark.png" ) watermark , _ , _ := image . Decode ( wmb ) defer wmb . Close () // 透かしを配置する場所 (画像サイズによって異なるため、実際は計算が必要) watermarkRect := image . Rectangle { image . Point { 100 , 100 }, image . Point { 200 , 300 }} // 合成処理(ここではバイリニア補間で実施) b := img . Bounds () m := image . NewNRGBA ( b ) draw . Draw ( m , b , img , image . ZP , draw . Src ) draw . BiLinear . Scale ( m , watermarkRect , watermark , watermark . Bounds (), draw . Over , nil ) // 合成画像の出力(JPEG 出力。他のエンコーダーを利用すれば他の画像形式も可) imgw , _ := os . Create ( "/tmp/dest.jpg" ) jpeg . Encode ( imgw , m , & jpeg . Options { Quality : jpeg . DefaultQuality }) defer imgw . Close () } PDF に透かしを入れる方法 また、今回の要件には PDF ファイルにも透かしを入れる必要がありました。PDF にはレイヤーという概念があり、元の PDF に対して透かしを入れるレイヤーを追加すれば実現できます。Go には PDF を標準で扱えるパッケージがなかったので、今回は pdfcpu を利用しました。 透かしのレイヤーを追加する関数が用意されており、以下のコードだけで実現できて大変便利です。 2 wm , _ := api . ImageWatermark ( "watermark.jpg" , "sc:.8 rel, rot:0" , onTop , update , pdfcpu . POINTS ) api . AddWatermarksFile ( "src.pdf" , "dest.pdf" , nil , wm , nil ) S3 画像に透かしを入れる方法 AWS を利用していると S3 に各種ファイルを保存することも多いと思います。 基本的には S3 画像を読み込んで透かし処理をすれば実現できますが、AWS には S3 のレスポンスを加工するマネージメントサービスがいくつかあり、今回は S3 Object Lambda を利用しました。 S3 Object Lambda を利用することで S3 からファイルを取得する際に Lambda による処理を実行したうえで返却することができます。これを利用して、元の画像に Lambda で透かしを入れて返却することができます。 処方箋プレビューに透かしを入れる前の構成は以下のようなイメージです。 アプリサーバーにて処方箋プレビューの S3 presigned URL(署名付き URL)を発行 クライアントに返す クライアントは受け取った URL にアクセス。処方箋プレビューが表示される これに S3 Object Lambda を利用して透かしを入れると以下のようになります。 アプリサーバーにて処方箋プレビューの S3 Object Lambda Access Point の presigned URL(署名付き URL)を発行 クライアントは受け取った URL にアクセス S3 Object Lambda Access Point は透かし処理を行う Lambda 関数を実行。その際に S3 Access Point の署名付き URL を発行し、リクエストパラメータに設定 Lambda Function は受け取った URL にアクセスし処方箋プレビューの元画像を取得して透かしを入れて返却 透かし入りの処方箋プレビューが表示される 新たに S3 Object Lambda Access Point , Lambda Function , S3 Access Point を用意する必要があって少しややこしいですが簡単に説明すると以下のような役割のものとなります。 icon title description S3 Access Point S3 Bucket に対する alias。S3 Object Lambda は必ずこれを経由して S3 にアクセスする必要がある Lambda Function S3 のファイルに対してなんらかの処理を実施する関数 S3 Object Lambda Access Point S3 Access Point に対して Lambda 関数を実行するためのアクセスポイント 透かし処理を実際呼び出す際は、アプリケーション側からは S3 の presigned URL から S3 Object Lambda Access Point の presigned URL に切り替えるだけなので、透かしありなしの切り替えも簡単です。 S3 presigned URL: https://[bucket-name].s3.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] ↓ S3 Object Lambda Access Point presigned URL: https://[s3-object-lambda-access-point]-[account-id].s3-object-lambda.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] S3 Object Lambda を利用することで以下のようなメリットがあります。 追加のインフラ構築が不要 既存サーバーのリソース追加や、新たに透かし処理用のサーバーを用意する必要がない。既存のアプリサーバーへの影響を軽微にできる Lambda が自動的にスケールを実施してくれるので負荷対策が容易 利用した分だけの課金となる(S3 Get + Lambda + S3 Object Lambda の利用料) 透かしを入れた処方箋プレビュー画像を事前に用意する必要がない 事前に用意するとリリース前に保持した画像に対しても処理する必要が出てくる (ただし、今回は特定のユーザーのみのアクセスなので不特定多数のアクセスが見込まれる場合は事前生成したほうがよいかもしれません) もちろん画像だけではなく、テキストデータなど他のものにも利用できるので、S3 に保管した機密情報等をフィルタリングして返したい用途などにもマッチします。 AWS 公式ドキュメントに詳細な解説と様々なユースケースのチュートリアルがありますので、興味がある方は是非御覧ください。 S3 Object Lambda を使用したオブジェクトの変換 パフォーマンス・チューニング ここまでで、処方箋プレビューに透かしを入れる処理自体は完成しました。ここで気になってくるのがパフォーマンスについてです。今までは S3 の画像を表示するだけでしたが透かし処理に S3 Object Lambda を使うことになったため、速度と料金が気になるところです。 様々なチューニングポイントがありますが、今回は Lambda のメモリ設定に焦点を絞ってお話したいと思います。 Lambda の CPU パフォーマンス Lambda はメモリ設定しかできませんが、メモリ量に比例して CPU 性能が向上する仕組みとなっています。 公式ドキュメント 上では 1769MB あたり 1vCPU であるとされていて、MAX の 10240MB で 6vCPU 使えるとしています。実際に Lambda の vCPU の設定を調べたブログ ( SENTIA tech blog ) では以下のようになっていたそうです。 Memory vCPUs 128 - 3008 MB 2 3009 - 5307 MB 3 5308 - 7076 MB 4 7077 - 8845 MB 5 8846 - 10240 MB 6 ただし、メモリをいくら増やしたところで関数自体がマルチスレッド・マルチプロセス化されていない場合はパフォーマンス向上が見込めないということにもなります。適切に並列処理が実装されていれば、vCPU が増える境目で大きな性能向上が期待できます。 Lambda の料金 東京リージョン(ap-northeast-1)において、2022 年 10 月現在は以下のような料金となります。 3 無料枠: 1 ヶ月あたり 100 万リクエスト、40 万 GB-秒(仮に 1 GB で動かしたとして 40 万秒使える) 128 MB だと 320 万秒、 10240 MB だと 4 万秒 有料枠: 100 万リクエストあたり 0.20 USD, GB-秒あたり 0.00001666667 USD 1 億リクエストあたり 20 USD 128MB で 1 万秒 使うと 0.02083 USD 1024MB で 1 万秒 使うと 0.16666 USD 上記は x86(amd64) の値段、arm だと 2 割引 arm は CPU パフォーマンスも 2 割向上すると言われているので、可能であれば arm を選択すると更にコスパ向上が期待できる このように見てみると、サーバーやコンテナを利用した場合はどんなに最小構成でも冗長化を考えると数十ドルはかかるので、特に頻繁に実行されない機能は Lambda によるサーバーレス化を検討すると良さそうです。 また、時間あたりで料金がかかるということは、API などネットワークアクセスで時間を使った場合も料金がかかることに注意が必要です。外部 API のパフォーマンスに依存してしまうので、なるべく Lambda は CPU を使った処理が支配的になるとコスパよく利用することができます。 ベンチマーク Lambda のパフォーマンス計測には、 AWS Lambda Power Tuning を利用しました。AWS Lambda Power Tuning は Step Functions と Lambda を使って Lambda の速度とコストを計測できるツールです。 Step Functions と Lambda をデプロイした上で、Step Functions の state machine に計測したい Lambda の ARN, memory 設定, 実行回数, payload を入力することでベンチマーク結果を取得できます。 実行すると最終結果に URL が出力され、その URL をブラウザで表示することで、グラフで結果とスコアボードで結果が可視化されます。また 2 つの結果を比較して表示することもできますので改善後に比較することも容易です。 4 AWS Lambda Power Tuning の導入 リポジトリのドキュメント に従い導入していきます。 今回は AWS Serverless Application Repository(SAR) を使って導入しました。AWS Serverless Application Repository(SAR) とはその名の通り、サーバーレスアプリケーションを管理するリポジトリですが、組織内に限らず公開することも可能で、他の人も再利用可能になっています。また、導入する際は公開ページの Deploy ボタンをポチるだけで自身の AWS アカウント上にデプロイされ、 CloudFormation の Stack として管理されます。削除も CloudFormation の Stack を削除するだけで実施できます。 AWS Lambda Power Tuning は こちらから 自身のアカウントにデプロイできます。 また、AWS や個人だけでなく Datadog や New Relic など他のパブリッシャーもサーバーレスアプリケーションを公開しているので、他にも使いたいものがないか探してみるとよいかもしれません。 AWS Lambda Power Tuning の実行 デプロイが完了すると、AWS Step Functions 上に state machine があるので、state machine にパラメータを入力することで計測ができます。 リポジトリのドキュメント にいくつかやり方が記載されています。ここでは 実行パラメータを定義した json ファイルを用意してスクリプトから実行する方法を紹介します。 まずは、 AWS Lambda Power Tuning リポジトリ を git clone し、 scripts/sample-execution-input.json を編集します。 { "lambdaARN" : "arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-performance-tuning" , "powerValues" : [ 128 , 256 , 512 , 832 , 1024 , 1536 , 1769 , 1770 , 2048 , 3008 ], "num" : 100 , "payload" : { "body" : "{ \" id \" : \" 123 \" , \" name \" : \" performance \" }" , "path" : "/api/performance" , "httpMethod" : "GET" , "isBase64Encoded" : false , "multiValueHeaders" : { "Accept" : [ "application/json" ] } }, "parallelInvocation" : false } 入力値の詳細は以下の通りです。 5 lambdaARN: Lambda Function の ARN powerValues: 計測したい Lambda のメモリを指定 num: メモリごとの Lambda 実行回数 payload: Lambda Function にわたす Event JSON の内容 parallelInvocation: ベンチマークを並列実行する 次に、 scripts/execute.sh を実行すると、ベンチマークが開始され、最後に結果 URL が出力されます。デプロイ時の設定によって、CloudFormation の Stack Name が違う場合もありますので、必要に応じて書き換えてください。 $ sh ./scripts/execute.sh -n Execution started... -n . : -n . SUCCEEDED Execution output: { "power" :128, "cost" :8.4E-9, "duration" :3.6706666666666674, "stateMachine" : { " executionCost ":4.0E-4," lambdaCost ":1.0178708203125003E-4," visualization ":" https://lambda-power-tuning.show/#gAAAAQACQAMABAAG6QbqBgAIwAs = ; NOxqQP9GmkDFII5AbxKBQLErfECGXW1AWDmGQNNNjkBpSo1ARf1nQA = = ; l08QMn1jtDJ9YzQz1pCSM5dPkDNjd9gzb9AbNPzmGzR9YzQ05vRTNA = ="}} 計測方法は以上、簡単ですね 👌 AWS Lambda Power Tuning の結果確認 計測結果を確認します。関数によって傾向は異なりますが、ここでは 2 種類のパターンを紹介します。 DB や API アクセスなど外部処理が支配的なケース(Network-intensive) Lambda 内の処理が外部処理で占めている場合は、 以下のようなグラフ になります。 赤色の実行時間がどのメモリ設定でも 4ms 前後と大きな変化がなく、青色のコストだけが右肩上がりになっています。 この場合は、Lambda の最低メモリである 128MB が最適ということになります。 CPU 実行時間が支配的なケース(CPU-intensive) Lambda 内の処理が内部処理で占めている場合は、 以下のようなグラフ になります。透かし処理は画像合成処理に多くの CPU を利用するので、こちらのパターンとなりました。 メモリが少ないと速度が出ておらず、メモリを増やすに連れて速度も改善しており、コストの増加も緩やかです。ただし、今回はプログラムがマルチスレッド・マルチプロセス対応はしていないため、コア数が増える 1770MB 以降は大きな速度改善は見られず、コストが上がる結果となっています。 よって、このケースではコストだけを考えたら低メモリが有利ですが、速度も考えたら 1024MB ~ 1770MB あたりにすることを検討すると良さそうです。 まとめ 処方箋プレビューに透かしを入れることになった背景、実現方法を紹介いたしました。 透かし処理はアルファブレンド処理を実施することで実現でき、PDF については透かし用のレイヤーを重ねることで実現できます。 S3 Object Lambda を利用することで、追加のサーバー構築をすることなく、S3 のファイルに対してフィルタリングをかけることができます。 Lambda のパフォーマンス・チューニングについては、AWS Lambda Power Tuning を利用して可視化できます。外部 API などネットワーク依存(Network-intensive)ではなく、CPU 処理が中心(CPU-intensive)になると、コスパよく利用することができます。 S3 のファイルに対してなんらかの処理を実施したいと考えてる方は、一度 S3 Object Lambda の利用を検討してみてはいかがでしょうか? また、Lambda のチューニングポイントは色々ありますが、AWS Summit Online で紹介された以下のセッションがおすすめなので、もっと深くチューニングしたい方は是非ご覧ください! AWS Lambda Performance Tuning Deep Dive〜本当に知りたいのは”ここ”だった〜 さいごに メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しております。医療という分野においては、機微な情報を扱ったり診療という大事な業務を止めないよう、可用性、パフォーマンス、セキュリティともに高いサービスレベルを求められます。興味がある方は是非ご連絡ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp Footnotes 元画像に直接文字や図形などを描画することももちろん可能ですが、透かしを画像データとしてデザイン、管理したいこともあり、今回はアルファブレンドを選択した。 アルファブレンド - wikipedia ↩ pdfcpu API document: watermark example ↩ ただし最初の 60 億 GB 秒/月。昨今は円安なのでドル建てで利用しているインフラコストも上がっており、USD 表記だとコストが変わらないようにみえるので要注意。 AWS Lambda 料金 ↩ この UI は AWS Lambda Power Tuning UI というツールで別途提供されており、 lambda-power-tuning.show ドメインでアクセスできるので、自前で用意する必要はない。URL は https://lambda-power-tuning.show/#AAEAAgADAAQ=;xKDwRUqaakU5+RtFcXLqRA==;UqkHOPJCBDjA6AM46DAEOA==;AAEAAgADAAQ=;aYP6RdyeeUUQKiVFuC76RA==;ZDoNOJy3DDiJrQs4zhENOA==;100;50 のような形式でデータが URL のパスパラメータにエンコードされている。 ↩ この他にも strategy など様々なパラメータがある: aws-lambda-power-tuning: README-INPUT-OUTPUT.md ↩
こんにちは。医療プラットフォーム本部プロダクト開発室エンジニアの中畑です。主にオンライン診療・服薬指導アプリ CLINICS の開発を担当しています。 今回は CLINICS アプリ内で扱う処方箋プレビューに透かし(watermark)を入れた話を紹介したいと思います。なぜ実施したのか、実装方法、パフォーマンスチューニングの 3 本立てでお送りしたいと思います。 課題と解決方針 まず、なぜ処方箋プレビューに透かしを入れることにしたのか。 CLINICS では診察後に患者が希望すると、かかりつけ薬局支援システム Pharms を導入している調剤薬局にてオンライン服薬指導を受けることができます。その際に医療機関から処方箋の画像ファイルや PDF をアップロードし、患者は CLINICS アプリを通じて、オンライン服薬指導を受けたい調剤薬局に処方箋データを送る必要があります。 患者はオンライン服薬指導を予約する前に医療機関からアップロードされた処方箋画像をプレビューできます。この処方箋プレビューは原本とは扱いが異なるため、患者がアプリ内でそれぞれを明確に区別できるような対応を入れる必要があり、その手段として処方箋プレビューに透かしを入れることにしたというのが理由となります。 実現・実装方法 まずは画像に透かしを入れる方法を考え、それからシステム上でどのタイミングで透かしを入れるか検討しました。前提条件ですが、処方箋プレビュー画像は以下のような形で保存されています。 フォーマット: JPEG, PNG, GIF, PDF(画像以外もある) 保存場所: S3 画像に透かしを入れる方法 透かしは技術的には 2 枚の画像をアルファブレンド 1 を使って合成した画像となります。 アルファブレンドの実現方法は言語やライブラリによっていろいろありますが、今回は処方箋を扱うシステムが Go を利用しているので、Go の以下の標準ライブラリを使って実装しました。また、画像には向き( orientation )の情報が保持されているのですが、向きを意識しないと合成時におかしくなることがあります。端末やツールによって取り扱いが異なる部分が多いので、それを解消してくれる disintegration/imaging パッケージを利用しました。 image パッケージ : ベースライブラリ ximage パッケージ : 描画処理 disintegration/imaging パッケージ : 読み込み時に向きを補正 詳細は省きますが、以下のようなコードで合成できます func Watermark ( srcFile string ) { // 元画像 imgb , _ := os . Open ( srcFile ) var img image . Image img , _ = imaging . Decode ( imgb , imaging . AutoOrientation ( true )) defer imgb . Close () // 上に重ねる透かし画像 wmb , _ := os . Open ( "watermark.png" ) watermark , _ , _ := image . Decode ( wmb ) defer wmb . Close () // 透かしを配置する場所 (画像サイズによって異なるため、実際は計算が必要) watermarkRect := image . Rectangle { image . Point { 100 , 100 }, image . Point { 200 , 300 }} // 合成処理(ここではバイリニア補間で実施) b := img . Bounds () m := image . NewNRGBA ( b ) draw . Draw ( m , b , img , image . ZP , draw . Src ) draw . BiLinear . Scale ( m , watermarkRect , watermark , watermark . Bounds (), draw . Over , nil ) // 合成画像の出力(JPEG 出力。他のエンコーダーを利用すれば他の画像形式も可) imgw , _ := os . Create ( "/tmp/dest.jpg" ) jpeg . Encode ( imgw , m , & jpeg . Options { Quality : jpeg . DefaultQuality }) defer imgw . Close () } PDF に透かしを入れる方法 また、今回の要件には PDF ファイルにも透かしを入れる必要がありました。PDF にはレイヤーという概念があり、元の PDF に対して透かしを入れるレイヤーを追加すれば実現できます。Go には PDF を標準で扱えるパッケージがなかったので、今回は pdfcpu を利用しました。 透かしのレイヤーを追加する関数が用意されており、以下のコードだけで実現できて大変便利です。 2 wm , _ := api . ImageWatermark ( "watermark.jpg" , "sc:.8 rel, rot:0" , onTop , update , pdfcpu . POINTS ) api . AddWatermarksFile ( "src.pdf" , "dest.pdf" , nil , wm , nil ) S3 画像に透かしを入れる方法 AWS を利用していると S3 に各種ファイルを保存することも多いと思います。 基本的には S3 画像を読み込んで透かし処理をすれば実現できますが、AWS には S3 のレスポンスを加工するマネージメントサービスがいくつかあり、今回は S3 Object Lambda を利用しました。 S3 Object Lambda を利用することで S3 からファイルを取得する際に Lambda による処理を実行したうえで返却することができます。これを利用して、元の画像に Lambda で透かしを入れて返却することができます。 処方箋プレビューに透かしを入れる前の構成は以下のようなイメージです。 アプリサーバーにて処方箋プレビューの S3 presigned URL(署名付き URL)を発行 クライアントに返す クライアントは受け取った URL にアクセス。処方箋プレビューが表示される これに S3 Object Lambda を利用して透かしを入れると以下のようになります。 アプリサーバーにて処方箋プレビューの S3 Object Lambda Access Point の presigned URL(署名付き URL)を発行 クライアントは受け取った URL にアクセス S3 Object Lambda Access Point は透かし処理を行う Lambda 関数を実行。その際に S3 Access Point の署名付き URL を発行し、リクエストパラメータに設定 Lambda Function は受け取った URL にアクセスし処方箋プレビューの元画像を取得して透かしを入れて返却 透かし入りの処方箋プレビューが表示される 新たに S3 Object Lambda Access Point , Lambda Function , S3 Access Point を用意する必要があって少しややこしいですが簡単に説明すると以下のような役割のものとなります。 icon title description S3 Access Point S3 Bucket に対する alias。S3 Object Lambda は必ずこれを経由して S3 にアクセスする必要がある Lambda Function S3 のファイルに対してなんらかの処理を実施する関数 S3 Object Lambda Access Point S3 Access Point に対して Lambda 関数を実行するためのアクセスポイント 透かし処理を実際呼び出す際は、アプリケーション側からは S3 の presigned URL から S3 Object Lambda Access Point の presigned URL に切り替えるだけなので、透かしありなしの切り替えも簡単です。 S3 presigned URL: https://[bucket-name].s3.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] ↓ S3 Object Lambda Access Point presigned URL: https://[s3-object-lambda-access-point]-[account-id].s3-object-lambda.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] S3 Object Lambda を利用することで以下のようなメリットがあります。 追加のインフラ構築が不要 既存サーバーのリソース追加や、新たに透かし処理用のサーバーを用意する必要がない。既存のアプリサーバーへの影響を軽微にできる Lambda が自動的にスケールを実施してくれるので負荷対策が容易 利用した分だけの課金となる(S3 Get + Lambda + S3 Object Lambda の利用料) 透かしを入れた処方箋プレビュー画像を事前に用意する必要がない 事前に用意するとリリース前に保持した画像に対しても処理する必要が出てくる (ただし、今回は特定のユーザーのみのアクセスなので不特定多数のアクセスが見込まれる場合は事前生成したほうがよいかもしれません) もちろん画像だけではなく、テキストデータなど他のものにも利用できるので、S3 に保管した機密情報等をフィルタリングして返したい用途などにもマッチします。 AWS 公式ドキュメントに詳細な解説と様々なユースケースのチュートリアルがありますので、興味がある方は是非御覧ください。 S3 Object Lambda を使用したオブジェクトの変換 パフォーマンス・チューニング ここまでで、処方箋プレビューに透かしを入れる処理自体は完成しました。ここで気になってくるのがパフォーマンスについてです。今までは S3 の画像を表示するだけでしたが透かし処理に S3 Object Lambda を使うことになったため、速度と料金が気になるところです。 様々なチューニングポイントがありますが、今回は Lambda のメモリ設定に焦点を絞ってお話したいと思います。 Lambda の CPU パフォーマンス Lambda はメモリ設定しかできませんが、メモリ量に比例して CPU 性能が向上する仕組みとなっています。 公式ドキュメント 上では 1769MB あたり 1vCPU であるとされていて、MAX の 10240MB で 6vCPU 使えるとしています。実際に Lambda の vCPU の設定を調べたブログ ( SENTIA tech blog ) では以下のようになっていたそうです。 Memory vCPUs 128 - 3008 MB 2 3009 - 5307 MB 3 5308 - 7076 MB 4 7077 - 8845 MB 5 8846 - 10240 MB 6 ただし、メモリをいくら増やしたところで関数自体がマルチスレッド・マルチプロセス化されていない場合はパフォーマンス向上が見込めないということにもなります。適切に並列処理が実装されていれば、vCPU が増える境目で大きな性能向上が期待できます。 Lambda の料金 東京リージョン(ap-northeast-1)において、2022 年 10 月現在は以下のような料金となります。 3 無料枠: 1 ヶ月あたり 100 万リクエスト、40 万 GB-秒(仮に 1 GB で動かしたとして 40 万秒使える) 128 MB だと 320 万秒、 10240 MB だと 4 万秒 有料枠: 100 万リクエストあたり 0.20 USD, GB-秒あたり 0.00001666667 USD 1 億リクエストあたり 20 USD 128MB で 1 万秒 使うと 0.02083 USD 1024MB で 1 万秒 使うと 0.16666 USD 上記は x86(amd64) の値段、arm だと 2 割引 arm は CPU パフォーマンスも 2 割向上すると言われているので、可能であれば arm を選択すると更にコスパ向上が期待できる このように見てみると、サーバーやコンテナを利用した場合はどんなに最小構成でも冗長化を考えると数十ドルはかかるので、特に頻繁に実行されない機能は Lambda によるサーバーレス化を検討すると良さそうです。 また、時間あたりで料金がかかるということは、API などネットワークアクセスで時間を使った場合も料金がかかることに注意が必要です。外部 API のパフォーマンスに依存してしまうので、なるべく Lambda は CPU を使った処理が支配的になるとコスパよく利用することができます。 ベンチマーク Lambda のパフォーマンス計測には、 AWS Lambda Power Tuning を利用しました。AWS Lambda Power Tuning は Step Functions と Lambda を使って Lambda の速度とコストを計測できるツールです。 Step Functions と Lambda をデプロイした上で、Step Functions の state machine に計測したい Lambda の ARN, memory 設定, 実行回数, payload を入力することでベンチマーク結果を取得できます。 実行すると最終結果に URL が出力され、その URL をブラウザで表示することで、グラフで結果とスコアボードで結果が可視化されます。また 2 つの結果を比較して表示することもできますので改善後に比較することも容易です。 4 AWS Lambda Power Tuning の導入 リポジトリのドキュメント に従い導入していきます。 今回は AWS Serverless Application Repository(SAR) を使って導入しました。AWS Serverless Application Repository(SAR) とはその名の通り、サーバーレスアプリケーションを管理するリポジトリですが、組織内に限らず公開することも可能で、他の人も再利用可能になっています。また、導入する際は公開ページの Deploy ボタンをポチるだけで自身の AWS アカウント上にデプロイされ、 CloudFormation の Stack として管理されます。削除も CloudFormation の Stack を削除するだけで実施できます。 AWS Lambda Power Tuning は こちらから 自身のアカウントにデプロイできます。 また、AWS や個人だけでなく Datadog や New Relic など他のパブリッシャーもサーバーレスアプリケーションを公開しているので、他にも使いたいものがないか探してみるとよいかもしれません。 AWS Lambda Power Tuning の実行 デプロイが完了すると、AWS Step Functions 上に state machine があるので、state machine にパラメータを入力することで計測ができます。 リポジトリのドキュメント にいくつかやり方が記載されています。ここでは 実行パラメータを定義した json ファイルを用意してスクリプトから実行する方法を紹介します。 まずは、 AWS Lambda Power Tuning リポジトリ を git clone し、 scripts/sample-execution-input.json を編集します。 { "lambdaARN" : "arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-performance-tuning" , "powerValues" : [ 128 , 256 , 512 , 832 , 1024 , 1536 , 1769 , 1770 , 2048 , 3008 ], "num" : 100 , "payload" : { "body" : "{ \" id \" : \" 123 \" , \" name \" : \" performance \" }" , "path" : "/api/performance" , "httpMethod" : "GET" , "isBase64Encoded" : false , "multiValueHeaders" : { "Accept" : [ "application/json" ] } }, "parallelInvocation" : false } 入力値の詳細は以下の通りです。 5 lambdaARN: Lambda Function の ARN powerValues: 計測したい Lambda のメモリを指定 num: メモリごとの Lambda 実行回数 payload: Lambda Function にわたす Event JSON の内容 parallelInvocation: ベンチマークを並列実行する 次に、 scripts/execute.sh を実行すると、ベンチマークが開始され、最後に結果 URL が出力されます。デプロイ時の設定によって、CloudFormation の Stack Name が違う場合もありますので、必要に応じて書き換えてください。 $ sh ./scripts/execute.sh -n Execution started... -n . : -n . SUCCEEDED Execution output: { "power" :128, "cost" :8.4E-9, "duration" :3.6706666666666674, "stateMachine" : { " executionCost ":4.0E-4," lambdaCost ":1.0178708203125003E-4," visualization ":" https://lambda-power-tuning.show/#gAAAAQACQAMABAAG6QbqBgAIwAs = ; NOxqQP9GmkDFII5AbxKBQLErfECGXW1AWDmGQNNNjkBpSo1ARf1nQA = = ; l08QMn1jtDJ9YzQz1pCSM5dPkDNjd9gzb9AbNPzmGzR9YzQ05vRTNA = ="}} 計測方法は以上、簡単ですね 👌 AWS Lambda Power Tuning の結果確認 計測結果を確認します。関数によって傾向は異なりますが、ここでは 2 種類のパターンを紹介します。 DB や API アクセスなど外部処理が支配的なケース(Network-intensive) Lambda 内の処理が外部処理で占めている場合は、 以下のようなグラフ になります。 赤色の実行時間がどのメモリ設定でも 4ms 前後と大きな変化がなく、青色のコストだけが右肩上がりになっています。 この場合は、Lambda の最低メモリである 128MB が最適ということになります。 CPU 実行時間が支配的なケース(CPU-intensive) Lambda 内の処理が内部処理で占めている場合は、 以下のようなグラフ になります。透かし処理は画像合成処理に多くの CPU を利用するので、こちらのパターンとなりました。 メモリが少ないと速度が出ておらず、メモリを増やすに連れて速度も改善しており、コストの増加も緩やかです。ただし、今回はプログラムがマルチスレッド・マルチプロセス対応はしていないため、コア数が増える 1770MB 以降は大きな速度改善は見られず、コストが上がる結果となっています。 よって、このケースではコストだけを考えたら低メモリが有利ですが、速度も考えたら 1024MB ~ 1770MB あたりにすることを検討すると良さそうです。 まとめ 処方箋プレビューに透かしを入れることになった背景、実現方法を紹介いたしました。 透かし処理はアルファブレンド処理を実施することで実現でき、PDF については透かし用のレイヤーを重ねることで実現できます。 S3 Object Lambda を利用することで、追加のサーバー構築をすることなく、S3 のファイルに対してフィルタリングをかけることができます。 Lambda のパフォーマンス・チューニングについては、AWS Lambda Power Tuning を利用して可視化できます。外部 API などネットワーク依存(Network-intensive)ではなく、CPU 処理が中心(CPU-intensive)になると、コスパよく利用することができます。 S3 のファイルに対してなんらかの処理を実施したいと考えてる方は、一度 S3 Object Lambda の利用を検討してみてはいかがでしょうか? また、Lambda のチューニングポイントは色々ありますが、AWS Summit Online で紹介された以下のセッションがおすすめなので、もっと深くチューニングしたい方は是非ご覧ください! AWS Lambda Performance Tuning Deep Dive〜本当に知りたいのは”ここ”だった〜 さいごに メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しております。医療という分野においては、機微な情報を扱ったり診療という大事な業務を止めないよう、可用性、パフォーマンス、セキュリティともに高いサービスレベルを求められます。興味がある方は是非ご連絡ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp Footnotes 元画像に直接文字や図形などを描画することももちろん可能ですが、透かしを画像データとしてデザイン、管理したいこともあり、今回はアルファブレンドを選択した。 アルファブレンド - wikipedia ↩ pdfcpu API document: watermark example ↩ ただし最初の 60 億 GB 秒/月。昨今は円安なのでドル建てで利用しているインフラコストも上がっており、USD 表記だとコストが変わらないようにみえるので要注意。 AWS Lambda 料金 ↩ この UI は AWS Lambda Power Tuning UI というツールで別途提供されており、 lambda-power-tuning.show ドメインでアクセスできるので、自前で用意する必要はない。URL は https://lambda-power-tuning.show/#AAEAAgADAAQ=;xKDwRUqaakU5+RtFcXLqRA==;UqkHOPJCBDjA6AM46DAEOA==;AAEAAgADAAQ=;aYP6RdyeeUUQKiVFuC76RA==;ZDoNOJy3DDiJrQs4zhENOA==;100;50 のような形式でデータが URL のパスパラメータにエンコードされている。 ↩ この他にも strategy など様々なパラメータがある: aws-lambda-power-tuning: README-INPUT-OUTPUT.md ↩
こんにちは。医療プラットフォーム本部プロダクト開発室エンジニアの中畑です。主にオンライン診療・服薬指導アプリ CLINICS の開発を担当しています。 今回は CLINICS アプリ内で扱う処方箋プレビューに透かし(watermark)を入れた話を紹介したいと思います。なぜ実施したのか、実装方法、パフォーマンスチューニングの 3 本立てでお送りしたいと思います。 課題と解決方針 まず、なぜ処方箋プレビューに透かしを入れることにしたのか。 CLINICS では診察後に患者が希望すると、かかりつけ薬局支援システム Pharms を導入している調剤薬局にてオンライン服薬指導を受けることができます。その際に医療機関から処方箋の画像ファイルや PDF をアップロードし、患者は CLINICS アプリを通じて、オンライン服薬指導を受けたい調剤薬局に処方箋データを送る必要があります。 患者はオンライン服薬指導を予約する前に医療機関からアップロードされた処方箋画像をプレビューできます。この処方箋プレビューは原本とは扱いが異なるため、患者がアプリ内でそれぞれを明確に区別できるような対応を入れる必要があり、その手段として処方箋プレビューに透かしを入れることにしたというのが理由となります。 実現・実装方法 まずは画像に透かしを入れる方法を考え、それからシステム上でどのタイミングで透かしを入れるか検討しました。前提条件ですが、処方箋プレビュー画像は以下のような形で保存されています。 フォーマット: JPEG, PNG, GIF, PDF(画像以外もある) 保存場所: S3 画像に透かしを入れる方法 透かしは技術的には 2 枚の画像をアルファブレンド 1 を使って合成した画像となります。 アルファブレンドの実現方法は言語やライブラリによっていろいろありますが、今回は処方箋を扱うシステムが Go を利用しているので、Go の以下の標準ライブラリを使って実装しました。また、画像には向き( orientation )の情報が保持されているのですが、向きを意識しないと合成時におかしくなることがあります。端末やツールによって取り扱いが異なる部分が多いので、それを解消してくれる disintegration/imaging パッケージを利用しました。 image パッケージ : ベースライブラリ ximage パッケージ : 描画処理 disintegration/imaging パッケージ : 読み込み時に向きを補正 詳細は省きますが、以下のようなコードで合成できます func Watermark ( srcFile string ) { // 元画像 imgb , _ := os . Open ( srcFile ) var img image . Image img , _ = imaging . Decode ( imgb , imaging . AutoOrientation ( true )) defer imgb . Close () // 上に重ねる透かし画像 wmb , _ := os . Open ( "watermark.png" ) watermark , _ , _ := image . Decode ( wmb ) defer wmb . Close () // 透かしを配置する場所 (画像サイズによって異なるため、実際は計算が必要) watermarkRect := image . Rectangle { image . Point { 100 , 100 }, image . Point { 200 , 300 }} // 合成処理(ここではバイリニア補間で実施) b := img . Bounds () m := image . NewNRGBA ( b ) draw . Draw ( m , b , img , image . ZP , draw . Src ) draw . BiLinear . Scale ( m , watermarkRect , watermark , watermark . Bounds (), draw . Over , nil ) // 合成画像の出力(JPEG 出力。他のエンコーダーを利用すれば他の画像形式も可) imgw , _ := os . Create ( "/tmp/dest.jpg" ) jpeg . Encode ( imgw , m , & jpeg . Options { Quality : jpeg . DefaultQuality }) defer imgw . Close () } PDF に透かしを入れる方法 また、今回の要件には PDF ファイルにも透かしを入れる必要がありました。PDF にはレイヤーという概念があり、元の PDF に対して透かしを入れるレイヤーを追加すれば実現できます。Go には PDF を標準で扱えるパッケージがなかったので、今回は pdfcpu を利用しました。 透かしのレイヤーを追加する関数が用意されており、以下のコードだけで実現できて大変便利です。 2 wm , _ := api . ImageWatermark ( "watermark.jpg" , "sc:.8 rel, rot:0" , onTop , update , pdfcpu . POINTS ) api . AddWatermarksFile ( "src.pdf" , "dest.pdf" , nil , wm , nil ) S3 画像に透かしを入れる方法 AWS を利用していると S3 に各種ファイルを保存することも多いと思います。 基本的には S3 画像を読み込んで透かし処理をすれば実現できますが、AWS には S3 のレスポンスを加工するマネージメントサービスがいくつかあり、今回は S3 Object Lambda を利用しました。 S3 Object Lambda を利用することで S3 からファイルを取得する際に Lambda による処理を実行したうえで返却することができます。これを利用して、元の画像に Lambda で透かしを入れて返却することができます。 処方箋プレビューに透かしを入れる前の構成は以下のようなイメージです。 アプリサーバーにて処方箋プレビューの S3 presigned URL(署名付き URL)を発行 クライアントに返す クライアントは受け取った URL にアクセス。処方箋プレビューが表示される これに S3 Object Lambda を利用して透かしを入れると以下のようになります。 アプリサーバーにて処方箋プレビューの S3 Object Lambda Access Point の presigned URL(署名付き URL)を発行 クライアントは受け取った URL にアクセス S3 Object Lambda Access Point は透かし処理を行う Lambda 関数を実行。その際に S3 Access Point の署名付き URL を発行し、リクエストパラメータに設定 Lambda Function は受け取った URL にアクセスし処方箋プレビューの元画像を取得して透かしを入れて返却 透かし入りの処方箋プレビューが表示される 新たに S3 Object Lambda Access Point , Lambda Function , S3 Access Point を用意する必要があって少しややこしいですが簡単に説明すると以下のような役割のものとなります。 icon title description S3 Access Point S3 Bucket に対する alias。S3 Object Lambda は必ずこれを経由して S3 にアクセスする必要がある Lambda Function S3 のファイルに対してなんらかの処理を実施する関数 S3 Object Lambda Access Point S3 Access Point に対して Lambda 関数を実行するためのアクセスポイント 透かし処理を実際呼び出す際は、アプリケーション側からは S3 の presigned URL から S3 Object Lambda Access Point の presigned URL に切り替えるだけなので、透かしありなしの切り替えも簡単です。 S3 presigned URL: https://[bucket-name].s3.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] ↓ S3 Object Lambda Access Point presigned URL: https://[s3-object-lambda-access-point]-[account-id].s3-object-lambda.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] S3 Object Lambda を利用することで以下のようなメリットがあります。 追加のインフラ構築が不要 既存サーバーのリソース追加や、新たに透かし処理用のサーバーを用意する必要がない。既存のアプリサーバーへの影響を軽微にできる Lambda が自動的にスケールを実施してくれるので負荷対策が容易 利用した分だけの課金となる(S3 Get + Lambda + S3 Object Lambda の利用料) 透かしを入れた処方箋プレビュー画像を事前に用意する必要がない 事前に用意するとリリース前に保持した画像に対しても処理する必要が出てくる (ただし、今回は特定のユーザーのみのアクセスなので不特定多数のアクセスが見込まれる場合は事前生成したほうがよいかもしれません) もちろん画像だけではなく、テキストデータなど他のものにも利用できるので、S3 に保管した機密情報等をフィルタリングして返したい用途などにもマッチします。 AWS 公式ドキュメントに詳細な解説と様々なユースケースのチュートリアルがありますので、興味がある方は是非御覧ください。 S3 Object Lambda を使用したオブジェクトの変換 パフォーマンス・チューニング ここまでで、処方箋プレビューに透かしを入れる処理自体は完成しました。ここで気になってくるのがパフォーマンスについてです。今までは S3 の画像を表示するだけでしたが透かし処理に S3 Object Lambda を使うことになったため、速度と料金が気になるところです。 様々なチューニングポイントがありますが、今回は Lambda のメモリ設定に焦点を絞ってお話したいと思います。 Lambda の CPU パフォーマンス Lambda はメモリ設定しかできませんが、メモリ量に比例して CPU 性能が向上する仕組みとなっています。 公式ドキュメント 上では 1769MB あたり 1vCPU であるとされていて、MAX の 10240MB で 6vCPU 使えるとしています。実際に Lambda の vCPU の設定を調べたブログ ( SENTIA tech blog ) では以下のようになっていたそうです。 Memory vCPUs 128 - 3008 MB 2 3009 - 5307 MB 3 5308 - 7076 MB 4 7077 - 8845 MB 5 8846 - 10240 MB 6 ただし、メモリをいくら増やしたところで関数自体がマルチスレッド・マルチプロセス化されていない場合はパフォーマンス向上が見込めないということにもなります。適切に並列処理が実装されていれば、vCPU が増える境目で大きな性能向上が期待できます。 Lambda の料金 東京リージョン(ap-northeast-1)において、2022 年 10 月現在は以下のような料金となります。 3 無料枠: 1 ヶ月あたり 100 万リクエスト、40 万 GB-秒(仮に 1 GB で動かしたとして 40 万秒使える) 128 MB だと 320 万秒、 10240 MB だと 4 万秒 有料枠: 100 万リクエストあたり 0.20 USD, GB-秒あたり 0.00001666667 USD 1 億リクエストあたり 20 USD 128MB で 1 万秒 使うと 0.02083 USD 1024MB で 1 万秒 使うと 0.16666 USD 上記は x86(amd64) の値段、arm だと 2 割引 arm は CPU パフォーマンスも 2 割向上すると言われているので、可能であれば arm を選択すると更にコスパ向上が期待できる このように見てみると、サーバーやコンテナを利用した場合はどんなに最小構成でも冗長化を考えると数十ドルはかかるので、特に頻繁に実行されない機能は Lambda によるサーバーレス化を検討すると良さそうです。 また、時間あたりで料金がかかるということは、API などネットワークアクセスで時間を使った場合も料金がかかることに注意が必要です。外部 API のパフォーマンスに依存してしまうので、なるべく Lambda は CPU を使った処理が支配的になるとコスパよく利用することができます。 ベンチマーク Lambda のパフォーマンス計測には、 AWS Lambda Power Tuning を利用しました。AWS Lambda Power Tuning は Step Functions と Lambda を使って Lambda の速度とコストを計測できるツールです。 Step Functions と Lambda をデプロイした上で、Step Functions の state machine に計測したい Lambda の ARN, memory 設定, 実行回数, payload を入力することでベンチマーク結果を取得できます。 実行すると最終結果に URL が出力され、その URL をブラウザで表示することで、グラフで結果とスコアボードで結果が可視化されます。また 2 つの結果を比較して表示することもできますので改善後に比較することも容易です。 4 AWS Lambda Power Tuning の導入 リポジトリのドキュメント に従い導入していきます。 今回は AWS Serverless Application Repository(SAR) を使って導入しました。AWS Serverless Application Repository(SAR) とはその名の通り、サーバーレスアプリケーションを管理するリポジトリですが、組織内に限らず公開することも可能で、他の人も再利用可能になっています。また、導入する際は公開ページの Deploy ボタンをポチるだけで自身の AWS アカウント上にデプロイされ、 CloudFormation の Stack として管理されます。削除も CloudFormation の Stack を削除するだけで実施できます。 AWS Lambda Power Tuning は こちらから 自身のアカウントにデプロイできます。 また、AWS や個人だけでなく Datadog や New Relic など他のパブリッシャーもサーバーレスアプリケーションを公開しているので、他にも使いたいものがないか探してみるとよいかもしれません。 AWS Lambda Power Tuning の実行 デプロイが完了すると、AWS Step Functions 上に state machine があるので、state machine にパラメータを入力することで計測ができます。 リポジトリのドキュメント にいくつかやり方が記載されています。ここでは 実行パラメータを定義した json ファイルを用意してスクリプトから実行する方法を紹介します。 まずは、 AWS Lambda Power Tuning リポジトリ を git clone し、 scripts/sample-execution-input.json を編集します。 { "lambdaARN" : "arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-performance-tuning" , "powerValues" : [ 128 , 256 , 512 , 832 , 1024 , 1536 , 1769 , 1770 , 2048 , 3008 ], "num" : 100 , "payload" : { "body" : "{ \" id \" : \" 123 \" , \" name \" : \" performance \" }" , "path" : "/api/performance" , "httpMethod" : "GET" , "isBase64Encoded" : false , "multiValueHeaders" : { "Accept" : [ "application/json" ] } }, "parallelInvocation" : false } 入力値の詳細は以下の通りです。 5 lambdaARN: Lambda Function の ARN powerValues: 計測したい Lambda のメモリを指定 num: メモリごとの Lambda 実行回数 payload: Lambda Function にわたす Event JSON の内容 parallelInvocation: ベンチマークを並列実行する 次に、 scripts/execute.sh を実行すると、ベンチマークが開始され、最後に結果 URL が出力されます。デプロイ時の設定によって、CloudFormation の Stack Name が違う場合もありますので、必要に応じて書き換えてください。 $ sh ./scripts/execute.sh -n Execution started... -n . : -n . SUCCEEDED Execution output: { "power" :128, "cost" :8.4E-9, "duration" :3.6706666666666674, "stateMachine" : { " executionCost ":4.0E-4," lambdaCost ":1.0178708203125003E-4," visualization ":" https://lambda-power-tuning.show/#gAAAAQACQAMABAAG6QbqBgAIwAs = ; NOxqQP9GmkDFII5AbxKBQLErfECGXW1AWDmGQNNNjkBpSo1ARf1nQA = = ; l08QMn1jtDJ9YzQz1pCSM5dPkDNjd9gzb9AbNPzmGzR9YzQ05vRTNA = ="}} 計測方法は以上、簡単ですね 👌 AWS Lambda Power Tuning の結果確認 計測結果を確認します。関数によって傾向は異なりますが、ここでは 2 種類のパターンを紹介します。 DB や API アクセスなど外部処理が支配的なケース(Network-intensive) Lambda 内の処理が外部処理で占めている場合は、 以下のようなグラフ になります。 赤色の実行時間がどのメモリ設定でも 4ms 前後と大きな変化がなく、青色のコストだけが右肩上がりになっています。 この場合は、Lambda の最低メモリである 128MB が最適ということになります。 CPU 実行時間が支配的なケース(CPU-intensive) Lambda 内の処理が内部処理で占めている場合は、 以下のようなグラフ になります。透かし処理は画像合成処理に多くの CPU を利用するので、こちらのパターンとなりました。 メモリが少ないと速度が出ておらず、メモリを増やすに連れて速度も改善しており、コストの増加も緩やかです。ただし、今回はプログラムがマルチスレッド・マルチプロセス対応はしていないため、コア数が増える 1770MB 以降は大きな速度改善は見られず、コストが上がる結果となっています。 よって、このケースではコストだけを考えたら低メモリが有利ですが、速度も考えたら 1024MB ~ 1770MB あたりにすることを検討すると良さそうです。 まとめ 処方箋プレビューに透かしを入れることになった背景、実現方法を紹介いたしました。 透かし処理はアルファブレンド処理を実施することで実現でき、PDF については透かし用のレイヤーを重ねることで実現できます。 S3 Object Lambda を利用することで、追加のサーバー構築をすることなく、S3 のファイルに対してフィルタリングをかけることができます。 Lambda のパフォーマンス・チューニングについては、AWS Lambda Power Tuning を利用して可視化できます。外部 API などネットワーク依存(Network-intensive)ではなく、CPU 処理が中心(CPU-intensive)になると、コスパよく利用することができます。 S3 のファイルに対してなんらかの処理を実施したいと考えてる方は、一度 S3 Object Lambda の利用を検討してみてはいかがでしょうか? また、Lambda のチューニングポイントは色々ありますが、AWS Summit Online で紹介された以下のセッションがおすすめなので、もっと深くチューニングしたい方は是非ご覧ください! AWS Lambda Performance Tuning Deep Dive〜本当に知りたいのは”ここ”だった〜 さいごに メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しております。医療という分野においては、機微な情報を扱ったり診療という大事な業務を止めないよう、可用性、パフォーマンス、セキュリティともに高いサービスレベルを求められます。興味がある方は是非ご連絡ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp Footnotes 元画像に直接文字や図形などを描画することももちろん可能ですが、透かしを画像データとしてデザイン、管理したいこともあり、今回はアルファブレンドを選択した。 アルファブレンド - wikipedia ↩ pdfcpu API document: watermark example ↩ ただし最初の 60 億 GB 秒/月。昨今は円安なのでドル建てで利用しているインフラコストも上がっており、USD 表記だとコストが変わらないようにみえるので要注意。 AWS Lambda 料金 ↩ この UI は AWS Lambda Power Tuning UI というツールで別途提供されており、 lambda-power-tuning.show ドメインでアクセスできるので、自前で用意する必要はない。URL は https://lambda-power-tuning.show/#AAEAAgADAAQ=;xKDwRUqaakU5+RtFcXLqRA==;UqkHOPJCBDjA6AM46DAEOA==;AAEAAgADAAQ=;aYP6RdyeeUUQKiVFuC76RA==;ZDoNOJy3DDiJrQs4zhENOA==;100;50 のような形式でデータが URL のパスパラメータにエンコードされている。 ↩ この他にも strategy など様々なパラメータがある: aws-lambda-power-tuning: README-INPUT-OUTPUT.md ↩
こんにちは。医療プラットフォーム本部プロダクト開発室エンジニアの中畑です。主にオンライン診療・服薬指導アプリ CLINICS の開発を担当しています。 今回は CLINICS アプリ内で扱う処方箋プレビューに透かし(watermark)を入れた話を紹介したいと思います。なぜ実施したのか、実装方法、パフォーマンスチューニングの 3 本立てでお送りしたいと思います。 課題と解決方針 まず、なぜ処方箋プレビューに透かしを入れることにしたのか。 CLINICS では診察後に患者が希望すると、かかりつけ薬局支援システム Pharms を導入している調剤薬局にてオンライン服薬指導を受けることができます。その際に医療機関から処方箋の画像ファイルや PDF をアップロードし、患者は CLINICS アプリを通じて、オンライン服薬指導を受けたい調剤薬局に処方箋データを送る必要があります。 患者はオンライン服薬指導を予約する前に医療機関からアップロードされた処方箋画像をプレビューできます。この処方箋プレビューは原本とは扱いが異なるため、患者がアプリ内でそれぞれを明確に区別できるような対応を入れる必要があり、その手段として処方箋プレビューに透かしを入れることにしたというのが理由となります。 実現・実装方法 まずは画像に透かしを入れる方法を考え、それからシステム上でどのタイミングで透かしを入れるか検討しました。前提条件ですが、処方箋プレビュー画像は以下のような形で保存されています。 フォーマット: JPEG, PNG, GIF, PDF(画像以外もある) 保存場所: S3 画像に透かしを入れる方法 透かしは技術的には 2 枚の画像をアルファブレンド 1 を使って合成した画像となります。 アルファブレンドの実現方法は言語やライブラリによっていろいろありますが、今回は処方箋を扱うシステムが Go を利用しているので、Go の以下の標準ライブラリを使って実装しました。また、画像には向き( orientation )の情報が保持されているのですが、向きを意識しないと合成時におかしくなることがあります。端末やツールによって取り扱いが異なる部分が多いので、それを解消してくれる disintegration/imaging パッケージを利用しました。 image パッケージ : ベースライブラリ ximage パッケージ : 描画処理 disintegration/imaging パッケージ : 読み込み時に向きを補正 詳細は省きますが、以下のようなコードで合成できます func Watermark ( srcFile string ) { // 元画像 imgb , _ := os . Open ( srcFile ) var img image . Image img , _ = imaging . Decode ( imgb , imaging . AutoOrientation ( true )) defer imgb . Close () // 上に重ねる透かし画像 wmb , _ := os . Open ( "watermark.png" ) watermark , _ , _ := image . Decode ( wmb ) defer wmb . Close () // 透かしを配置する場所 (画像サイズによって異なるため、実際は計算が必要) watermarkRect := image . Rectangle { image . Point { 100 , 100 }, image . Point { 200 , 300 }} // 合成処理(ここではバイリニア補間で実施) b := img . Bounds () m := image . NewNRGBA ( b ) draw . Draw ( m , b , img , image . ZP , draw . Src ) draw . BiLinear . Scale ( m , watermarkRect , watermark , watermark . Bounds (), draw . Over , nil ) // 合成画像の出力(JPEG 出力。他のエンコーダーを利用すれば他の画像形式も可) imgw , _ := os . Create ( "/tmp/dest.jpg" ) jpeg . Encode ( imgw , m , & jpeg . Options { Quality : jpeg . DefaultQuality }) defer imgw . Close () } PDF に透かしを入れる方法 また、今回の要件には PDF ファイルにも透かしを入れる必要がありました。PDF にはレイヤーという概念があり、元の PDF に対して透かしを入れるレイヤーを追加すれば実現できます。Go には PDF を標準で扱えるパッケージがなかったので、今回は pdfcpu を利用しました。 透かしのレイヤーを追加する関数が用意されており、以下のコードだけで実現できて大変便利です。 2 wm , _ := api . ImageWatermark ( "watermark.jpg" , "sc:.8 rel, rot:0" , onTop , update , pdfcpu . POINTS ) api . AddWatermarksFile ( "src.pdf" , "dest.pdf" , nil , wm , nil ) S3 画像に透かしを入れる方法 AWS を利用していると S3 に各種ファイルを保存することも多いと思います。 基本的には S3 画像を読み込んで透かし処理をすれば実現できますが、AWS には S3 のレスポンスを加工するマネージメントサービスがいくつかあり、今回は S3 Object Lambda を利用しました。 S3 Object Lambda を利用することで S3 からファイルを取得する際に Lambda による処理を実行したうえで返却することができます。これを利用して、元の画像に Lambda で透かしを入れて返却することができます。 処方箋プレビューに透かしを入れる前の構成は以下のようなイメージです。 アプリサーバーにて処方箋プレビューの S3 presigned URL(署名付き URL)を発行 クライアントに返す クライアントは受け取った URL にアクセス。処方箋プレビューが表示される これに S3 Object Lambda を利用して透かしを入れると以下のようになります。 アプリサーバーにて処方箋プレビューの S3 Object Lambda Access Point の presigned URL(署名付き URL)を発行 クライアントは受け取った URL にアクセス S3 Object Lambda Access Point は透かし処理を行う Lambda 関数を実行。その際に S3 Access Point の署名付き URL を発行し、リクエストパラメータに設定 Lambda Function は受け取った URL にアクセスし処方箋プレビューの元画像を取得して透かしを入れて返却 透かし入りの処方箋プレビューが表示される 新たに S3 Object Lambda Access Point , Lambda Function , S3 Access Point を用意する必要があって少しややこしいですが簡単に説明すると以下のような役割のものとなります。 icon title description S3 Access Point S3 Bucket に対する alias。S3 Object Lambda は必ずこれを経由して S3 にアクセスする必要がある Lambda Function S3 のファイルに対してなんらかの処理を実施する関数 S3 Object Lambda Access Point S3 Access Point に対して Lambda 関数を実行するためのアクセスポイント 透かし処理を実際呼び出す際は、アプリケーション側からは S3 の presigned URL から S3 Object Lambda Access Point の presigned URL に切り替えるだけなので、透かしありなしの切り替えも簡単です。 S3 presigned URL: https://[bucket-name].s3.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] ↓ S3 Object Lambda Access Point presigned URL: https://[s3-object-lambda-access-point]-[account-id].s3-object-lambda.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] S3 Object Lambda を利用することで以下のようなメリットがあります。 追加のインフラ構築が不要 既存サーバーのリソース追加や、新たに透かし処理用のサーバーを用意する必要がない。既存のアプリサーバーへの影響を軽微にできる Lambda が自動的にスケールを実施してくれるので負荷対策が容易 利用した分だけの課金となる(S3 Get + Lambda + S3 Object Lambda の利用料) 透かしを入れた処方箋プレビュー画像を事前に用意する必要がない 事前に用意するとリリース前に保持した画像に対しても処理する必要が出てくる (ただし、今回は特定のユーザーのみのアクセスなので不特定多数のアクセスが見込まれる場合は事前生成したほうがよいかもしれません) もちろん画像だけではなく、テキストデータなど他のものにも利用できるので、S3 に保管した機密情報等をフィルタリングして返したい用途などにもマッチします。 AWS 公式ドキュメントに詳細な解説と様々なユースケースのチュートリアルがありますので、興味がある方は是非御覧ください。 S3 Object Lambda を使用したオブジェクトの変換 パフォーマンス・チューニング ここまでで、処方箋プレビューに透かしを入れる処理自体は完成しました。ここで気になってくるのがパフォーマンスについてです。今までは S3 の画像を表示するだけでしたが透かし処理に S3 Object Lambda を使うことになったため、速度と料金が気になるところです。 様々なチューニングポイントがありますが、今回は Lambda のメモリ設定に焦点を絞ってお話したいと思います。 Lambda の CPU パフォーマンス Lambda はメモリ設定しかできませんが、メモリ量に比例して CPU 性能が向上する仕組みとなっています。 公式ドキュメント 上では 1769MB あたり 1vCPU であるとされていて、MAX の 10240MB で 6vCPU 使えるとしています。実際に Lambda の vCPU の設定を調べたブログ ( SENTIA tech blog ) では以下のようになっていたそうです。 Memory vCPUs 128 - 3008 MB 2 3009 - 5307 MB 3 5308 - 7076 MB 4 7077 - 8845 MB 5 8846 - 10240 MB 6 ただし、メモリをいくら増やしたところで関数自体がマルチスレッド・マルチプロセス化されていない場合はパフォーマンス向上が見込めないということにもなります。適切に並列処理が実装されていれば、vCPU が増える境目で大きな性能向上が期待できます。 Lambda の料金 東京リージョン(ap-northeast-1)において、2022 年 10 月現在は以下のような料金となります。 3 無料枠: 1 ヶ月あたり 100 万リクエスト、40 万 GB-秒(仮に 1 GB で動かしたとして 40 万秒使える) 128 MB だと 320 万秒、 10240 MB だと 4 万秒 有料枠: 100 万リクエストあたり 0.20 USD, GB-秒あたり 0.00001666667 USD 1 億リクエストあたり 20 USD 128MB で 1 万秒 使うと 0.02083 USD 1024MB で 1 万秒 使うと 0.16666 USD 上記は x86(amd64) の値段、arm だと 2 割引 arm は CPU パフォーマンスも 2 割向上すると言われているので、可能であれば arm を選択すると更にコスパ向上が期待できる このように見てみると、サーバーやコンテナを利用した場合はどんなに最小構成でも冗長化を考えると数十ドルはかかるので、特に頻繁に実行されない機能は Lambda によるサーバーレス化を検討すると良さそうです。 また、時間あたりで料金がかかるということは、API などネットワークアクセスで時間を使った場合も料金がかかることに注意が必要です。外部 API のパフォーマンスに依存してしまうので、なるべく Lambda は CPU を使った処理が支配的になるとコスパよく利用することができます。 ベンチマーク Lambda のパフォーマンス計測には、 AWS Lambda Power Tuning を利用しました。AWS Lambda Power Tuning は Step Functions と Lambda を使って Lambda の速度とコストを計測できるツールです。 Step Functions と Lambda をデプロイした上で、Step Functions の state machine に計測したい Lambda の ARN, memory 設定, 実行回数, payload を入力することでベンチマーク結果を取得できます。 実行すると最終結果に URL が出力され、その URL をブラウザで表示することで、グラフで結果とスコアボードで結果が可視化されます。また 2 つの結果を比較して表示することもできますので改善後に比較することも容易です。 4 AWS Lambda Power Tuning の導入 リポジトリのドキュメント に従い導入していきます。 今回は AWS Serverless Application Repository(SAR) を使って導入しました。AWS Serverless Application Repository(SAR) とはその名の通り、サーバーレスアプリケーションを管理するリポジトリですが、組織内に限らず公開することも可能で、他の人も再利用可能になっています。また、導入する際は公開ページの Deploy ボタンをポチるだけで自身の AWS アカウント上にデプロイされ、 CloudFormation の Stack として管理されます。削除も CloudFormation の Stack を削除するだけで実施できます。 AWS Lambda Power Tuning は こちらから 自身のアカウントにデプロイできます。 また、AWS や個人だけでなく Datadog や New Relic など他のパブリッシャーもサーバーレスアプリケーションを公開しているので、他にも使いたいものがないか探してみるとよいかもしれません。 AWS Lambda Power Tuning の実行 デプロイが完了すると、AWS Step Functions 上に state machine があるので、state machine にパラメータを入力することで計測ができます。 リポジトリのドキュメント にいくつかやり方が記載されています。ここでは 実行パラメータを定義した json ファイルを用意してスクリプトから実行する方法を紹介します。 まずは、 AWS Lambda Power Tuning リポジトリ を git clone し、 scripts/sample-execution-input.json を編集します。 { "lambdaARN" : "arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-performance-tuning" , "powerValues" : [ 128 , 256 , 512 , 832 , 1024 , 1536 , 1769 , 1770 , 2048 , 3008 ], "num" : 100 , "payload" : { "body" : "{ \" id \" : \" 123 \" , \" name \" : \" performance \" }" , "path" : "/api/performance" , "httpMethod" : "GET" , "isBase64Encoded" : false , "multiValueHeaders" : { "Accept" : [ "application/json" ] } }, "parallelInvocation" : false } 入力値の詳細は以下の通りです。 5 lambdaARN: Lambda Function の ARN powerValues: 計測したい Lambda のメモリを指定 num: メモリごとの Lambda 実行回数 payload: Lambda Function にわたす Event JSON の内容 parallelInvocation: ベンチマークを並列実行する 次に、 scripts/execute.sh を実行すると、ベンチマークが開始され、最後に結果 URL が出力されます。デプロイ時の設定によって、CloudFormation の Stack Name が違う場合もありますので、必要に応じて書き換えてください。 $ sh ./scripts/execute.sh -n Execution started... -n . : -n . SUCCEEDED Execution output: { "power" :128, "cost" :8.4E-9, "duration" :3.6706666666666674, "stateMachine" : { " executionCost ":4.0E-4," lambdaCost ":1.0178708203125003E-4," visualization ":" https://lambda-power-tuning.show/#gAAAAQACQAMABAAG6QbqBgAIwAs = ; NOxqQP9GmkDFII5AbxKBQLErfECGXW1AWDmGQNNNjkBpSo1ARf1nQA = = ; l08QMn1jtDJ9YzQz1pCSM5dPkDNjd9gzb9AbNPzmGzR9YzQ05vRTNA = ="}} 計測方法は以上、簡単ですね 👌 AWS Lambda Power Tuning の結果確認 計測結果を確認します。関数によって傾向は異なりますが、ここでは 2 種類のパターンを紹介します。 DB や API アクセスなど外部処理が支配的なケース(Network-intensive) Lambda 内の処理が外部処理で占めている場合は、 以下のようなグラフ になります。 赤色の実行時間がどのメモリ設定でも 4ms 前後と大きな変化がなく、青色のコストだけが右肩上がりになっています。 この場合は、Lambda の最低メモリである 128MB が最適ということになります。 CPU 実行時間が支配的なケース(CPU-intensive) Lambda 内の処理が内部処理で占めている場合は、 以下のようなグラフ になります。透かし処理は画像合成処理に多くの CPU を利用するので、こちらのパターンとなりました。 メモリが少ないと速度が出ておらず、メモリを増やすに連れて速度も改善しており、コストの増加も緩やかです。ただし、今回はプログラムがマルチスレッド・マルチプロセス対応はしていないため、コア数が増える 1770MB 以降は大きな速度改善は見られず、コストが上がる結果となっています。 よって、このケースではコストだけを考えたら低メモリが有利ですが、速度も考えたら 1024MB ~ 1770MB あたりにすることを検討すると良さそうです。 まとめ 処方箋プレビューに透かしを入れることになった背景、実現方法を紹介いたしました。 透かし処理はアルファブレンド処理を実施することで実現でき、PDF については透かし用のレイヤーを重ねることで実現できます。 S3 Object Lambda を利用することで、追加のサーバー構築をすることなく、S3 のファイルに対してフィルタリングをかけることができます。 Lambda のパフォーマンス・チューニングについては、AWS Lambda Power Tuning を利用して可視化できます。外部 API などネットワーク依存(Network-intensive)ではなく、CPU 処理が中心(CPU-intensive)になると、コスパよく利用することができます。 S3 のファイルに対してなんらかの処理を実施したいと考えてる方は、一度 S3 Object Lambda の利用を検討してみてはいかがでしょうか? また、Lambda のチューニングポイントは色々ありますが、AWS Summit Online で紹介された以下のセッションがおすすめなので、もっと深くチューニングしたい方は是非ご覧ください! AWS Lambda Performance Tuning Deep Dive〜本当に知りたいのは”ここ”だった〜 さいごに メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しております。医療という分野においては、機微な情報を扱ったり診療という大事な業務を止めないよう、可用性、パフォーマンス、セキュリティともに高いサービスレベルを求められます。興味がある方は是非ご連絡ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp Footnotes 元画像に直接文字や図形などを描画することももちろん可能ですが、透かしを画像データとしてデザイン、管理したいこともあり、今回はアルファブレンドを選択した。 アルファブレンド - wikipedia ↩ pdfcpu API document: watermark example ↩ ただし最初の 60 億 GB 秒/月。昨今は円安なのでドル建てで利用しているインフラコストも上がっており、USD 表記だとコストが変わらないようにみえるので要注意。 AWS Lambda 料金 ↩ この UI は AWS Lambda Power Tuning UI というツールで別途提供されており、 lambda-power-tuning.show ドメインでアクセスできるので、自前で用意する必要はない。URL は https://lambda-power-tuning.show/#AAEAAgADAAQ=;xKDwRUqaakU5+RtFcXLqRA==;UqkHOPJCBDjA6AM46DAEOA==;AAEAAgADAAQ=;aYP6RdyeeUUQKiVFuC76RA==;ZDoNOJy3DDiJrQs4zhENOA==;100;50 のような形式でデータが URL のパスパラメータにエンコードされている。 ↩ この他にも strategy など様々なパラメータがある: aws-lambda-power-tuning: README-INPUT-OUTPUT.md ↩
こんにちは。医療プラットフォーム本部プロダクト開発室エンジニアの中畑です。主にオンライン診療・服薬指導アプリ CLINICS の開発を担当しています。 今回は CLINICS アプリ内で扱う処方箋プレビューに透かし(watermark)を入れた話を紹介したいと思います。なぜ実施したのか、実装方法、パフォーマンスチューニングの 3 本立てでお送りしたいと思います。 課題と解決方針 まず、なぜ処方箋プレビューに透かしを入れることにしたのか。 CLINICS では診察後に患者が希望すると、かかりつけ薬局支援システム Pharms を導入している調剤薬局にてオンライン服薬指導を受けることができます。その際に医療機関から処方箋の画像ファイルや PDF をアップロードし、患者は CLINICS アプリを通じて、オンライン服薬指導を受けたい調剤薬局に処方箋データを送る必要があります。 患者はオンライン服薬指導を予約する前に医療機関からアップロードされた処方箋画像をプレビューできます。この処方箋プレビューは原本とは扱いが異なるため、患者がアプリ内でそれぞれを明確に区別できるような対応を入れる必要があり、その手段として処方箋プレビューに透かしを入れることにしたというのが理由となります。 実現・実装方法 まずは画像に透かしを入れる方法を考え、それからシステム上でどのタイミングで透かしを入れるか検討しました。前提条件ですが、処方箋プレビュー画像は以下のような形で保存されています。 フォーマット: JPEG, PNG, GIF, PDF(画像以外もある) 保存場所: S3 画像に透かしを入れる方法 透かしは技術的には 2 枚の画像をアルファブレンド 1 を使って合成した画像となります。 アルファブレンドの実現方法は言語やライブラリによっていろいろありますが、今回は処方箋を扱うシステムが Go を利用しているので、Go の以下の標準ライブラリを使って実装しました。また、画像には向き( orientation )の情報が保持されているのですが、向きを意識しないと合成時におかしくなることがあります。端末やツールによって取り扱いが異なる部分が多いので、それを解消してくれる disintegration/imaging パッケージを利用しました。 image パッケージ : ベースライブラリ ximage パッケージ : 描画処理 disintegration/imaging パッケージ : 読み込み時に向きを補正 詳細は省きますが、以下のようなコードで合成できます func Watermark ( srcFile string ) { // 元画像 imgb , _ := os . Open ( srcFile ) var img image . Image img , _ = imaging . Decode ( imgb , imaging . AutoOrientation ( true )) defer imgb . Close () // 上に重ねる透かし画像 wmb , _ := os . Open ( "watermark.png" ) watermark , _ , _ := image . Decode ( wmb ) defer wmb . Close () // 透かしを配置する場所 (画像サイズによって異なるため、実際は計算が必要) watermarkRect := image . Rectangle { image . Point { 100 , 100 }, image . Point { 200 , 300 }} // 合成処理(ここではバイリニア補間で実施) b := img . Bounds () m := image . NewNRGBA ( b ) draw . Draw ( m , b , img , image . ZP , draw . Src ) draw . BiLinear . Scale ( m , watermarkRect , watermark , watermark . Bounds (), draw . Over , nil ) // 合成画像の出力(JPEG 出力。他のエンコーダーを利用すれば他の画像形式も可) imgw , _ := os . Create ( "/tmp/dest.jpg" ) jpeg . Encode ( imgw , m , & jpeg . Options { Quality : jpeg . DefaultQuality }) defer imgw . Close () } PDF に透かしを入れる方法 また、今回の要件には PDF ファイルにも透かしを入れる必要がありました。PDF にはレイヤーという概念があり、元の PDF に対して透かしを入れるレイヤーを追加すれば実現できます。Go には PDF を標準で扱えるパッケージがなかったので、今回は pdfcpu を利用しました。 透かしのレイヤーを追加する関数が用意されており、以下のコードだけで実現できて大変便利です。 2 wm , _ := api . ImageWatermark ( "watermark.jpg" , "sc:.8 rel, rot:0" , onTop , update , pdfcpu . POINTS ) api . AddWatermarksFile ( "src.pdf" , "dest.pdf" , nil , wm , nil ) S3 画像に透かしを入れる方法 AWS を利用していると S3 に各種ファイルを保存することも多いと思います。 基本的には S3 画像を読み込んで透かし処理をすれば実現できますが、AWS には S3 のレスポンスを加工するマネージメントサービスがいくつかあり、今回は S3 Object Lambda を利用しました。 S3 Object Lambda を利用することで S3 からファイルを取得する際に Lambda による処理を実行したうえで返却することができます。これを利用して、元の画像に Lambda で透かしを入れて返却することができます。 処方箋プレビューに透かしを入れる前の構成は以下のようなイメージです。 アプリサーバーにて処方箋プレビューの S3 presigned URL(署名付き URL)を発行 クライアントに返す クライアントは受け取った URL にアクセス。処方箋プレビューが表示される これに S3 Object Lambda を利用して透かしを入れると以下のようになります。 アプリサーバーにて処方箋プレビューの S3 Object Lambda Access Point の presigned URL(署名付き URL)を発行 クライアントは受け取った URL にアクセス S3 Object Lambda Access Point は透かし処理を行う Lambda 関数を実行。その際に S3 Access Point の署名付き URL を発行し、リクエストパラメータに設定 Lambda Function は受け取った URL にアクセスし処方箋プレビューの元画像を取得して透かしを入れて返却 透かし入りの処方箋プレビューが表示される 新たに S3 Object Lambda Access Point , Lambda Function , S3 Access Point を用意する必要があって少しややこしいですが簡単に説明すると以下のような役割のものとなります。 icon title description S3 Access Point S3 Bucket に対する alias。S3 Object Lambda は必ずこれを経由して S3 にアクセスする必要がある Lambda Function S3 のファイルに対してなんらかの処理を実施する関数 S3 Object Lambda Access Point S3 Access Point に対して Lambda 関数を実行するためのアクセスポイント 透かし処理を実際呼び出す際は、アプリケーション側からは S3 の presigned URL から S3 Object Lambda Access Point の presigned URL に切り替えるだけなので、透かしありなしの切り替えも簡単です。 S3 presigned URL: https://[bucket-name].s3.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] ↓ S3 Object Lambda Access Point presigned URL: https://[s3-object-lambda-access-point]-[account-id].s3-object-lambda.ap-northeast-1.amazonaws.com/prescription.jpg?[signature] S3 Object Lambda を利用することで以下のようなメリットがあります。 追加のインフラ構築が不要 既存サーバーのリソース追加や、新たに透かし処理用のサーバーを用意する必要がない。既存のアプリサーバーへの影響を軽微にできる Lambda が自動的にスケールを実施してくれるので負荷対策が容易 利用した分だけの課金となる(S3 Get + Lambda + S3 Object Lambda の利用料) 透かしを入れた処方箋プレビュー画像を事前に用意する必要がない 事前に用意するとリリース前に保持した画像に対しても処理する必要が出てくる (ただし、今回は特定のユーザーのみのアクセスなので不特定多数のアクセスが見込まれる場合は事前生成したほうがよいかもしれません) もちろん画像だけではなく、テキストデータなど他のものにも利用できるので、S3 に保管した機密情報等をフィルタリングして返したい用途などにもマッチします。 AWS 公式ドキュメントに詳細な解説と様々なユースケースのチュートリアルがありますので、興味がある方は是非御覧ください。 S3 Object Lambda を使用したオブジェクトの変換 パフォーマンス・チューニング ここまでで、処方箋プレビューに透かしを入れる処理自体は完成しました。ここで気になってくるのがパフォーマンスについてです。今までは S3 の画像を表示するだけでしたが透かし処理に S3 Object Lambda を使うことになったため、速度と料金が気になるところです。 様々なチューニングポイントがありますが、今回は Lambda のメモリ設定に焦点を絞ってお話したいと思います。 Lambda の CPU パフォーマンス Lambda はメモリ設定しかできませんが、メモリ量に比例して CPU 性能が向上する仕組みとなっています。 公式ドキュメント 上では 1769MB あたり 1vCPU であるとされていて、MAX の 10240MB で 6vCPU 使えるとしています。実際に Lambda の vCPU の設定を調べたブログ ( SENTIA tech blog ) では以下のようになっていたそうです。 Memory vCPUs 128 - 3008 MB 2 3009 - 5307 MB 3 5308 - 7076 MB 4 7077 - 8845 MB 5 8846 - 10240 MB 6 ただし、メモリをいくら増やしたところで関数自体がマルチスレッド・マルチプロセス化されていない場合はパフォーマンス向上が見込めないということにもなります。適切に並列処理が実装されていれば、vCPU が増える境目で大きな性能向上が期待できます。 Lambda の料金 東京リージョン(ap-northeast-1)において、2022 年 10 月現在は以下のような料金となります。 3 無料枠: 1 ヶ月あたり 100 万リクエスト、40 万 GB-秒(仮に 1 GB で動かしたとして 40 万秒使える) 128 MB だと 320 万秒、 10240 MB だと 4 万秒 有料枠: 100 万リクエストあたり 0.20 USD, GB-秒あたり 0.00001666667 USD 1 億リクエストあたり 20 USD 128MB で 1 万秒 使うと 0.02083 USD 1024MB で 1 万秒 使うと 0.16666 USD 上記は x86(amd64) の値段、arm だと 2 割引 arm は CPU パフォーマンスも 2 割向上すると言われているので、可能であれば arm を選択すると更にコスパ向上が期待できる このように見てみると、サーバーやコンテナを利用した場合はどんなに最小構成でも冗長化を考えると数十ドルはかかるので、特に頻繁に実行されない機能は Lambda によるサーバーレス化を検討すると良さそうです。 また、時間あたりで料金がかかるということは、API などネットワークアクセスで時間を使った場合も料金がかかることに注意が必要です。外部 API のパフォーマンスに依存してしまうので、なるべく Lambda は CPU を使った処理が支配的になるとコスパよく利用することができます。 ベンチマーク Lambda のパフォーマンス計測には、 AWS Lambda Power Tuning を利用しました。AWS Lambda Power Tuning は Step Functions と Lambda を使って Lambda の速度とコストを計測できるツールです。 Step Functions と Lambda をデプロイした上で、Step Functions の state machine に計測したい Lambda の ARN, memory 設定, 実行回数, payload を入力することでベンチマーク結果を取得できます。 実行すると最終結果に URL が出力され、その URL をブラウザで表示することで、グラフで結果とスコアボードで結果が可視化されます。また 2 つの結果を比較して表示することもできますので改善後に比較することも容易です。 4 AWS Lambda Power Tuning の導入 リポジトリのドキュメント に従い導入していきます。 今回は AWS Serverless Application Repository(SAR) を使って導入しました。AWS Serverless Application Repository(SAR) とはその名の通り、サーバーレスアプリケーションを管理するリポジトリですが、組織内に限らず公開することも可能で、他の人も再利用可能になっています。また、導入する際は公開ページの Deploy ボタンをポチるだけで自身の AWS アカウント上にデプロイされ、 CloudFormation の Stack として管理されます。削除も CloudFormation の Stack を削除するだけで実施できます。 AWS Lambda Power Tuning は こちらから 自身のアカウントにデプロイできます。 また、AWS や個人だけでなく Datadog や New Relic など他のパブリッシャーもサーバーレスアプリケーションを公開しているので、他にも使いたいものがないか探してみるとよいかもしれません。 AWS Lambda Power Tuning の実行 デプロイが完了すると、AWS Step Functions 上に state machine があるので、state machine にパラメータを入力することで計測ができます。 リポジトリのドキュメント にいくつかやり方が記載されています。ここでは 実行パラメータを定義した json ファイルを用意してスクリプトから実行する方法を紹介します。 まずは、 AWS Lambda Power Tuning リポジトリ を git clone し、 scripts/sample-execution-input.json を編集します。 { "lambdaARN" : "arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-performance-tuning" , "powerValues" : [ 128 , 256 , 512 , 832 , 1024 , 1536 , 1769 , 1770 , 2048 , 3008 ], "num" : 100 , "payload" : { "body" : "{ \" id \" : \" 123 \" , \" name \" : \" performance \" }" , "path" : "/api/performance" , "httpMethod" : "GET" , "isBase64Encoded" : false , "multiValueHeaders" : { "Accept" : [ "application/json" ] } }, "parallelInvocation" : false } 入力値の詳細は以下の通りです。 5 lambdaARN: Lambda Function の ARN powerValues: 計測したい Lambda のメモリを指定 num: メモリごとの Lambda 実行回数 payload: Lambda Function にわたす Event JSON の内容 parallelInvocation: ベンチマークを並列実行する 次に、 scripts/execute.sh を実行すると、ベンチマークが開始され、最後に結果 URL が出力されます。デプロイ時の設定によって、CloudFormation の Stack Name が違う場合もありますので、必要に応じて書き換えてください。 $ sh ./scripts/execute.sh -n Execution started... -n . : -n . SUCCEEDED Execution output: { "power" :128, "cost" :8.4E-9, "duration" :3.6706666666666674, "stateMachine" : { " executionCost ":4.0E-4," lambdaCost ":1.0178708203125003E-4," visualization ":" https://lambda-power-tuning.show/#gAAAAQACQAMABAAG6QbqBgAIwAs = ; NOxqQP9GmkDFII5AbxKBQLErfECGXW1AWDmGQNNNjkBpSo1ARf1nQA = = ; l08QMn1jtDJ9YzQz1pCSM5dPkDNjd9gzb9AbNPzmGzR9YzQ05vRTNA = ="}} 計測方法は以上、簡単ですね 👌 AWS Lambda Power Tuning の結果確認 計測結果を確認します。関数によって傾向は異なりますが、ここでは 2 種類のパターンを紹介します。 DB や API アクセスなど外部処理が支配的なケース(Network-intensive) Lambda 内の処理が外部処理で占めている場合は、 以下のようなグラフ になります。 赤色の実行時間がどのメモリ設定でも 4ms 前後と大きな変化がなく、青色のコストだけが右肩上がりになっています。 この場合は、Lambda の最低メモリである 128MB が最適ということになります。 CPU 実行時間が支配的なケース(CPU-intensive) Lambda 内の処理が内部処理で占めている場合は、 以下のようなグラフ になります。透かし処理は画像合成処理に多くの CPU を利用するので、こちらのパターンとなりました。 メモリが少ないと速度が出ておらず、メモリを増やすに連れて速度も改善しており、コストの増加も緩やかです。ただし、今回はプログラムがマルチスレッド・マルチプロセス対応はしていないため、コア数が増える 1770MB 以降は大きな速度改善は見られず、コストが上がる結果となっています。 よって、このケースではコストだけを考えたら低メモリが有利ですが、速度も考えたら 1024MB ~ 1770MB あたりにすることを検討すると良さそうです。 まとめ 処方箋プレビューに透かしを入れることになった背景、実現方法を紹介いたしました。 透かし処理はアルファブレンド処理を実施することで実現でき、PDF については透かし用のレイヤーを重ねることで実現できます。 S3 Object Lambda を利用することで、追加のサーバー構築をすることなく、S3 のファイルに対してフィルタリングをかけることができます。 Lambda のパフォーマンス・チューニングについては、AWS Lambda Power Tuning を利用して可視化できます。外部 API などネットワーク依存(Network-intensive)ではなく、CPU 処理が中心(CPU-intensive)になると、コスパよく利用することができます。 S3 のファイルに対してなんらかの処理を実施したいと考えてる方は、一度 S3 Object Lambda の利用を検討してみてはいかがでしょうか? また、Lambda のチューニングポイントは色々ありますが、AWS Summit Online で紹介された以下のセッションがおすすめなので、もっと深くチューニングしたい方は是非ご覧ください! AWS Lambda Performance Tuning Deep Dive〜本当に知りたいのは”ここ”だった〜 さいごに メドレーでは、医療分野の社会課題を IT にて解決するために日々邁進しております。医療という分野においては、機微な情報を扱ったり診療という大事な業務を止めないよう、可用性、パフォーマンス、セキュリティともに高いサービスレベルを求められます。興味がある方は是非ご連絡ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp Footnotes 元画像に直接文字や図形などを描画することももちろん可能ですが、透かしを画像データとしてデザイン、管理したいこともあり、今回はアルファブレンドを選択した。 アルファブレンド - wikipedia ↩ pdfcpu API document: watermark example ↩ ただし最初の 60 億 GB 秒/月。昨今は円安なのでドル建てで利用しているインフラコストも上がっており、USD 表記だとコストが変わらないようにみえるので要注意。 AWS Lambda 料金 ↩ この UI は AWS Lambda Power Tuning UI というツールで別途提供されており、 lambda-power-tuning.show ドメインでアクセスできるので、自前で用意する必要はない。URL は https://lambda-power-tuning.show/#AAEAAgADAAQ=;xKDwRUqaakU5+RtFcXLqRA==;UqkHOPJCBDjA6AM46DAEOA==;AAEAAgADAAQ=;aYP6RdyeeUUQKiVFuC76RA==;ZDoNOJy3DDiJrQs4zhENOA==;100;50 のような形式でデータが URL のパスパラメータにエンコードされている。 ↩ この他にも strategy など様々なパラメータがある: aws-lambda-power-tuning: README-INPUT-OUTPUT.md ↩
はじめに みなさん、こんにちは。エンジニアの新居です。クラウド診療支援システム CLINICS が 8 月から大幅に アップデート して、より患者と医療機関が密接に繋がるようになりました。 今回のアップデートで中心になっている機能領域として PRM(Patient Relationship Management) というものがあります。 この PRM が一体どういったものなのか、プロジェクトはどのように進んでいったのかを関係者にインタビューしていきました。 メドレーで大規模なプロジェクト開発をどのように行なっているかの一端をお見せできればと考えています! インタビュイー紹介 宍戸さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の開発責任者。前職は株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2017 年メドレー入社。 酒井さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の UI/UX 全般を担当。本プロジェクトのリード。前職は受託開発会社で様々なサイトやシステムのデザインに従事。2019 年メドレー入社。 田中さん 医療プラットフォーム CTO。医療プラットフォームの開発統括をしながら、自身は患者統合基盤の開発を担当。前職までは SIer や IT コンサルタントを経て、株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2016 年メドレー入社。 来田さん 前職まではクリニックで精神科医として従事。CLINCS オンライン診療の立ち上げ時から関わる。現在は CLINICS のディレクションを中心として事業部と開発の架け橋となっている。 PRM とはどういうものなのか? 新居 : では今回中心となった PRM とはいったい何なのかというところから解説していただいて良いですか? 一般的にはそこまで知られてない言葉なのではないかと思うのですが…。 宍戸 : 元々 CRM(Customer Relationship Management)というマーケティングなどの文脈の用語があります。これは「自社サービスや商品を使っている顧客」と「サービスや商品を提供している企業」との関係性を管理することを指しますが、それをもとに適切なアクションをおこなうことで最終的には利益向上を目指すものと考えられます。 PRM は CRM と同様に医療機関と患者さんの関係性を管理することで必要なサービスを提供する ためのもの、と考えています。 CLINICS で基幹システムである電子カルテなどではなく、 オンライン診療・予約・問診など患者接点となる機能をターゲットにしてそれらを強化・改善 していったのが、CLINICS の今回の PRM プロジェクトになります。 CLINICS 開発責任者 宍戸さん 新居 : 確かに CRM は最近良く聞く言葉でしたが、PRM は患者と医療サービスを使う医療機関との関係を深めるためのものなんですね。 PRM プロジェクトの始まり CLINICS に感じていた課題とは 新居 : このプロジェクトは半年ほどかけていると聞きましたが、「PRM をやっていこう」となったのが半年前からだったんでしょうか? 宍戸 : 元々 CLINICS はオンライン診療のシステムとして 2016 年からサービスを提供してきました。2018 年からは CLINICS カルテとして電子カルテシステムも提供を開始しましたが、そのタイミング以降はオンライン診療部分に関してはそこまで大きくアップデートはしてこなかったんです。 現在はクラウド診療支援システムとして販売していますが、これまでは電子カルテなど基幹部分の強化がメインになっていたからです。しかし、数年開発・運用してきた中で、本来医療機関の診察のメインである対面診療の予約については課題が積み残されていたり、昨今の状況から来る オンライン診療のニーズなどを始めとして、患者接点を強化していく必要がある ということになり、今回取り組むことになりました。 酒井 : PRM というと本当にここ最近の潮流なのですが、 CLINICS はもともと「患者とつながる」をコンセプト としており、今回の機能強化などもこのコンセプトに基づき実施しています。その上で、患者とのタッチポイントで抜けていた部分や強化したほうが良い部分をより作り込んでいったという形です。 例えば、CLINICS は元々オンライン診療のシステムとして始まっており、対面診療予約に関して使いづらさを感じるケースがあったため、対面診療予約をより行いやすくするよう改修を行うなどです。 CLINICS デザイナー 酒井さん 新居 : 元々あったものをさらに患者とのタッチポイントを強化するために、改修・改善していったんですね。 プロジェクトの背景 どのくらいの規模のプロジェクトだったのか 新居 : 半年のプロジェクトということですが、規模としてはどの位の大きさだったんでしょうか? 酒井 : 私が経験したプロジェクトの中でも関わる部署など考えると一番大きかったと思います。開発はもちろんですが、CLINICS チームの セールス・カスタマーサクセス・マーケティング、広報もなのでほぼ CLINICS の全部署と関わっていました 。 田中 : 他にも、サービスとしては患者統合基盤・ 患者向けアプリ・ Dentis が関係しています。 宍戸 : QA チームにもしっかり入ってもらって QA を行なったりしてもらったので、本当に 医療プラットフォームのほぼ全てのチームと関わって開発していった 感じです。最終的にプロダクトの見せ方や売り方なども含めて調整していったので事業部とも密接に関わって作り上げていきました。 新しい PRM 機能をどうやって、プロダクトに融合させたのか 新居 : そこまで大きい規模のプロジェクトだということで、PRM の各種機能をどうやって上手くプロダクトに落としこむのか、実際にはどのようにみなさん推進されていったんでしょう。 宍戸 : プロジェクトの推進という意味では、最初は来田さんや( Dentis の運営を行なっている)プロダクト戦略室の平山さんと スモールスタートで意見を出し合いながらモックを作っていき、まずはビジュアル面から機能のイメージなどを刷り合わせ ていくのに、酒井さんに手を動かしてもらいながら進めていました。 その過程でこのプロジェクトにおける重要な意思決定がありました。CLINICS ではログイン不要のシンプルな予約機能を以前より提供していました。この機能は患者アプリとは元々違うドメインで運営していたものでしたが、今回のタイミングでこの両者のアプリケーションを患者アプリと同じドメインに統合して運営していこうという決定をしました。 患者アプリは CLINICS チームとは別チームで運営しているという背景もあり、チーム間の連携が格段に増え 、考えなければいけない事柄も多くなります。プロジェクトとして関係者が多くなってきたので、 今までの考え方を 1 度リセットしてプロジェクトの進め方を新しく模索する必要がありました 。 この点がかなり自分の中では大変でした。来田さんも大変だったと思います。こうした全体像を描いたり、全体スケジュールを決めたりというところでは、田中さんにアドバイスをもらいながらプロジェクトを進めていきました。 プロジェクトを推進するために必要だったこと 難しい意思決定をどのようにしていた? 新居 : 途中から一気にプロジェクトがスケールアップしたということなんですね。今回のプロジェクトは色々な部署も関わるし、たくさんの機能や改修が入っています。開発側と事業部側との意識合わせが、かなり必要だったんではないかと思うのですが、その辺はいかがだったでしょうか。 来田 : 例えば、今まで CLINICS では対面診療予約の際に患者のクレジットカード登録を必須としていたのですが、今回の改修でクレジットカード登録を不要に変更できるように対応を行いました。この対応は、CLINICS オンライン診療の初期から念願の機能だったりします。自分は事業部長である田中大介さんと今年の年明けくらいから話をして、メンバーに「よし、このプロジェクトをやっていこう」という話を持っていくようにしていました。やっぱり、大変なプロジェクトだというのはあったのでスタートを切れる状態に徐々にしていった感じです。 プロジェクトが佳境に入ってからは自分は各部署との調整がメインの業務でした。開発側と事業側とか CLINICS と Dentis だとかそういった立場の違いから来る要求の違いを納得できる仕様に落とし込む作業です。やっぱり、それぞれの立場でベストを尽くそうとみんな考えているんですが、全部を取り入れることはできないので。私が調整するときには それぞれ反対の立場の人たちの考えをきちんと伝えるようにということをしながら、仕様を決めていく ことを念頭に置いていました。 具体的な調整の例としては、開発する機能とセールスする際のプランの刷り合わせだったりですね。「機能としてはこうしたほうが使いやすいよね」というだけだと、セールス側としては「どこをウリにして売っていくのかというのが見えてこない」という話になったんですね。これはどちらの言い分も一理あるものなんですが、こういうところを時間をかけて 双方納得行くように決めていきました 。 CLINICS ディレクター・医師 来田さん 酒井 : この例でやったことは、まず現在のプランと機能を全部書き出して、組み合わせを整理するということでした。これをベースにして話を刷り合わせていったんですが、文字ベースだと人間分かった気になるんですが、実はそこまでピンと来ない。ですので、 LP のモックを作ってこれを見た顧客が導入したくなるようなウリになっているのかをすぐに判断できる状態 にしました。 宍戸 : こうしてちゃんと機能を解いていって細かい部分も含めて、時には CLINICS のコンセプトに立ち返って事業部・開発双方で時間をかけて認識を合わせることができたのは、大変でしたが良かったですね。 リリースまでのプロセスで顧客の声を反映 新居 : やはりここまでのプロジェクトだと最初の段階でちゃんとリリースまでを見据えた意識合わせをチーム内で行なっていたんですね。開発が進行していって、リリースするまではどのようにプロジェクトは進んでいったんでしょう。 宍戸 : リリースをする直前も、単純に仕様や機能について文書で説明しても理解するのは難しいので、酒井さんにディレクションをしてもらいながら、開発環境を使って QA チームやカスタマーサポートチームと一緒に認識合わせやリリースに向けた準備を行なっていきました。 来田 : あとは関係が構築できている 顧客に実際機能を見てもらって反応を伺う ということもしていきました。色々な使い方をしている顧客 10 件くらいにモックをベースにヒアリングをかけていきました。価格や導入時期なども含めた話を聞いていって、そこをすぐにプロジェクトにフィードバックする体制を構築していました。 酒井 : 実際に顧客の声をこうして聞けるというのは本当にプロダクトにとってありがたいことでした。特にこういう使い勝手などに直接関わる部分では、こうしたフィードバックの有無で完成度が違ってきます。 機能の正式リリース前から機能評価に協力していただける医療機関がいるのは、カスタマーサポートの皆さんが顧客と良好な関係を築けているおかげ だと思っています。こういった点はメドレーの強みだと思います。 プロジェクトで大切にしたこと 新居 : 開発段階からユーザーの声を吸い上げていけるのは、本当に良いですね。プロダクトの改善スピードなんかも段違いに早くなりそうです。他に大切にしていた部分などありますか? 酒井 : 今回の機能開発においては、プロジェクトの責任者として、開発以外のメンバーともしっかりコミュニケーションし合意形成を図ることを意識してプロジェクトを推進しました。 先ほどの話にも出ましたが 1 つの機能を実装するとなっても、実際の医療現場での使い勝手や、メドレーでのサポート、売り方など色々な視点で考えないといけないプロジェクトでした。そのため、そういったところをカスタマーサクセスのメンバー含む 10 人ほどと毎週 1 つ 1 つ納得がいくように詰めていくという作業をしていったりしました。 来田さんのように現役の医師の意見、顧客の意見なども取り入れられますし、カスタマーサポートには医療事務経験者の方もいらっしゃるので、多方面の視点を総合的に取り入れられるコミュニケーション を心がけていました。 手段はテキストや Figma でのデザイン、実際の実装を見せるなど適切な手段を選んでコミュニケーションコストを低くするようにして進行していました。 実際の機能実装にあたってはエンジニアも自身で考えて細かいボールを拾ったりしながら、実装 していってもらいました。この辺りはこのプロジェクトだからというよりも、メドレーの開発のベースになっている部分なので良いですよね。 メンバーのイメージ合わせがプロジェクト成功の鍵 新居 : 先ほどもお話がありましたが、プロジェクト途中の意思決定で関わるプロダクトが多くなったのでスイッチングが大変だったということでしたが、実際にそのタイミングになったときに全体のシステム設計なんかも変わったりしたんでしょうか。 田中 : 私はプロジェクトの途中から入ったんですが、そのタイミングで、宍戸さんや来田さんがちょうどシステム設計を考え直していたり、プロジェクト計画の引き直しをしているところでした。そこでまずは全体像を把握したいと考えてドキュメントなどを見せてもらったんです。各機能の細かい要件は分かったのですが、このプロジェクト全体としての各システムの責務や依存関係などがいまいち分かりづらく、メンバー間の認識も若干ズレが発生しているように感じました。なのでまずは**メンバーの認識を合わせ、各チームが何をするべきかを明確化するために、各システムの責務など全体感の整理整頓をして「見える化」**することから始めました。 このプロジェクトに限らずですが、プロジェクトとして抽象度の高い段階でのドキュメントは、文章のみの記載だと実は関係者にしっかりと伝わっていなかったということが多々あります。各自が頭の中でイメージした図に差異が発生している状態です。自分はそういう際に良く言っているのが**「大枠で良いので、まずは整理結果を図にしてみんなのイメージを合わせる**」ということです。こうして認識を合わせることで各チームが進めやすくなり後戻りリスクも軽減されるので、この段階でしっかりとイメージをすりあわせる事がとても重要だと考えています。 医療プラットフォーム CTO 田中さん 新居 : たしかにテキストで仕様なんかを書きながら説明しても、実は認識に齟齬があった…という経験があります。その後はどのようなプロセスでプロジェクト推進したんですか? 田中 : その後は各プロダクトにて実装予定の機能で、プロダクト間で共通化できそうな機能を共通サービスとして切り出すかどうかをみんなと考えていきました。結果として大きな機能では「ビデオ通話」部分を共通化するという意思決定をしました。他にも「問診票の提供」機能が候補としてあがっていましたが、共通化せずに各プロダクトの責務としてそれぞれ実装するという結論となりました。同じような機能に見えても、医科と歯科という業務ドメイン毎のコンテキスト差異を十分に検討した上での判断でした。 その差異がプロダクト機能としての強みとなり、安易な共通化はプロダクト成長の足枷となるリスクも存在 するためです。この判断も先ほどの全体の依存関係などが整理されたからこそ、意思決定できるものでした。 酒井 : 機能実装時に実感しましたが、やはり医科と歯科では細かい部分の要件などが異なる部分が多かったので、この段階で共通化の判断ができたのは大変助かりました。 PRM プロジェクトの大きな成果 新居 : 最後になりますが、このプロジェクトが終わっての成果はどのように考えていますか? 宍戸 : 冒頭に話した CLINICS の課題に関してはある程度、解決ができたと思います。 医療機関の業務に沿った形でプロダクトが進化 できた点はよかったと思います。特に問診周りなどは発熱外来などニーズの多様化に即して使い勝手が良くなったと思っています。 酒井 : 他にもビデオ通話部分など「オンライン診療」を提供するプロダクトとして「対面診療」だけではなく、もっと有用なプロダクトにできたかと思っています。 来田 : まだリリースしたばかりですが、初月の販売実績も期待していたよりも良く、そうした意味でも マーケットフィットした機能を出せたなという手応え が実感としてもありますね。 田中 : 予約やオンライン診療もドメインを統一した結果、会員登録数が増えていますので、そうした点も成果と言えます。 酒井 : あとは直接の成果というわけではないのですが、今回のプロジェクトの副産物として CLINICS の機能を整理整頓していった結果、 改めて「一気通貫で医療機関の業務をサポートできる」というトータルでの機能提供と各機能のシームレスな連携は CLINICS の強み だ、というのが再確認できたのは大きいです。 新居 : これからのさらなる CLINICS の発展に期待できますね!本日は長い時間お話ありがとうございました! おわりに メドレーとしてもかなり大規模なプロジェクトだった今回の PRM プロジェクトでしたが、みなさんのお話を聞いて各部署・各プロダクトで綿密に連携しながらプロジェクトを成功させようとしている様子が感じられるインタビューでした。 特にプロジェクト進行の話は非常に参考になりました。皆さんのご参考になる部分があれば幸いです。 こうしたプロジェクトで自分の力を発揮したい!と思った方はぜひお気軽に下記からご応募ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに みなさん、こんにちは。エンジニアの新居です。クラウド診療支援システム CLINICS が 8 月から大幅に アップデート して、より患者と医療機関が密接に繋がるようになりました。 今回のアップデートで中心になっている機能領域として PRM(Patient Relationship Management) というものがあります。 この PRM が一体どういったものなのか、プロジェクトはどのように進んでいったのかを関係者にインタビューしていきました。 メドレーで大規模なプロジェクト開発をどのように行なっているかの一端をお見せできればと考えています! インタビュイー紹介 宍戸さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の開発責任者。前職は株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2017 年メドレー入社。 酒井さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の UI/UX 全般を担当。本プロジェクトのリード。前職は受託開発会社で様々なサイトやシステムのデザインに従事。2019 年メドレー入社。 田中さん 医療プラットフォーム CTO。医療プラットフォームの開発統括をしながら、自身は患者統合基盤の開発を担当。前職までは SIer や IT コンサルタントを経て、株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2016 年メドレー入社。 来田さん 前職まではクリニックで精神科医として従事。CLINCS オンライン診療の立ち上げ時から関わる。現在は CLINICS のディレクションを中心として事業部と開発の架け橋となっている。 PRM とはどういうものなのか? 新居 : では今回中心となった PRM とはいったい何なのかというところから解説していただいて良いですか? 一般的にはそこまで知られてない言葉なのではないかと思うのですが…。 宍戸 : 元々 CRM(Customer Relationship Management)というマーケティングなどの文脈の用語があります。これは「自社サービスや商品を使っている顧客」と「サービスや商品を提供している企業」との関係性を管理することを指しますが、それをもとに適切なアクションをおこなうことで最終的には利益向上を目指すものと考えられます。 PRM は CRM と同様に医療機関と患者さんの関係性を管理することで必要なサービスを提供する ためのもの、と考えています。 CLINICS で基幹システムである電子カルテなどではなく、 オンライン診療・予約・問診など患者接点となる機能をターゲットにしてそれらを強化・改善 していったのが、CLINICS の今回の PRM プロジェクトになります。 CLINICS 開発責任者 宍戸さん 新居 : 確かに CRM は最近良く聞く言葉でしたが、PRM は患者と医療サービスを使う医療機関との関係を深めるためのものなんですね。 PRM プロジェクトの始まり CLINICS に感じていた課題とは 新居 : このプロジェクトは半年ほどかけていると聞きましたが、「PRM をやっていこう」となったのが半年前からだったんでしょうか? 宍戸 : 元々 CLINICS はオンライン診療のシステムとして 2016 年からサービスを提供してきました。2018 年からは CLINICS カルテとして電子カルテシステムも提供を開始しましたが、そのタイミング以降はオンライン診療部分に関してはそこまで大きくアップデートはしてこなかったんです。 現在はクラウド診療支援システムとして販売していますが、これまでは電子カルテなど基幹部分の強化がメインになっていたからです。しかし、数年開発・運用してきた中で、本来医療機関の診察のメインである対面診療の予約については課題が積み残されていたり、昨今の状況から来る オンライン診療のニーズなどを始めとして、患者接点を強化していく必要がある ということになり、今回取り組むことになりました。 酒井 : PRM というと本当にここ最近の潮流なのですが、 CLINICS はもともと「患者とつながる」をコンセプト としており、今回の機能強化などもこのコンセプトに基づき実施しています。その上で、患者とのタッチポイントで抜けていた部分や強化したほうが良い部分をより作り込んでいったという形です。 例えば、CLINICS は元々オンライン診療のシステムとして始まっており、対面診療予約に関して使いづらさを感じるケースがあったため、対面診療予約をより行いやすくするよう改修を行うなどです。 CLINICS デザイナー 酒井さん 新居 : 元々あったものをさらに患者とのタッチポイントを強化するために、改修・改善していったんですね。 プロジェクトの背景 どのくらいの規模のプロジェクトだったのか 新居 : 半年のプロジェクトということですが、規模としてはどの位の大きさだったんでしょうか? 酒井 : 私が経験したプロジェクトの中でも関わる部署など考えると一番大きかったと思います。開発はもちろんですが、CLINICS チームの セールス・カスタマーサクセス・マーケティング、広報もなのでほぼ CLINICS の全部署と関わっていました 。 田中 : 他にも、サービスとしては患者統合基盤・ 患者向けアプリ・ Dentis が関係しています。 宍戸 : QA チームにもしっかり入ってもらって QA を行なったりしてもらったので、本当に 医療プラットフォームのほぼ全てのチームと関わって開発していった 感じです。最終的にプロダクトの見せ方や売り方なども含めて調整していったので事業部とも密接に関わって作り上げていきました。 新しい PRM 機能をどうやって、プロダクトに融合させたのか 新居 : そこまで大きい規模のプロジェクトだということで、PRM の各種機能をどうやって上手くプロダクトに落としこむのか、実際にはどのようにみなさん推進されていったんでしょう。 宍戸 : プロジェクトの推進という意味では、最初は来田さんや( Dentis の運営を行なっている)プロダクト戦略室の平山さんと スモールスタートで意見を出し合いながらモックを作っていき、まずはビジュアル面から機能のイメージなどを刷り合わせ ていくのに、酒井さんに手を動かしてもらいながら進めていました。 その過程でこのプロジェクトにおける重要な意思決定がありました。CLINICS ではログイン不要のシンプルな予約機能を以前より提供していました。この機能は患者アプリとは元々違うドメインで運営していたものでしたが、今回のタイミングでこの両者のアプリケーションを患者アプリと同じドメインに統合して運営していこうという決定をしました。 患者アプリは CLINICS チームとは別チームで運営しているという背景もあり、チーム間の連携が格段に増え 、考えなければいけない事柄も多くなります。プロジェクトとして関係者が多くなってきたので、 今までの考え方を 1 度リセットしてプロジェクトの進め方を新しく模索する必要がありました 。 この点がかなり自分の中では大変でした。来田さんも大変だったと思います。こうした全体像を描いたり、全体スケジュールを決めたりというところでは、田中さんにアドバイスをもらいながらプロジェクトを進めていきました。 プロジェクトを推進するために必要だったこと 難しい意思決定をどのようにしていた? 新居 : 途中から一気にプロジェクトがスケールアップしたということなんですね。今回のプロジェクトは色々な部署も関わるし、たくさんの機能や改修が入っています。開発側と事業部側との意識合わせが、かなり必要だったんではないかと思うのですが、その辺はいかがだったでしょうか。 来田 : 例えば、今まで CLINICS では対面診療予約の際に患者のクレジットカード登録を必須としていたのですが、今回の改修でクレジットカード登録を不要に変更できるように対応を行いました。この対応は、CLINICS オンライン診療の初期から念願の機能だったりします。自分は事業部長である田中大介さんと今年の年明けくらいから話をして、メンバーに「よし、このプロジェクトをやっていこう」という話を持っていくようにしていました。やっぱり、大変なプロジェクトだというのはあったのでスタートを切れる状態に徐々にしていった感じです。 プロジェクトが佳境に入ってからは自分は各部署との調整がメインの業務でした。開発側と事業側とか CLINICS と Dentis だとかそういった立場の違いから来る要求の違いを納得できる仕様に落とし込む作業です。やっぱり、それぞれの立場でベストを尽くそうとみんな考えているんですが、全部を取り入れることはできないので。私が調整するときには それぞれ反対の立場の人たちの考えをきちんと伝えるようにということをしながら、仕様を決めていく ことを念頭に置いていました。 具体的な調整の例としては、開発する機能とセールスする際のプランの刷り合わせだったりですね。「機能としてはこうしたほうが使いやすいよね」というだけだと、セールス側としては「どこをウリにして売っていくのかというのが見えてこない」という話になったんですね。これはどちらの言い分も一理あるものなんですが、こういうところを時間をかけて 双方納得行くように決めていきました 。 CLINICS ディレクター・医師 来田さん 酒井 : この例でやったことは、まず現在のプランと機能を全部書き出して、組み合わせを整理するということでした。これをベースにして話を刷り合わせていったんですが、文字ベースだと人間分かった気になるんですが、実はそこまでピンと来ない。ですので、 LP のモックを作ってこれを見た顧客が導入したくなるようなウリになっているのかをすぐに判断できる状態 にしました。 宍戸 : こうしてちゃんと機能を解いていって細かい部分も含めて、時には CLINICS のコンセプトに立ち返って事業部・開発双方で時間をかけて認識を合わせることができたのは、大変でしたが良かったですね。 リリースまでのプロセスで顧客の声を反映 新居 : やはりここまでのプロジェクトだと最初の段階でちゃんとリリースまでを見据えた意識合わせをチーム内で行なっていたんですね。開発が進行していって、リリースするまではどのようにプロジェクトは進んでいったんでしょう。 宍戸 : リリースをする直前も、単純に仕様や機能について文書で説明しても理解するのは難しいので、酒井さんにディレクションをしてもらいながら、開発環境を使って QA チームやカスタマーサポートチームと一緒に認識合わせやリリースに向けた準備を行なっていきました。 来田 : あとは関係が構築できている 顧客に実際機能を見てもらって反応を伺う ということもしていきました。色々な使い方をしている顧客 10 件くらいにモックをベースにヒアリングをかけていきました。価格や導入時期なども含めた話を聞いていって、そこをすぐにプロジェクトにフィードバックする体制を構築していました。 酒井 : 実際に顧客の声をこうして聞けるというのは本当にプロダクトにとってありがたいことでした。特にこういう使い勝手などに直接関わる部分では、こうしたフィードバックの有無で完成度が違ってきます。 機能の正式リリース前から機能評価に協力していただける医療機関がいるのは、カスタマーサポートの皆さんが顧客と良好な関係を築けているおかげ だと思っています。こういった点はメドレーの強みだと思います。 プロジェクトで大切にしたこと 新居 : 開発段階からユーザーの声を吸い上げていけるのは、本当に良いですね。プロダクトの改善スピードなんかも段違いに早くなりそうです。他に大切にしていた部分などありますか? 酒井 : 今回の機能開発においては、プロジェクトの責任者として、開発以外のメンバーともしっかりコミュニケーションし合意形成を図ることを意識してプロジェクトを推進しました。 先ほどの話にも出ましたが 1 つの機能を実装するとなっても、実際の医療現場での使い勝手や、メドレーでのサポート、売り方など色々な視点で考えないといけないプロジェクトでした。そのため、そういったところをカスタマーサクセスのメンバー含む 10 人ほどと毎週 1 つ 1 つ納得がいくように詰めていくという作業をしていったりしました。 来田さんのように現役の医師の意見、顧客の意見なども取り入れられますし、カスタマーサポートには医療事務経験者の方もいらっしゃるので、多方面の視点を総合的に取り入れられるコミュニケーション を心がけていました。 手段はテキストや Figma でのデザイン、実際の実装を見せるなど適切な手段を選んでコミュニケーションコストを低くするようにして進行していました。 実際の機能実装にあたってはエンジニアも自身で考えて細かいボールを拾ったりしながら、実装 していってもらいました。この辺りはこのプロジェクトだからというよりも、メドレーの開発のベースになっている部分なので良いですよね。 メンバーのイメージ合わせがプロジェクト成功の鍵 新居 : 先ほどもお話がありましたが、プロジェクト途中の意思決定で関わるプロダクトが多くなったのでスイッチングが大変だったということでしたが、実際にそのタイミングになったときに全体のシステム設計なんかも変わったりしたんでしょうか。 田中 : 私はプロジェクトの途中から入ったんですが、そのタイミングで、宍戸さんや来田さんがちょうどシステム設計を考え直していたり、プロジェクト計画の引き直しをしているところでした。そこでまずは全体像を把握したいと考えてドキュメントなどを見せてもらったんです。各機能の細かい要件は分かったのですが、このプロジェクト全体としての各システムの責務や依存関係などがいまいち分かりづらく、メンバー間の認識も若干ズレが発生しているように感じました。なのでまずは**メンバーの認識を合わせ、各チームが何をするべきかを明確化するために、各システムの責務など全体感の整理整頓をして「見える化」**することから始めました。 このプロジェクトに限らずですが、プロジェクトとして抽象度の高い段階でのドキュメントは、文章のみの記載だと実は関係者にしっかりと伝わっていなかったということが多々あります。各自が頭の中でイメージした図に差異が発生している状態です。自分はそういう際に良く言っているのが**「大枠で良いので、まずは整理結果を図にしてみんなのイメージを合わせる**」ということです。こうして認識を合わせることで各チームが進めやすくなり後戻りリスクも軽減されるので、この段階でしっかりとイメージをすりあわせる事がとても重要だと考えています。 医療プラットフォーム CTO 田中さん 新居 : たしかにテキストで仕様なんかを書きながら説明しても、実は認識に齟齬があった…という経験があります。その後はどのようなプロセスでプロジェクト推進したんですか? 田中 : その後は各プロダクトにて実装予定の機能で、プロダクト間で共通化できそうな機能を共通サービスとして切り出すかどうかをみんなと考えていきました。結果として大きな機能では「ビデオ通話」部分を共通化するという意思決定をしました。他にも「問診票の提供」機能が候補としてあがっていましたが、共通化せずに各プロダクトの責務としてそれぞれ実装するという結論となりました。同じような機能に見えても、医科と歯科という業務ドメイン毎のコンテキスト差異を十分に検討した上での判断でした。 その差異がプロダクト機能としての強みとなり、安易な共通化はプロダクト成長の足枷となるリスクも存在 するためです。この判断も先ほどの全体の依存関係などが整理されたからこそ、意思決定できるものでした。 酒井 : 機能実装時に実感しましたが、やはり医科と歯科では細かい部分の要件などが異なる部分が多かったので、この段階で共通化の判断ができたのは大変助かりました。 PRM プロジェクトの大きな成果 新居 : 最後になりますが、このプロジェクトが終わっての成果はどのように考えていますか? 宍戸 : 冒頭に話した CLINICS の課題に関してはある程度、解決ができたと思います。 医療機関の業務に沿った形でプロダクトが進化 できた点はよかったと思います。特に問診周りなどは発熱外来などニーズの多様化に即して使い勝手が良くなったと思っています。 酒井 : 他にもビデオ通話部分など「オンライン診療」を提供するプロダクトとして「対面診療」だけではなく、もっと有用なプロダクトにできたかと思っています。 来田 : まだリリースしたばかりですが、初月の販売実績も期待していたよりも良く、そうした意味でも マーケットフィットした機能を出せたなという手応え が実感としてもありますね。 田中 : 予約やオンライン診療もドメインを統一した結果、会員登録数が増えていますので、そうした点も成果と言えます。 酒井 : あとは直接の成果というわけではないのですが、今回のプロジェクトの副産物として CLINICS の機能を整理整頓していった結果、 改めて「一気通貫で医療機関の業務をサポートできる」というトータルでの機能提供と各機能のシームレスな連携は CLINICS の強み だ、というのが再確認できたのは大きいです。 新居 : これからのさらなる CLINICS の発展に期待できますね!本日は長い時間お話ありがとうございました! おわりに メドレーとしてもかなり大規模なプロジェクトだった今回の PRM プロジェクトでしたが、みなさんのお話を聞いて各部署・各プロダクトで綿密に連携しながらプロジェクトを成功させようとしている様子が感じられるインタビューでした。 特にプロジェクト進行の話は非常に参考になりました。皆さんのご参考になる部分があれば幸いです。 こうしたプロジェクトで自分の力を発揮したい!と思った方はぜひお気軽に下記からご応募ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに みなさん、こんにちは。エンジニアの新居です。クラウド診療支援システム CLINICS が 8 月から大幅に アップデート して、より患者と医療機関が密接に繋がるようになりました。 今回のアップデートで中心になっている機能領域として PRM(Patient Relationship Management) というものがあります。 この PRM が一体どういったものなのか、プロジェクトはどのように進んでいったのかを関係者にインタビューしていきました。 メドレーで大規模なプロジェクト開発をどのように行なっているかの一端をお見せできればと考えています! インタビュイー紹介 宍戸さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の開発責任者。前職は株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2017 年メドレー入社。 酒井さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の UI/UX 全般を担当。本プロジェクトのリード。前職は受託開発会社で様々なサイトやシステムのデザインに従事。2019 年メドレー入社。 田中さん 医療プラットフォーム CTO。医療プラットフォームの開発統括をしながら、自身は患者統合基盤の開発を担当。前職までは SIer や IT コンサルタントを経て、株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2016 年メドレー入社。 来田さん 前職まではクリニックで精神科医として従事。CLINCS オンライン診療の立ち上げ時から関わる。現在は CLINICS のディレクションを中心として事業部と開発の架け橋となっている。 PRM とはどういうものなのか? 新居 : では今回中心となった PRM とはいったい何なのかというところから解説していただいて良いですか? 一般的にはそこまで知られてない言葉なのではないかと思うのですが…。 宍戸 : 元々 CRM(Customer Relationship Management)というマーケティングなどの文脈の用語があります。これは「自社サービスや商品を使っている顧客」と「サービスや商品を提供している企業」との関係性を管理することを指しますが、それをもとに適切なアクションをおこなうことで最終的には利益向上を目指すものと考えられます。 PRM は CRM と同様に医療機関と患者さんの関係性を管理することで必要なサービスを提供する ためのもの、と考えています。 CLINICS で基幹システムである電子カルテなどではなく、 オンライン診療・予約・問診など患者接点となる機能をターゲットにしてそれらを強化・改善 していったのが、CLINICS の今回の PRM プロジェクトになります。 CLINICS 開発責任者 宍戸さん 新居 : 確かに CRM は最近良く聞く言葉でしたが、PRM は患者と医療サービスを使う医療機関との関係を深めるためのものなんですね。 PRM プロジェクトの始まり CLINICS に感じていた課題とは 新居 : このプロジェクトは半年ほどかけていると聞きましたが、「PRM をやっていこう」となったのが半年前からだったんでしょうか? 宍戸 : 元々 CLINICS はオンライン診療のシステムとして 2016 年からサービスを提供してきました。2018 年からは CLINICS カルテとして電子カルテシステムも提供を開始しましたが、そのタイミング以降はオンライン診療部分に関してはそこまで大きくアップデートはしてこなかったんです。 現在はクラウド診療支援システムとして販売していますが、これまでは電子カルテなど基幹部分の強化がメインになっていたからです。しかし、数年開発・運用してきた中で、本来医療機関の診察のメインである対面診療の予約については課題が積み残されていたり、昨今の状況から来る オンライン診療のニーズなどを始めとして、患者接点を強化していく必要がある ということになり、今回取り組むことになりました。 酒井 : PRM というと本当にここ最近の潮流なのですが、 CLINICS はもともと「患者とつながる」をコンセプト としており、今回の機能強化などもこのコンセプトに基づき実施しています。その上で、患者とのタッチポイントで抜けていた部分や強化したほうが良い部分をより作り込んでいったという形です。 例えば、CLINICS は元々オンライン診療のシステムとして始まっており、対面診療予約に関して使いづらさを感じるケースがあったため、対面診療予約をより行いやすくするよう改修を行うなどです。 CLINICS デザイナー 酒井さん 新居 : 元々あったものをさらに患者とのタッチポイントを強化するために、改修・改善していったんですね。 プロジェクトの背景 どのくらいの規模のプロジェクトだったのか 新居 : 半年のプロジェクトということですが、規模としてはどの位の大きさだったんでしょうか? 酒井 : 私が経験したプロジェクトの中でも関わる部署など考えると一番大きかったと思います。開発はもちろんですが、CLINICS チームの セールス・カスタマーサクセス・マーケティング、広報もなのでほぼ CLINICS の全部署と関わっていました 。 田中 : 他にも、サービスとしては患者統合基盤・ 患者向けアプリ・ Dentis が関係しています。 宍戸 : QA チームにもしっかり入ってもらって QA を行なったりしてもらったので、本当に 医療プラットフォームのほぼ全てのチームと関わって開発していった 感じです。最終的にプロダクトの見せ方や売り方なども含めて調整していったので事業部とも密接に関わって作り上げていきました。 新しい PRM 機能をどうやって、プロダクトに融合させたのか 新居 : そこまで大きい規模のプロジェクトだということで、PRM の各種機能をどうやって上手くプロダクトに落としこむのか、実際にはどのようにみなさん推進されていったんでしょう。 宍戸 : プロジェクトの推進という意味では、最初は来田さんや( Dentis の運営を行なっている)プロダクト戦略室の平山さんと スモールスタートで意見を出し合いながらモックを作っていき、まずはビジュアル面から機能のイメージなどを刷り合わせ ていくのに、酒井さんに手を動かしてもらいながら進めていました。 その過程でこのプロジェクトにおける重要な意思決定がありました。CLINICS ではログイン不要のシンプルな予約機能を以前より提供していました。この機能は患者アプリとは元々違うドメインで運営していたものでしたが、今回のタイミングでこの両者のアプリケーションを患者アプリと同じドメインに統合して運営していこうという決定をしました。 患者アプリは CLINICS チームとは別チームで運営しているという背景もあり、チーム間の連携が格段に増え 、考えなければいけない事柄も多くなります。プロジェクトとして関係者が多くなってきたので、 今までの考え方を 1 度リセットしてプロジェクトの進め方を新しく模索する必要がありました 。 この点がかなり自分の中では大変でした。来田さんも大変だったと思います。こうした全体像を描いたり、全体スケジュールを決めたりというところでは、田中さんにアドバイスをもらいながらプロジェクトを進めていきました。 プロジェクトを推進するために必要だったこと 難しい意思決定をどのようにしていた? 新居 : 途中から一気にプロジェクトがスケールアップしたということなんですね。今回のプロジェクトは色々な部署も関わるし、たくさんの機能や改修が入っています。開発側と事業部側との意識合わせが、かなり必要だったんではないかと思うのですが、その辺はいかがだったでしょうか。 来田 : 例えば、今まで CLINICS では対面診療予約の際に患者のクレジットカード登録を必須としていたのですが、今回の改修でクレジットカード登録を不要に変更できるように対応を行いました。この対応は、CLINICS オンライン診療の初期から念願の機能だったりします。自分は事業部長である田中大介さんと今年の年明けくらいから話をして、メンバーに「よし、このプロジェクトをやっていこう」という話を持っていくようにしていました。やっぱり、大変なプロジェクトだというのはあったのでスタートを切れる状態に徐々にしていった感じです。 プロジェクトが佳境に入ってからは自分は各部署との調整がメインの業務でした。開発側と事業側とか CLINICS と Dentis だとかそういった立場の違いから来る要求の違いを納得できる仕様に落とし込む作業です。やっぱり、それぞれの立場でベストを尽くそうとみんな考えているんですが、全部を取り入れることはできないので。私が調整するときには それぞれ反対の立場の人たちの考えをきちんと伝えるようにということをしながら、仕様を決めていく ことを念頭に置いていました。 具体的な調整の例としては、開発する機能とセールスする際のプランの刷り合わせだったりですね。「機能としてはこうしたほうが使いやすいよね」というだけだと、セールス側としては「どこをウリにして売っていくのかというのが見えてこない」という話になったんですね。これはどちらの言い分も一理あるものなんですが、こういうところを時間をかけて 双方納得行くように決めていきました 。 CLINICS ディレクター・医師 来田さん 酒井 : この例でやったことは、まず現在のプランと機能を全部書き出して、組み合わせを整理するということでした。これをベースにして話を刷り合わせていったんですが、文字ベースだと人間分かった気になるんですが、実はそこまでピンと来ない。ですので、 LP のモックを作ってこれを見た顧客が導入したくなるようなウリになっているのかをすぐに判断できる状態 にしました。 宍戸 : こうしてちゃんと機能を解いていって細かい部分も含めて、時には CLINICS のコンセプトに立ち返って事業部・開発双方で時間をかけて認識を合わせることができたのは、大変でしたが良かったですね。 リリースまでのプロセスで顧客の声を反映 新居 : やはりここまでのプロジェクトだと最初の段階でちゃんとリリースまでを見据えた意識合わせをチーム内で行なっていたんですね。開発が進行していって、リリースするまではどのようにプロジェクトは進んでいったんでしょう。 宍戸 : リリースをする直前も、単純に仕様や機能について文書で説明しても理解するのは難しいので、酒井さんにディレクションをしてもらいながら、開発環境を使って QA チームやカスタマーサポートチームと一緒に認識合わせやリリースに向けた準備を行なっていきました。 来田 : あとは関係が構築できている 顧客に実際機能を見てもらって反応を伺う ということもしていきました。色々な使い方をしている顧客 10 件くらいにモックをベースにヒアリングをかけていきました。価格や導入時期なども含めた話を聞いていって、そこをすぐにプロジェクトにフィードバックする体制を構築していました。 酒井 : 実際に顧客の声をこうして聞けるというのは本当にプロダクトにとってありがたいことでした。特にこういう使い勝手などに直接関わる部分では、こうしたフィードバックの有無で完成度が違ってきます。 機能の正式リリース前から機能評価に協力していただける医療機関がいるのは、カスタマーサポートの皆さんが顧客と良好な関係を築けているおかげ だと思っています。こういった点はメドレーの強みだと思います。 プロジェクトで大切にしたこと 新居 : 開発段階からユーザーの声を吸い上げていけるのは、本当に良いですね。プロダクトの改善スピードなんかも段違いに早くなりそうです。他に大切にしていた部分などありますか? 酒井 : 今回の機能開発においては、プロジェクトの責任者として、開発以外のメンバーともしっかりコミュニケーションし合意形成を図ることを意識してプロジェクトを推進しました。 先ほどの話にも出ましたが 1 つの機能を実装するとなっても、実際の医療現場での使い勝手や、メドレーでのサポート、売り方など色々な視点で考えないといけないプロジェクトでした。そのため、そういったところをカスタマーサクセスのメンバー含む 10 人ほどと毎週 1 つ 1 つ納得がいくように詰めていくという作業をしていったりしました。 来田さんのように現役の医師の意見、顧客の意見なども取り入れられますし、カスタマーサポートには医療事務経験者の方もいらっしゃるので、多方面の視点を総合的に取り入れられるコミュニケーション を心がけていました。 手段はテキストや Figma でのデザイン、実際の実装を見せるなど適切な手段を選んでコミュニケーションコストを低くするようにして進行していました。 実際の機能実装にあたってはエンジニアも自身で考えて細かいボールを拾ったりしながら、実装 していってもらいました。この辺りはこのプロジェクトだからというよりも、メドレーの開発のベースになっている部分なので良いですよね。 メンバーのイメージ合わせがプロジェクト成功の鍵 新居 : 先ほどもお話がありましたが、プロジェクト途中の意思決定で関わるプロダクトが多くなったのでスイッチングが大変だったということでしたが、実際にそのタイミングになったときに全体のシステム設計なんかも変わったりしたんでしょうか。 田中 : 私はプロジェクトの途中から入ったんですが、そのタイミングで、宍戸さんや来田さんがちょうどシステム設計を考え直していたり、プロジェクト計画の引き直しをしているところでした。そこでまずは全体像を把握したいと考えてドキュメントなどを見せてもらったんです。各機能の細かい要件は分かったのですが、このプロジェクト全体としての各システムの責務や依存関係などがいまいち分かりづらく、メンバー間の認識も若干ズレが発生しているように感じました。なのでまずは**メンバーの認識を合わせ、各チームが何をするべきかを明確化するために、各システムの責務など全体感の整理整頓をして「見える化」**することから始めました。 このプロジェクトに限らずですが、プロジェクトとして抽象度の高い段階でのドキュメントは、文章のみの記載だと実は関係者にしっかりと伝わっていなかったということが多々あります。各自が頭の中でイメージした図に差異が発生している状態です。自分はそういう際に良く言っているのが**「大枠で良いので、まずは整理結果を図にしてみんなのイメージを合わせる**」ということです。こうして認識を合わせることで各チームが進めやすくなり後戻りリスクも軽減されるので、この段階でしっかりとイメージをすりあわせる事がとても重要だと考えています。 医療プラットフォーム CTO 田中さん 新居 : たしかにテキストで仕様なんかを書きながら説明しても、実は認識に齟齬があった…という経験があります。その後はどのようなプロセスでプロジェクト推進したんですか? 田中 : その後は各プロダクトにて実装予定の機能で、プロダクト間で共通化できそうな機能を共通サービスとして切り出すかどうかをみんなと考えていきました。結果として大きな機能では「ビデオ通話」部分を共通化するという意思決定をしました。他にも「問診票の提供」機能が候補としてあがっていましたが、共通化せずに各プロダクトの責務としてそれぞれ実装するという結論となりました。同じような機能に見えても、医科と歯科という業務ドメイン毎のコンテキスト差異を十分に検討した上での判断でした。 その差異がプロダクト機能としての強みとなり、安易な共通化はプロダクト成長の足枷となるリスクも存在 するためです。この判断も先ほどの全体の依存関係などが整理されたからこそ、意思決定できるものでした。 酒井 : 機能実装時に実感しましたが、やはり医科と歯科では細かい部分の要件などが異なる部分が多かったので、この段階で共通化の判断ができたのは大変助かりました。 PRM プロジェクトの大きな成果 新居 : 最後になりますが、このプロジェクトが終わっての成果はどのように考えていますか? 宍戸 : 冒頭に話した CLINICS の課題に関してはある程度、解決ができたと思います。 医療機関の業務に沿った形でプロダクトが進化 できた点はよかったと思います。特に問診周りなどは発熱外来などニーズの多様化に即して使い勝手が良くなったと思っています。 酒井 : 他にもビデオ通話部分など「オンライン診療」を提供するプロダクトとして「対面診療」だけではなく、もっと有用なプロダクトにできたかと思っています。 来田 : まだリリースしたばかりですが、初月の販売実績も期待していたよりも良く、そうした意味でも マーケットフィットした機能を出せたなという手応え が実感としてもありますね。 田中 : 予約やオンライン診療もドメインを統一した結果、会員登録数が増えていますので、そうした点も成果と言えます。 酒井 : あとは直接の成果というわけではないのですが、今回のプロジェクトの副産物として CLINICS の機能を整理整頓していった結果、 改めて「一気通貫で医療機関の業務をサポートできる」というトータルでの機能提供と各機能のシームレスな連携は CLINICS の強み だ、というのが再確認できたのは大きいです。 新居 : これからのさらなる CLINICS の発展に期待できますね!本日は長い時間お話ありがとうございました! おわりに メドレーとしてもかなり大規模なプロジェクトだった今回の PRM プロジェクトでしたが、みなさんのお話を聞いて各部署・各プロダクトで綿密に連携しながらプロジェクトを成功させようとしている様子が感じられるインタビューでした。 特にプロジェクト進行の話は非常に参考になりました。皆さんのご参考になる部分があれば幸いです。 こうしたプロジェクトで自分の力を発揮したい!と思った方はぜひお気軽に下記からご応募ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに みなさん、こんにちは。エンジニアの新居です。クラウド診療支援システム CLINICS が 8 月から大幅に アップデート して、より患者と医療機関が密接に繋がるようになりました。 今回のアップデートで中心になっている機能領域として PRM(Patient Relationship Management) というものがあります。 この PRM が一体どういったものなのか、プロジェクトはどのように進んでいったのかを関係者にインタビューしていきました。 メドレーで大規模なプロジェクト開発をどのように行なっているかの一端をお見せできればと考えています! インタビュイー紹介 宍戸さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の開発責任者。前職は株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2017 年メドレー入社。 酒井さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の UI/UX 全般を担当。本プロジェクトのリード。前職は受託開発会社で様々なサイトやシステムのデザインに従事。2019 年メドレー入社。 田中さん 医療プラットフォーム CTO。医療プラットフォームの開発統括をしながら、自身は患者統合基盤の開発を担当。前職までは SIer や IT コンサルタントを経て、株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2016 年メドレー入社。 来田さん 前職まではクリニックで精神科医として従事。CLINCS オンライン診療の立ち上げ時から関わる。現在は CLINICS のディレクションを中心として事業部と開発の架け橋となっている。 PRM とはどういうものなのか? 新居 : では今回中心となった PRM とはいったい何なのかというところから解説していただいて良いですか? 一般的にはそこまで知られてない言葉なのではないかと思うのですが…。 宍戸 : 元々 CRM(Customer Relationship Management)というマーケティングなどの文脈の用語があります。これは「自社サービスや商品を使っている顧客」と「サービスや商品を提供している企業」との関係性を管理することを指しますが、それをもとに適切なアクションをおこなうことで最終的には利益向上を目指すものと考えられます。 PRM は CRM と同様に医療機関と患者さんの関係性を管理することで必要なサービスを提供する ためのもの、と考えています。 CLINICS で基幹システムである電子カルテなどではなく、 オンライン診療・予約・問診など患者接点となる機能をターゲットにしてそれらを強化・改善 していったのが、CLINICS の今回の PRM プロジェクトになります。 CLINICS 開発責任者 宍戸さん 新居 : 確かに CRM は最近良く聞く言葉でしたが、PRM は患者と医療サービスを使う医療機関との関係を深めるためのものなんですね。 PRM プロジェクトの始まり CLINICS に感じていた課題とは 新居 : このプロジェクトは半年ほどかけていると聞きましたが、「PRM をやっていこう」となったのが半年前からだったんでしょうか? 宍戸 : 元々 CLINICS はオンライン診療のシステムとして 2016 年からサービスを提供してきました。2018 年からは CLINICS カルテとして電子カルテシステムも提供を開始しましたが、そのタイミング以降はオンライン診療部分に関してはそこまで大きくアップデートはしてこなかったんです。 現在はクラウド診療支援システムとして販売していますが、これまでは電子カルテなど基幹部分の強化がメインになっていたからです。しかし、数年開発・運用してきた中で、本来医療機関の診察のメインである対面診療の予約については課題が積み残されていたり、昨今の状況から来る オンライン診療のニーズなどを始めとして、患者接点を強化していく必要がある ということになり、今回取り組むことになりました。 酒井 : PRM というと本当にここ最近の潮流なのですが、 CLINICS はもともと「患者とつながる」をコンセプト としており、今回の機能強化などもこのコンセプトに基づき実施しています。その上で、患者とのタッチポイントで抜けていた部分や強化したほうが良い部分をより作り込んでいったという形です。 例えば、CLINICS は元々オンライン診療のシステムとして始まっており、対面診療予約に関して使いづらさを感じるケースがあったため、対面診療予約をより行いやすくするよう改修を行うなどです。 CLINICS デザイナー 酒井さん 新居 : 元々あったものをさらに患者とのタッチポイントを強化するために、改修・改善していったんですね。 プロジェクトの背景 どのくらいの規模のプロジェクトだったのか 新居 : 半年のプロジェクトということですが、規模としてはどの位の大きさだったんでしょうか? 酒井 : 私が経験したプロジェクトの中でも関わる部署など考えると一番大きかったと思います。開発はもちろんですが、CLINICS チームの セールス・カスタマーサクセス・マーケティング、広報もなのでほぼ CLINICS の全部署と関わっていました 。 田中 : 他にも、サービスとしては患者統合基盤・ 患者向けアプリ・ Dentis が関係しています。 宍戸 : QA チームにもしっかり入ってもらって QA を行なったりしてもらったので、本当に 医療プラットフォームのほぼ全てのチームと関わって開発していった 感じです。最終的にプロダクトの見せ方や売り方なども含めて調整していったので事業部とも密接に関わって作り上げていきました。 新しい PRM 機能をどうやって、プロダクトに融合させたのか 新居 : そこまで大きい規模のプロジェクトだということで、PRM の各種機能をどうやって上手くプロダクトに落としこむのか、実際にはどのようにみなさん推進されていったんでしょう。 宍戸 : プロジェクトの推進という意味では、最初は来田さんや( Dentis の運営を行なっている)プロダクト戦略室の平山さんと スモールスタートで意見を出し合いながらモックを作っていき、まずはビジュアル面から機能のイメージなどを刷り合わせ ていくのに、酒井さんに手を動かしてもらいながら進めていました。 その過程でこのプロジェクトにおける重要な意思決定がありました。CLINICS ではログイン不要のシンプルな予約機能を以前より提供していました。この機能は患者アプリとは元々違うドメインで運営していたものでしたが、今回のタイミングでこの両者のアプリケーションを患者アプリと同じドメインに統合して運営していこうという決定をしました。 患者アプリは CLINICS チームとは別チームで運営しているという背景もあり、チーム間の連携が格段に増え 、考えなければいけない事柄も多くなります。プロジェクトとして関係者が多くなってきたので、 今までの考え方を 1 度リセットしてプロジェクトの進め方を新しく模索する必要がありました 。 この点がかなり自分の中では大変でした。来田さんも大変だったと思います。こうした全体像を描いたり、全体スケジュールを決めたりというところでは、田中さんにアドバイスをもらいながらプロジェクトを進めていきました。 プロジェクトを推進するために必要だったこと 難しい意思決定をどのようにしていた? 新居 : 途中から一気にプロジェクトがスケールアップしたということなんですね。今回のプロジェクトは色々な部署も関わるし、たくさんの機能や改修が入っています。開発側と事業部側との意識合わせが、かなり必要だったんではないかと思うのですが、その辺はいかがだったでしょうか。 来田 : 例えば、今まで CLINICS では対面診療予約の際に患者のクレジットカード登録を必須としていたのですが、今回の改修でクレジットカード登録を不要に変更できるように対応を行いました。この対応は、CLINICS オンライン診療の初期から念願の機能だったりします。自分は事業部長である田中大介さんと今年の年明けくらいから話をして、メンバーに「よし、このプロジェクトをやっていこう」という話を持っていくようにしていました。やっぱり、大変なプロジェクトだというのはあったのでスタートを切れる状態に徐々にしていった感じです。 プロジェクトが佳境に入ってからは自分は各部署との調整がメインの業務でした。開発側と事業側とか CLINICS と Dentis だとかそういった立場の違いから来る要求の違いを納得できる仕様に落とし込む作業です。やっぱり、それぞれの立場でベストを尽くそうとみんな考えているんですが、全部を取り入れることはできないので。私が調整するときには それぞれ反対の立場の人たちの考えをきちんと伝えるようにということをしながら、仕様を決めていく ことを念頭に置いていました。 具体的な調整の例としては、開発する機能とセールスする際のプランの刷り合わせだったりですね。「機能としてはこうしたほうが使いやすいよね」というだけだと、セールス側としては「どこをウリにして売っていくのかというのが見えてこない」という話になったんですね。これはどちらの言い分も一理あるものなんですが、こういうところを時間をかけて 双方納得行くように決めていきました 。 CLINICS ディレクター・医師 来田さん 酒井 : この例でやったことは、まず現在のプランと機能を全部書き出して、組み合わせを整理するということでした。これをベースにして話を刷り合わせていったんですが、文字ベースだと人間分かった気になるんですが、実はそこまでピンと来ない。ですので、 LP のモックを作ってこれを見た顧客が導入したくなるようなウリになっているのかをすぐに判断できる状態 にしました。 宍戸 : こうしてちゃんと機能を解いていって細かい部分も含めて、時には CLINICS のコンセプトに立ち返って事業部・開発双方で時間をかけて認識を合わせることができたのは、大変でしたが良かったですね。 リリースまでのプロセスで顧客の声を反映 新居 : やはりここまでのプロジェクトだと最初の段階でちゃんとリリースまでを見据えた意識合わせをチーム内で行なっていたんですね。開発が進行していって、リリースするまではどのようにプロジェクトは進んでいったんでしょう。 宍戸 : リリースをする直前も、単純に仕様や機能について文書で説明しても理解するのは難しいので、酒井さんにディレクションをしてもらいながら、開発環境を使って QA チームやカスタマーサポートチームと一緒に認識合わせやリリースに向けた準備を行なっていきました。 来田 : あとは関係が構築できている 顧客に実際機能を見てもらって反応を伺う ということもしていきました。色々な使い方をしている顧客 10 件くらいにモックをベースにヒアリングをかけていきました。価格や導入時期なども含めた話を聞いていって、そこをすぐにプロジェクトにフィードバックする体制を構築していました。 酒井 : 実際に顧客の声をこうして聞けるというのは本当にプロダクトにとってありがたいことでした。特にこういう使い勝手などに直接関わる部分では、こうしたフィードバックの有無で完成度が違ってきます。 機能の正式リリース前から機能評価に協力していただける医療機関がいるのは、カスタマーサポートの皆さんが顧客と良好な関係を築けているおかげ だと思っています。こういった点はメドレーの強みだと思います。 プロジェクトで大切にしたこと 新居 : 開発段階からユーザーの声を吸い上げていけるのは、本当に良いですね。プロダクトの改善スピードなんかも段違いに早くなりそうです。他に大切にしていた部分などありますか? 酒井 : 今回の機能開発においては、プロジェクトの責任者として、開発以外のメンバーともしっかりコミュニケーションし合意形成を図ることを意識してプロジェクトを推進しました。 先ほどの話にも出ましたが 1 つの機能を実装するとなっても、実際の医療現場での使い勝手や、メドレーでのサポート、売り方など色々な視点で考えないといけないプロジェクトでした。そのため、そういったところをカスタマーサクセスのメンバー含む 10 人ほどと毎週 1 つ 1 つ納得がいくように詰めていくという作業をしていったりしました。 来田さんのように現役の医師の意見、顧客の意見なども取り入れられますし、カスタマーサポートには医療事務経験者の方もいらっしゃるので、多方面の視点を総合的に取り入れられるコミュニケーション を心がけていました。 手段はテキストや Figma でのデザイン、実際の実装を見せるなど適切な手段を選んでコミュニケーションコストを低くするようにして進行していました。 実際の機能実装にあたってはエンジニアも自身で考えて細かいボールを拾ったりしながら、実装 していってもらいました。この辺りはこのプロジェクトだからというよりも、メドレーの開発のベースになっている部分なので良いですよね。 メンバーのイメージ合わせがプロジェクト成功の鍵 新居 : 先ほどもお話がありましたが、プロジェクト途中の意思決定で関わるプロダクトが多くなったのでスイッチングが大変だったということでしたが、実際にそのタイミングになったときに全体のシステム設計なんかも変わったりしたんでしょうか。 田中 : 私はプロジェクトの途中から入ったんですが、そのタイミングで、宍戸さんや来田さんがちょうどシステム設計を考え直していたり、プロジェクト計画の引き直しをしているところでした。そこでまずは全体像を把握したいと考えてドキュメントなどを見せてもらったんです。各機能の細かい要件は分かったのですが、このプロジェクト全体としての各システムの責務や依存関係などがいまいち分かりづらく、メンバー間の認識も若干ズレが発生しているように感じました。なのでまずは**メンバーの認識を合わせ、各チームが何をするべきかを明確化するために、各システムの責務など全体感の整理整頓をして「見える化」**することから始めました。 このプロジェクトに限らずですが、プロジェクトとして抽象度の高い段階でのドキュメントは、文章のみの記載だと実は関係者にしっかりと伝わっていなかったということが多々あります。各自が頭の中でイメージした図に差異が発生している状態です。自分はそういう際に良く言っているのが**「大枠で良いので、まずは整理結果を図にしてみんなのイメージを合わせる**」ということです。こうして認識を合わせることで各チームが進めやすくなり後戻りリスクも軽減されるので、この段階でしっかりとイメージをすりあわせる事がとても重要だと考えています。 医療プラットフォーム CTO 田中さん 新居 : たしかにテキストで仕様なんかを書きながら説明しても、実は認識に齟齬があった…という経験があります。その後はどのようなプロセスでプロジェクト推進したんですか? 田中 : その後は各プロダクトにて実装予定の機能で、プロダクト間で共通化できそうな機能を共通サービスとして切り出すかどうかをみんなと考えていきました。結果として大きな機能では「ビデオ通話」部分を共通化するという意思決定をしました。他にも「問診票の提供」機能が候補としてあがっていましたが、共通化せずに各プロダクトの責務としてそれぞれ実装するという結論となりました。同じような機能に見えても、医科と歯科という業務ドメイン毎のコンテキスト差異を十分に検討した上での判断でした。 その差異がプロダクト機能としての強みとなり、安易な共通化はプロダクト成長の足枷となるリスクも存在 するためです。この判断も先ほどの全体の依存関係などが整理されたからこそ、意思決定できるものでした。 酒井 : 機能実装時に実感しましたが、やはり医科と歯科では細かい部分の要件などが異なる部分が多かったので、この段階で共通化の判断ができたのは大変助かりました。 PRM プロジェクトの大きな成果 新居 : 最後になりますが、このプロジェクトが終わっての成果はどのように考えていますか? 宍戸 : 冒頭に話した CLINICS の課題に関してはある程度、解決ができたと思います。 医療機関の業務に沿った形でプロダクトが進化 できた点はよかったと思います。特に問診周りなどは発熱外来などニーズの多様化に即して使い勝手が良くなったと思っています。 酒井 : 他にもビデオ通話部分など「オンライン診療」を提供するプロダクトとして「対面診療」だけではなく、もっと有用なプロダクトにできたかと思っています。 来田 : まだリリースしたばかりですが、初月の販売実績も期待していたよりも良く、そうした意味でも マーケットフィットした機能を出せたなという手応え が実感としてもありますね。 田中 : 予約やオンライン診療もドメインを統一した結果、会員登録数が増えていますので、そうした点も成果と言えます。 酒井 : あとは直接の成果というわけではないのですが、今回のプロジェクトの副産物として CLINICS の機能を整理整頓していった結果、 改めて「一気通貫で医療機関の業務をサポートできる」というトータルでの機能提供と各機能のシームレスな連携は CLINICS の強み だ、というのが再確認できたのは大きいです。 新居 : これからのさらなる CLINICS の発展に期待できますね!本日は長い時間お話ありがとうございました! おわりに メドレーとしてもかなり大規模なプロジェクトだった今回の PRM プロジェクトでしたが、みなさんのお話を聞いて各部署・各プロダクトで綿密に連携しながらプロジェクトを成功させようとしている様子が感じられるインタビューでした。 特にプロジェクト進行の話は非常に参考になりました。皆さんのご参考になる部分があれば幸いです。 こうしたプロジェクトで自分の力を発揮したい!と思った方はぜひお気軽に下記からご応募ください。 https://www.medley.jp/jobs/
はじめに みなさん、こんにちは。エンジニアの新居です。クラウド診療支援システム CLINICS が 8 月から大幅に アップデート して、より患者と医療機関が密接に繋がるようになりました。 今回のアップデートで中心になっている機能領域として PRM(Patient Relationship Management) というものがあります。 この PRM が一体どういったものなのか、プロジェクトはどのように進んでいったのかを関係者にインタビューしていきました。 メドレーで大規模なプロジェクト開発をどのように行なっているかの一端をお見せできればと考えています! インタビュイー紹介 宍戸さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の開発責任者。前職は株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2017 年メドレー入社。 酒井さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の UI/UX 全般を担当。本プロジェクトのリード。前職は受託開発会社で様々なサイトやシステムのデザインに従事。2019 年メドレー入社。 田中さん 医療プラットフォーム CTO。医療プラットフォームの開発統括をしながら、自身は患者統合基盤の開発を担当。前職までは SIer や IT コンサルタントを経て、株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2016 年メドレー入社。 来田さん 前職まではクリニックで精神科医として従事。CLINCS オンライン診療の立ち上げ時から関わる。現在は CLINICS のディレクションを中心として事業部と開発の架け橋となっている。 PRM とはどういうものなのか? 新居 : では今回中心となった PRM とはいったい何なのかというところから解説していただいて良いですか? 一般的にはそこまで知られてない言葉なのではないかと思うのですが…。 宍戸 : 元々 CRM(Customer Relationship Management)というマーケティングなどの文脈の用語があります。これは「自社サービスや商品を使っている顧客」と「サービスや商品を提供している企業」との関係性を管理することを指しますが、それをもとに適切なアクションをおこなうことで最終的には利益向上を目指すものと考えられます。 PRM は CRM と同様に医療機関と患者さんの関係性を管理することで必要なサービスを提供する ためのもの、と考えています。 CLINICS で基幹システムである電子カルテなどではなく、 オンライン診療・予約・問診など患者接点となる機能をターゲットにしてそれらを強化・改善 していったのが、CLINICS の今回の PRM プロジェクトになります。 CLINICS 開発責任者 宍戸さん 新居 : 確かに CRM は最近良く聞く言葉でしたが、PRM は患者と医療サービスを使う医療機関との関係を深めるためのものなんですね。 PRM プロジェクトの始まり CLINICS に感じていた課題とは 新居 : このプロジェクトは半年ほどかけていると聞きましたが、「PRM をやっていこう」となったのが半年前からだったんでしょうか? 宍戸 : 元々 CLINICS はオンライン診療のシステムとして 2016 年からサービスを提供してきました。2018 年からは CLINICS カルテとして電子カルテシステムも提供を開始しましたが、そのタイミング以降はオンライン診療部分に関してはそこまで大きくアップデートはしてこなかったんです。 現在はクラウド診療支援システムとして販売していますが、これまでは電子カルテなど基幹部分の強化がメインになっていたからです。しかし、数年開発・運用してきた中で、本来医療機関の診察のメインである対面診療の予約については課題が積み残されていたり、昨今の状況から来る オンライン診療のニーズなどを始めとして、患者接点を強化していく必要がある ということになり、今回取り組むことになりました。 酒井 : PRM というと本当にここ最近の潮流なのですが、 CLINICS はもともと「患者とつながる」をコンセプト としており、今回の機能強化などもこのコンセプトに基づき実施しています。その上で、患者とのタッチポイントで抜けていた部分や強化したほうが良い部分をより作り込んでいったという形です。 例えば、CLINICS は元々オンライン診療のシステムとして始まっており、対面診療予約に関して使いづらさを感じるケースがあったため、対面診療予約をより行いやすくするよう改修を行うなどです。 CLINICS デザイナー 酒井さん 新居 : 元々あったものをさらに患者とのタッチポイントを強化するために、改修・改善していったんですね。 プロジェクトの背景 どのくらいの規模のプロジェクトだったのか 新居 : 半年のプロジェクトということですが、規模としてはどの位の大きさだったんでしょうか? 酒井 : 私が経験したプロジェクトの中でも関わる部署など考えると一番大きかったと思います。開発はもちろんですが、CLINICS チームの セールス・カスタマーサクセス・マーケティング、広報もなのでほぼ CLINICS の全部署と関わっていました 。 田中 : 他にも、サービスとしては患者統合基盤・ 患者向けアプリ・ Dentis が関係しています。 宍戸 : QA チームにもしっかり入ってもらって QA を行なったりしてもらったので、本当に 医療プラットフォームのほぼ全てのチームと関わって開発していった 感じです。最終的にプロダクトの見せ方や売り方なども含めて調整していったので事業部とも密接に関わって作り上げていきました。 新しい PRM 機能をどうやって、プロダクトに融合させたのか 新居 : そこまで大きい規模のプロジェクトだということで、PRM の各種機能をどうやって上手くプロダクトに落としこむのか、実際にはどのようにみなさん推進されていったんでしょう。 宍戸 : プロジェクトの推進という意味では、最初は来田さんや( Dentis の運営を行なっている)プロダクト戦略室の平山さんと スモールスタートで意見を出し合いながらモックを作っていき、まずはビジュアル面から機能のイメージなどを刷り合わせ ていくのに、酒井さんに手を動かしてもらいながら進めていました。 その過程でこのプロジェクトにおける重要な意思決定がありました。CLINICS ではログイン不要のシンプルな予約機能を以前より提供していました。この機能は患者アプリとは元々違うドメインで運営していたものでしたが、今回のタイミングでこの両者のアプリケーションを患者アプリと同じドメインに統合して運営していこうという決定をしました。 患者アプリは CLINICS チームとは別チームで運営しているという背景もあり、チーム間の連携が格段に増え 、考えなければいけない事柄も多くなります。プロジェクトとして関係者が多くなってきたので、 今までの考え方を 1 度リセットしてプロジェクトの進め方を新しく模索する必要がありました 。 この点がかなり自分の中では大変でした。来田さんも大変だったと思います。こうした全体像を描いたり、全体スケジュールを決めたりというところでは、田中さんにアドバイスをもらいながらプロジェクトを進めていきました。 プロジェクトを推進するために必要だったこと 難しい意思決定をどのようにしていた? 新居 : 途中から一気にプロジェクトがスケールアップしたということなんですね。今回のプロジェクトは色々な部署も関わるし、たくさんの機能や改修が入っています。開発側と事業部側との意識合わせが、かなり必要だったんではないかと思うのですが、その辺はいかがだったでしょうか。 来田 : 例えば、今まで CLINICS では対面診療予約の際に患者のクレジットカード登録を必須としていたのですが、今回の改修でクレジットカード登録を不要に変更できるように対応を行いました。この対応は、CLINICS オンライン診療の初期から念願の機能だったりします。自分は事業部長である田中大介さんと今年の年明けくらいから話をして、メンバーに「よし、このプロジェクトをやっていこう」という話を持っていくようにしていました。やっぱり、大変なプロジェクトだというのはあったのでスタートを切れる状態に徐々にしていった感じです。 プロジェクトが佳境に入ってからは自分は各部署との調整がメインの業務でした。開発側と事業側とか CLINICS と Dentis だとかそういった立場の違いから来る要求の違いを納得できる仕様に落とし込む作業です。やっぱり、それぞれの立場でベストを尽くそうとみんな考えているんですが、全部を取り入れることはできないので。私が調整するときには それぞれ反対の立場の人たちの考えをきちんと伝えるようにということをしながら、仕様を決めていく ことを念頭に置いていました。 具体的な調整の例としては、開発する機能とセールスする際のプランの刷り合わせだったりですね。「機能としてはこうしたほうが使いやすいよね」というだけだと、セールス側としては「どこをウリにして売っていくのかというのが見えてこない」という話になったんですね。これはどちらの言い分も一理あるものなんですが、こういうところを時間をかけて 双方納得行くように決めていきました 。 CLINICS ディレクター・医師 来田さん 酒井 : この例でやったことは、まず現在のプランと機能を全部書き出して、組み合わせを整理するということでした。これをベースにして話を刷り合わせていったんですが、文字ベースだと人間分かった気になるんですが、実はそこまでピンと来ない。ですので、 LP のモックを作ってこれを見た顧客が導入したくなるようなウリになっているのかをすぐに判断できる状態 にしました。 宍戸 : こうしてちゃんと機能を解いていって細かい部分も含めて、時には CLINICS のコンセプトに立ち返って事業部・開発双方で時間をかけて認識を合わせることができたのは、大変でしたが良かったですね。 リリースまでのプロセスで顧客の声を反映 新居 : やはりここまでのプロジェクトだと最初の段階でちゃんとリリースまでを見据えた意識合わせをチーム内で行なっていたんですね。開発が進行していって、リリースするまではどのようにプロジェクトは進んでいったんでしょう。 宍戸 : リリースをする直前も、単純に仕様や機能について文書で説明しても理解するのは難しいので、酒井さんにディレクションをしてもらいながら、開発環境を使って QA チームやカスタマーサポートチームと一緒に認識合わせやリリースに向けた準備を行なっていきました。 来田 : あとは関係が構築できている 顧客に実際機能を見てもらって反応を伺う ということもしていきました。色々な使い方をしている顧客 10 件くらいにモックをベースにヒアリングをかけていきました。価格や導入時期なども含めた話を聞いていって、そこをすぐにプロジェクトにフィードバックする体制を構築していました。 酒井 : 実際に顧客の声をこうして聞けるというのは本当にプロダクトにとってありがたいことでした。特にこういう使い勝手などに直接関わる部分では、こうしたフィードバックの有無で完成度が違ってきます。 機能の正式リリース前から機能評価に協力していただける医療機関がいるのは、カスタマーサポートの皆さんが顧客と良好な関係を築けているおかげ だと思っています。こういった点はメドレーの強みだと思います。 プロジェクトで大切にしたこと 新居 : 開発段階からユーザーの声を吸い上げていけるのは、本当に良いですね。プロダクトの改善スピードなんかも段違いに早くなりそうです。他に大切にしていた部分などありますか? 酒井 : 今回の機能開発においては、プロジェクトの責任者として、開発以外のメンバーともしっかりコミュニケーションし合意形成を図ることを意識してプロジェクトを推進しました。 先ほどの話にも出ましたが 1 つの機能を実装するとなっても、実際の医療現場での使い勝手や、メドレーでのサポート、売り方など色々な視点で考えないといけないプロジェクトでした。そのため、そういったところをカスタマーサクセスのメンバー含む 10 人ほどと毎週 1 つ 1 つ納得がいくように詰めていくという作業をしていったりしました。 来田さんのように現役の医師の意見、顧客の意見なども取り入れられますし、カスタマーサポートには医療事務経験者の方もいらっしゃるので、多方面の視点を総合的に取り入れられるコミュニケーション を心がけていました。 手段はテキストや Figma でのデザイン、実際の実装を見せるなど適切な手段を選んでコミュニケーションコストを低くするようにして進行していました。 実際の機能実装にあたってはエンジニアも自身で考えて細かいボールを拾ったりしながら、実装 していってもらいました。この辺りはこのプロジェクトだからというよりも、メドレーの開発のベースになっている部分なので良いですよね。 メンバーのイメージ合わせがプロジェクト成功の鍵 新居 : 先ほどもお話がありましたが、プロジェクト途中の意思決定で関わるプロダクトが多くなったのでスイッチングが大変だったということでしたが、実際にそのタイミングになったときに全体のシステム設計なんかも変わったりしたんでしょうか。 田中 : 私はプロジェクトの途中から入ったんですが、そのタイミングで、宍戸さんや来田さんがちょうどシステム設計を考え直していたり、プロジェクト計画の引き直しをしているところでした。そこでまずは全体像を把握したいと考えてドキュメントなどを見せてもらったんです。各機能の細かい要件は分かったのですが、このプロジェクト全体としての各システムの責務や依存関係などがいまいち分かりづらく、メンバー間の認識も若干ズレが発生しているように感じました。なのでまずは**メンバーの認識を合わせ、各チームが何をするべきかを明確化するために、各システムの責務など全体感の整理整頓をして「見える化」**することから始めました。 このプロジェクトに限らずですが、プロジェクトとして抽象度の高い段階でのドキュメントは、文章のみの記載だと実は関係者にしっかりと伝わっていなかったということが多々あります。各自が頭の中でイメージした図に差異が発生している状態です。自分はそういう際に良く言っているのが**「大枠で良いので、まずは整理結果を図にしてみんなのイメージを合わせる**」ということです。こうして認識を合わせることで各チームが進めやすくなり後戻りリスクも軽減されるので、この段階でしっかりとイメージをすりあわせる事がとても重要だと考えています。 医療プラットフォーム CTO 田中さん 新居 : たしかにテキストで仕様なんかを書きながら説明しても、実は認識に齟齬があった…という経験があります。その後はどのようなプロセスでプロジェクト推進したんですか? 田中 : その後は各プロダクトにて実装予定の機能で、プロダクト間で共通化できそうな機能を共通サービスとして切り出すかどうかをみんなと考えていきました。結果として大きな機能では「ビデオ通話」部分を共通化するという意思決定をしました。他にも「問診票の提供」機能が候補としてあがっていましたが、共通化せずに各プロダクトの責務としてそれぞれ実装するという結論となりました。同じような機能に見えても、医科と歯科という業務ドメイン毎のコンテキスト差異を十分に検討した上での判断でした。 その差異がプロダクト機能としての強みとなり、安易な共通化はプロダクト成長の足枷となるリスクも存在 するためです。この判断も先ほどの全体の依存関係などが整理されたからこそ、意思決定できるものでした。 酒井 : 機能実装時に実感しましたが、やはり医科と歯科では細かい部分の要件などが異なる部分が多かったので、この段階で共通化の判断ができたのは大変助かりました。 PRM プロジェクトの大きな成果 新居 : 最後になりますが、このプロジェクトが終わっての成果はどのように考えていますか? 宍戸 : 冒頭に話した CLINICS の課題に関してはある程度、解決ができたと思います。 医療機関の業務に沿った形でプロダクトが進化 できた点はよかったと思います。特に問診周りなどは発熱外来などニーズの多様化に即して使い勝手が良くなったと思っています。 酒井 : 他にもビデオ通話部分など「オンライン診療」を提供するプロダクトとして「対面診療」だけではなく、もっと有用なプロダクトにできたかと思っています。 来田 : まだリリースしたばかりですが、初月の販売実績も期待していたよりも良く、そうした意味でも マーケットフィットした機能を出せたなという手応え が実感としてもありますね。 田中 : 予約やオンライン診療もドメインを統一した結果、会員登録数が増えていますので、そうした点も成果と言えます。 酒井 : あとは直接の成果というわけではないのですが、今回のプロジェクトの副産物として CLINICS の機能を整理整頓していった結果、 改めて「一気通貫で医療機関の業務をサポートできる」というトータルでの機能提供と各機能のシームレスな連携は CLINICS の強み だ、というのが再確認できたのは大きいです。 新居 : これからのさらなる CLINICS の発展に期待できますね!本日は長い時間お話ありがとうございました! おわりに メドレーとしてもかなり大規模なプロジェクトだった今回の PRM プロジェクトでしたが、みなさんのお話を聞いて各部署・各プロダクトで綿密に連携しながらプロジェクトを成功させようとしている様子が感じられるインタビューでした。 特にプロジェクト進行の話は非常に参考になりました。皆さんのご参考になる部分があれば幸いです。 こうしたプロジェクトで自分の力を発揮したい!と思った方はぜひお気軽に下記からご応募ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに みなさん、こんにちは。エンジニアの新居です。クラウド診療支援システム CLINICS が 8 月から大幅に アップデート して、より患者と医療機関が密接に繋がるようになりました。 今回のアップデートで中心になっている機能領域として PRM(Patient Relationship Management) というものがあります。 この PRM が一体どういったものなのか、プロジェクトはどのように進んでいったのかを関係者にインタビューしていきました。 メドレーで大規模なプロジェクト開発をどのように行なっているかの一端をお見せできればと考えています! インタビュイー紹介 宍戸さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の開発責任者。前職は株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2017 年メドレー入社。 酒井さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の UI/UX 全般を担当。本プロジェクトのリード。前職は受託開発会社で様々なサイトやシステムのデザインに従事。2019 年メドレー入社。 田中さん 医療プラットフォーム CTO。医療プラットフォームの開発統括をしながら、自身は患者統合基盤の開発を担当。前職までは SIer や IT コンサルタントを経て、株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2016 年メドレー入社。 来田さん 前職まではクリニックで精神科医として従事。CLINCS オンライン診療の立ち上げ時から関わる。現在は CLINICS のディレクションを中心として事業部と開発の架け橋となっている。 PRM とはどういうものなのか? 新居 : では今回中心となった PRM とはいったい何なのかというところから解説していただいて良いですか? 一般的にはそこまで知られてない言葉なのではないかと思うのですが…。 宍戸 : 元々 CRM(Customer Relationship Management)というマーケティングなどの文脈の用語があります。これは「自社サービスや商品を使っている顧客」と「サービスや商品を提供している企業」との関係性を管理することを指しますが、それをもとに適切なアクションをおこなうことで最終的には利益向上を目指すものと考えられます。 PRM は CRM と同様に医療機関と患者さんの関係性を管理することで必要なサービスを提供する ためのもの、と考えています。 CLINICS で基幹システムである電子カルテなどではなく、 オンライン診療・予約・問診など患者接点となる機能をターゲットにしてそれらを強化・改善 していったのが、CLINICS の今回の PRM プロジェクトになります。 CLINICS 開発責任者 宍戸さん 新居 : 確かに CRM は最近良く聞く言葉でしたが、PRM は患者と医療サービスを使う医療機関との関係を深めるためのものなんですね。 PRM プロジェクトの始まり CLINICS に感じていた課題とは 新居 : このプロジェクトは半年ほどかけていると聞きましたが、「PRM をやっていこう」となったのが半年前からだったんでしょうか? 宍戸 : 元々 CLINICS はオンライン診療のシステムとして 2016 年からサービスを提供してきました。2018 年からは CLINICS カルテとして電子カルテシステムも提供を開始しましたが、そのタイミング以降はオンライン診療部分に関してはそこまで大きくアップデートはしてこなかったんです。 現在はクラウド診療支援システムとして販売していますが、これまでは電子カルテなど基幹部分の強化がメインになっていたからです。しかし、数年開発・運用してきた中で、本来医療機関の診察のメインである対面診療の予約については課題が積み残されていたり、昨今の状況から来る オンライン診療のニーズなどを始めとして、患者接点を強化していく必要がある ということになり、今回取り組むことになりました。 酒井 : PRM というと本当にここ最近の潮流なのですが、 CLINICS はもともと「患者とつながる」をコンセプト としており、今回の機能強化などもこのコンセプトに基づき実施しています。その上で、患者とのタッチポイントで抜けていた部分や強化したほうが良い部分をより作り込んでいったという形です。 例えば、CLINICS は元々オンライン診療のシステムとして始まっており、対面診療予約に関して使いづらさを感じるケースがあったため、対面診療予約をより行いやすくするよう改修を行うなどです。 CLINICS デザイナー 酒井さん 新居 : 元々あったものをさらに患者とのタッチポイントを強化するために、改修・改善していったんですね。 プロジェクトの背景 どのくらいの規模のプロジェクトだったのか 新居 : 半年のプロジェクトということですが、規模としてはどの位の大きさだったんでしょうか? 酒井 : 私が経験したプロジェクトの中でも関わる部署など考えると一番大きかったと思います。開発はもちろんですが、CLINICS チームの セールス・カスタマーサクセス・マーケティング、広報もなのでほぼ CLINICS の全部署と関わっていました 。 田中 : 他にも、サービスとしては患者統合基盤・ 患者向けアプリ・ Dentis が関係しています。 宍戸 : QA チームにもしっかり入ってもらって QA を行なったりしてもらったので、本当に 医療プラットフォームのほぼ全てのチームと関わって開発していった 感じです。最終的にプロダクトの見せ方や売り方なども含めて調整していったので事業部とも密接に関わって作り上げていきました。 新しい PRM 機能をどうやって、プロダクトに融合させたのか 新居 : そこまで大きい規模のプロジェクトだということで、PRM の各種機能をどうやって上手くプロダクトに落としこむのか、実際にはどのようにみなさん推進されていったんでしょう。 宍戸 : プロジェクトの推進という意味では、最初は来田さんや( Dentis の運営を行なっている)プロダクト戦略室の平山さんと スモールスタートで意見を出し合いながらモックを作っていき、まずはビジュアル面から機能のイメージなどを刷り合わせ ていくのに、酒井さんに手を動かしてもらいながら進めていました。 その過程でこのプロジェクトにおける重要な意思決定がありました。CLINICS ではログイン不要のシンプルな予約機能を以前より提供していました。この機能は患者アプリとは元々違うドメインで運営していたものでしたが、今回のタイミングでこの両者のアプリケーションを患者アプリと同じドメインに統合して運営していこうという決定をしました。 患者アプリは CLINICS チームとは別チームで運営しているという背景もあり、チーム間の連携が格段に増え 、考えなければいけない事柄も多くなります。プロジェクトとして関係者が多くなってきたので、 今までの考え方を 1 度リセットしてプロジェクトの進め方を新しく模索する必要がありました 。 この点がかなり自分の中では大変でした。来田さんも大変だったと思います。こうした全体像を描いたり、全体スケジュールを決めたりというところでは、田中さんにアドバイスをもらいながらプロジェクトを進めていきました。 プロジェクトを推進するために必要だったこと 難しい意思決定をどのようにしていた? 新居 : 途中から一気にプロジェクトがスケールアップしたということなんですね。今回のプロジェクトは色々な部署も関わるし、たくさんの機能や改修が入っています。開発側と事業部側との意識合わせが、かなり必要だったんではないかと思うのですが、その辺はいかがだったでしょうか。 来田 : 例えば、今まで CLINICS では対面診療予約の際に患者のクレジットカード登録を必須としていたのですが、今回の改修でクレジットカード登録を不要に変更できるように対応を行いました。この対応は、CLINICS オンライン診療の初期から念願の機能だったりします。自分は事業部長である田中大介さんと今年の年明けくらいから話をして、メンバーに「よし、このプロジェクトをやっていこう」という話を持っていくようにしていました。やっぱり、大変なプロジェクトだというのはあったのでスタートを切れる状態に徐々にしていった感じです。 プロジェクトが佳境に入ってからは自分は各部署との調整がメインの業務でした。開発側と事業側とか CLINICS と Dentis だとかそういった立場の違いから来る要求の違いを納得できる仕様に落とし込む作業です。やっぱり、それぞれの立場でベストを尽くそうとみんな考えているんですが、全部を取り入れることはできないので。私が調整するときには それぞれ反対の立場の人たちの考えをきちんと伝えるようにということをしながら、仕様を決めていく ことを念頭に置いていました。 具体的な調整の例としては、開発する機能とセールスする際のプランの刷り合わせだったりですね。「機能としてはこうしたほうが使いやすいよね」というだけだと、セールス側としては「どこをウリにして売っていくのかというのが見えてこない」という話になったんですね。これはどちらの言い分も一理あるものなんですが、こういうところを時間をかけて 双方納得行くように決めていきました 。 CLINICS ディレクター・医師 来田さん 酒井 : この例でやったことは、まず現在のプランと機能を全部書き出して、組み合わせを整理するということでした。これをベースにして話を刷り合わせていったんですが、文字ベースだと人間分かった気になるんですが、実はそこまでピンと来ない。ですので、 LP のモックを作ってこれを見た顧客が導入したくなるようなウリになっているのかをすぐに判断できる状態 にしました。 宍戸 : こうしてちゃんと機能を解いていって細かい部分も含めて、時には CLINICS のコンセプトに立ち返って事業部・開発双方で時間をかけて認識を合わせることができたのは、大変でしたが良かったですね。 リリースまでのプロセスで顧客の声を反映 新居 : やはりここまでのプロジェクトだと最初の段階でちゃんとリリースまでを見据えた意識合わせをチーム内で行なっていたんですね。開発が進行していって、リリースするまではどのようにプロジェクトは進んでいったんでしょう。 宍戸 : リリースをする直前も、単純に仕様や機能について文書で説明しても理解するのは難しいので、酒井さんにディレクションをしてもらいながら、開発環境を使って QA チームやカスタマーサポートチームと一緒に認識合わせやリリースに向けた準備を行なっていきました。 来田 : あとは関係が構築できている 顧客に実際機能を見てもらって反応を伺う ということもしていきました。色々な使い方をしている顧客 10 件くらいにモックをベースにヒアリングをかけていきました。価格や導入時期なども含めた話を聞いていって、そこをすぐにプロジェクトにフィードバックする体制を構築していました。 酒井 : 実際に顧客の声をこうして聞けるというのは本当にプロダクトにとってありがたいことでした。特にこういう使い勝手などに直接関わる部分では、こうしたフィードバックの有無で完成度が違ってきます。 機能の正式リリース前から機能評価に協力していただける医療機関がいるのは、カスタマーサポートの皆さんが顧客と良好な関係を築けているおかげ だと思っています。こういった点はメドレーの強みだと思います。 プロジェクトで大切にしたこと 新居 : 開発段階からユーザーの声を吸い上げていけるのは、本当に良いですね。プロダクトの改善スピードなんかも段違いに早くなりそうです。他に大切にしていた部分などありますか? 酒井 : 今回の機能開発においては、プロジェクトの責任者として、開発以外のメンバーともしっかりコミュニケーションし合意形成を図ることを意識してプロジェクトを推進しました。 先ほどの話にも出ましたが 1 つの機能を実装するとなっても、実際の医療現場での使い勝手や、メドレーでのサポート、売り方など色々な視点で考えないといけないプロジェクトでした。そのため、そういったところをカスタマーサクセスのメンバー含む 10 人ほどと毎週 1 つ 1 つ納得がいくように詰めていくという作業をしていったりしました。 来田さんのように現役の医師の意見、顧客の意見なども取り入れられますし、カスタマーサポートには医療事務経験者の方もいらっしゃるので、多方面の視点を総合的に取り入れられるコミュニケーション を心がけていました。 手段はテキストや Figma でのデザイン、実際の実装を見せるなど適切な手段を選んでコミュニケーションコストを低くするようにして進行していました。 実際の機能実装にあたってはエンジニアも自身で考えて細かいボールを拾ったりしながら、実装 していってもらいました。この辺りはこのプロジェクトだからというよりも、メドレーの開発のベースになっている部分なので良いですよね。 メンバーのイメージ合わせがプロジェクト成功の鍵 新居 : 先ほどもお話がありましたが、プロジェクト途中の意思決定で関わるプロダクトが多くなったのでスイッチングが大変だったということでしたが、実際にそのタイミングになったときに全体のシステム設計なんかも変わったりしたんでしょうか。 田中 : 私はプロジェクトの途中から入ったんですが、そのタイミングで、宍戸さんや来田さんがちょうどシステム設計を考え直していたり、プロジェクト計画の引き直しをしているところでした。そこでまずは全体像を把握したいと考えてドキュメントなどを見せてもらったんです。各機能の細かい要件は分かったのですが、このプロジェクト全体としての各システムの責務や依存関係などがいまいち分かりづらく、メンバー間の認識も若干ズレが発生しているように感じました。なのでまずは**メンバーの認識を合わせ、各チームが何をするべきかを明確化するために、各システムの責務など全体感の整理整頓をして「見える化」**することから始めました。 このプロジェクトに限らずですが、プロジェクトとして抽象度の高い段階でのドキュメントは、文章のみの記載だと実は関係者にしっかりと伝わっていなかったということが多々あります。各自が頭の中でイメージした図に差異が発生している状態です。自分はそういう際に良く言っているのが**「大枠で良いので、まずは整理結果を図にしてみんなのイメージを合わせる**」ということです。こうして認識を合わせることで各チームが進めやすくなり後戻りリスクも軽減されるので、この段階でしっかりとイメージをすりあわせる事がとても重要だと考えています。 医療プラットフォーム CTO 田中さん 新居 : たしかにテキストで仕様なんかを書きながら説明しても、実は認識に齟齬があった…という経験があります。その後はどのようなプロセスでプロジェクト推進したんですか? 田中 : その後は各プロダクトにて実装予定の機能で、プロダクト間で共通化できそうな機能を共通サービスとして切り出すかどうかをみんなと考えていきました。結果として大きな機能では「ビデオ通話」部分を共通化するという意思決定をしました。他にも「問診票の提供」機能が候補としてあがっていましたが、共通化せずに各プロダクトの責務としてそれぞれ実装するという結論となりました。同じような機能に見えても、医科と歯科という業務ドメイン毎のコンテキスト差異を十分に検討した上での判断でした。 その差異がプロダクト機能としての強みとなり、安易な共通化はプロダクト成長の足枷となるリスクも存在 するためです。この判断も先ほどの全体の依存関係などが整理されたからこそ、意思決定できるものでした。 酒井 : 機能実装時に実感しましたが、やはり医科と歯科では細かい部分の要件などが異なる部分が多かったので、この段階で共通化の判断ができたのは大変助かりました。 PRM プロジェクトの大きな成果 新居 : 最後になりますが、このプロジェクトが終わっての成果はどのように考えていますか? 宍戸 : 冒頭に話した CLINICS の課題に関してはある程度、解決ができたと思います。 医療機関の業務に沿った形でプロダクトが進化 できた点はよかったと思います。特に問診周りなどは発熱外来などニーズの多様化に即して使い勝手が良くなったと思っています。 酒井 : 他にもビデオ通話部分など「オンライン診療」を提供するプロダクトとして「対面診療」だけではなく、もっと有用なプロダクトにできたかと思っています。 来田 : まだリリースしたばかりですが、初月の販売実績も期待していたよりも良く、そうした意味でも マーケットフィットした機能を出せたなという手応え が実感としてもありますね。 田中 : 予約やオンライン診療もドメインを統一した結果、会員登録数が増えていますので、そうした点も成果と言えます。 酒井 : あとは直接の成果というわけではないのですが、今回のプロジェクトの副産物として CLINICS の機能を整理整頓していった結果、 改めて「一気通貫で医療機関の業務をサポートできる」というトータルでの機能提供と各機能のシームレスな連携は CLINICS の強み だ、というのが再確認できたのは大きいです。 新居 : これからのさらなる CLINICS の発展に期待できますね!本日は長い時間お話ありがとうございました! おわりに メドレーとしてもかなり大規模なプロジェクトだった今回の PRM プロジェクトでしたが、みなさんのお話を聞いて各部署・各プロダクトで綿密に連携しながらプロジェクトを成功させようとしている様子が感じられるインタビューでした。 特にプロジェクト進行の話は非常に参考になりました。皆さんのご参考になる部分があれば幸いです。 こうしたプロジェクトで自分の力を発揮したい!と思った方はぜひお気軽に下記からご応募ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに みなさん、こんにちは。エンジニアの新居です。クラウド診療支援システム CLINICS が 8 月から大幅に アップデート して、より患者と医療機関が密接に繋がるようになりました。 今回のアップデートで中心になっている機能領域として PRM(Patient Relationship Management) というものがあります。 この PRM が一体どういったものなのか、プロジェクトはどのように進んでいったのかを関係者にインタビューしていきました。 メドレーで大規模なプロジェクト開発をどのように行なっているかの一端をお見せできればと考えています! インタビュイー紹介 宍戸さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の開発責任者。前職は株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2017 年メドレー入社。 酒井さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の UI/UX 全般を担当。本プロジェクトのリード。前職は受託開発会社で様々なサイトやシステムのデザインに従事。2019 年メドレー入社。 田中さん 医療プラットフォーム CTO。医療プラットフォームの開発統括をしながら、自身は患者統合基盤の開発を担当。前職までは SIer や IT コンサルタントを経て、株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2016 年メドレー入社。 来田さん 前職まではクリニックで精神科医として従事。CLINCS オンライン診療の立ち上げ時から関わる。現在は CLINICS のディレクションを中心として事業部と開発の架け橋となっている。 PRM とはどういうものなのか? 新居 : では今回中心となった PRM とはいったい何なのかというところから解説していただいて良いですか? 一般的にはそこまで知られてない言葉なのではないかと思うのですが…。 宍戸 : 元々 CRM(Customer Relationship Management)というマーケティングなどの文脈の用語があります。これは「自社サービスや商品を使っている顧客」と「サービスや商品を提供している企業」との関係性を管理することを指しますが、それをもとに適切なアクションをおこなうことで最終的には利益向上を目指すものと考えられます。 PRM は CRM と同様に医療機関と患者さんの関係性を管理することで必要なサービスを提供する ためのもの、と考えています。 CLINICS で基幹システムである電子カルテなどではなく、 オンライン診療・予約・問診など患者接点となる機能をターゲットにしてそれらを強化・改善 していったのが、CLINICS の今回の PRM プロジェクトになります。 CLINICS 開発責任者 宍戸さん 新居 : 確かに CRM は最近良く聞く言葉でしたが、PRM は患者と医療サービスを使う医療機関との関係を深めるためのものなんですね。 PRM プロジェクトの始まり CLINICS に感じていた課題とは 新居 : このプロジェクトは半年ほどかけていると聞きましたが、「PRM をやっていこう」となったのが半年前からだったんでしょうか? 宍戸 : 元々 CLINICS はオンライン診療のシステムとして 2016 年からサービスを提供してきました。2018 年からは CLINICS カルテとして電子カルテシステムも提供を開始しましたが、そのタイミング以降はオンライン診療部分に関してはそこまで大きくアップデートはしてこなかったんです。 現在はクラウド診療支援システムとして販売していますが、これまでは電子カルテなど基幹部分の強化がメインになっていたからです。しかし、数年開発・運用してきた中で、本来医療機関の診察のメインである対面診療の予約については課題が積み残されていたり、昨今の状況から来る オンライン診療のニーズなどを始めとして、患者接点を強化していく必要がある ということになり、今回取り組むことになりました。 酒井 : PRM というと本当にここ最近の潮流なのですが、 CLINICS はもともと「患者とつながる」をコンセプト としており、今回の機能強化などもこのコンセプトに基づき実施しています。その上で、患者とのタッチポイントで抜けていた部分や強化したほうが良い部分をより作り込んでいったという形です。 例えば、CLINICS は元々オンライン診療のシステムとして始まっており、対面診療予約に関して使いづらさを感じるケースがあったため、対面診療予約をより行いやすくするよう改修を行うなどです。 CLINICS デザイナー 酒井さん 新居 : 元々あったものをさらに患者とのタッチポイントを強化するために、改修・改善していったんですね。 プロジェクトの背景 どのくらいの規模のプロジェクトだったのか 新居 : 半年のプロジェクトということですが、規模としてはどの位の大きさだったんでしょうか? 酒井 : 私が経験したプロジェクトの中でも関わる部署など考えると一番大きかったと思います。開発はもちろんですが、CLINICS チームの セールス・カスタマーサクセス・マーケティング、広報もなのでほぼ CLINICS の全部署と関わっていました 。 田中 : 他にも、サービスとしては患者統合基盤・ 患者向けアプリ・ Dentis が関係しています。 宍戸 : QA チームにもしっかり入ってもらって QA を行なったりしてもらったので、本当に 医療プラットフォームのほぼ全てのチームと関わって開発していった 感じです。最終的にプロダクトの見せ方や売り方なども含めて調整していったので事業部とも密接に関わって作り上げていきました。 新しい PRM 機能をどうやって、プロダクトに融合させたのか 新居 : そこまで大きい規模のプロジェクトだということで、PRM の各種機能をどうやって上手くプロダクトに落としこむのか、実際にはどのようにみなさん推進されていったんでしょう。 宍戸 : プロジェクトの推進という意味では、最初は来田さんや( Dentis の運営を行なっている)プロダクト戦略室の平山さんと スモールスタートで意見を出し合いながらモックを作っていき、まずはビジュアル面から機能のイメージなどを刷り合わせ ていくのに、酒井さんに手を動かしてもらいながら進めていました。 その過程でこのプロジェクトにおける重要な意思決定がありました。CLINICS ではログイン不要のシンプルな予約機能を以前より提供していました。この機能は患者アプリとは元々違うドメインで運営していたものでしたが、今回のタイミングでこの両者のアプリケーションを患者アプリと同じドメインに統合して運営していこうという決定をしました。 患者アプリは CLINICS チームとは別チームで運営しているという背景もあり、チーム間の連携が格段に増え 、考えなければいけない事柄も多くなります。プロジェクトとして関係者が多くなってきたので、 今までの考え方を 1 度リセットしてプロジェクトの進め方を新しく模索する必要がありました 。 この点がかなり自分の中では大変でした。来田さんも大変だったと思います。こうした全体像を描いたり、全体スケジュールを決めたりというところでは、田中さんにアドバイスをもらいながらプロジェクトを進めていきました。 プロジェクトを推進するために必要だったこと 難しい意思決定をどのようにしていた? 新居 : 途中から一気にプロジェクトがスケールアップしたということなんですね。今回のプロジェクトは色々な部署も関わるし、たくさんの機能や改修が入っています。開発側と事業部側との意識合わせが、かなり必要だったんではないかと思うのですが、その辺はいかがだったでしょうか。 来田 : 例えば、今まで CLINICS では対面診療予約の際に患者のクレジットカード登録を必須としていたのですが、今回の改修でクレジットカード登録を不要に変更できるように対応を行いました。この対応は、CLINICS オンライン診療の初期から念願の機能だったりします。自分は事業部長である田中大介さんと今年の年明けくらいから話をして、メンバーに「よし、このプロジェクトをやっていこう」という話を持っていくようにしていました。やっぱり、大変なプロジェクトだというのはあったのでスタートを切れる状態に徐々にしていった感じです。 プロジェクトが佳境に入ってからは自分は各部署との調整がメインの業務でした。開発側と事業側とか CLINICS と Dentis だとかそういった立場の違いから来る要求の違いを納得できる仕様に落とし込む作業です。やっぱり、それぞれの立場でベストを尽くそうとみんな考えているんですが、全部を取り入れることはできないので。私が調整するときには それぞれ反対の立場の人たちの考えをきちんと伝えるようにということをしながら、仕様を決めていく ことを念頭に置いていました。 具体的な調整の例としては、開発する機能とセールスする際のプランの刷り合わせだったりですね。「機能としてはこうしたほうが使いやすいよね」というだけだと、セールス側としては「どこをウリにして売っていくのかというのが見えてこない」という話になったんですね。これはどちらの言い分も一理あるものなんですが、こういうところを時間をかけて 双方納得行くように決めていきました 。 CLINICS ディレクター・医師 来田さん 酒井 : この例でやったことは、まず現在のプランと機能を全部書き出して、組み合わせを整理するということでした。これをベースにして話を刷り合わせていったんですが、文字ベースだと人間分かった気になるんですが、実はそこまでピンと来ない。ですので、 LP のモックを作ってこれを見た顧客が導入したくなるようなウリになっているのかをすぐに判断できる状態 にしました。 宍戸 : こうしてちゃんと機能を解いていって細かい部分も含めて、時には CLINICS のコンセプトに立ち返って事業部・開発双方で時間をかけて認識を合わせることができたのは、大変でしたが良かったですね。 リリースまでのプロセスで顧客の声を反映 新居 : やはりここまでのプロジェクトだと最初の段階でちゃんとリリースまでを見据えた意識合わせをチーム内で行なっていたんですね。開発が進行していって、リリースするまではどのようにプロジェクトは進んでいったんでしょう。 宍戸 : リリースをする直前も、単純に仕様や機能について文書で説明しても理解するのは難しいので、酒井さんにディレクションをしてもらいながら、開発環境を使って QA チームやカスタマーサポートチームと一緒に認識合わせやリリースに向けた準備を行なっていきました。 来田 : あとは関係が構築できている 顧客に実際機能を見てもらって反応を伺う ということもしていきました。色々な使い方をしている顧客 10 件くらいにモックをベースにヒアリングをかけていきました。価格や導入時期なども含めた話を聞いていって、そこをすぐにプロジェクトにフィードバックする体制を構築していました。 酒井 : 実際に顧客の声をこうして聞けるというのは本当にプロダクトにとってありがたいことでした。特にこういう使い勝手などに直接関わる部分では、こうしたフィードバックの有無で完成度が違ってきます。 機能の正式リリース前から機能評価に協力していただける医療機関がいるのは、カスタマーサポートの皆さんが顧客と良好な関係を築けているおかげ だと思っています。こういった点はメドレーの強みだと思います。 プロジェクトで大切にしたこと 新居 : 開発段階からユーザーの声を吸い上げていけるのは、本当に良いですね。プロダクトの改善スピードなんかも段違いに早くなりそうです。他に大切にしていた部分などありますか? 酒井 : 今回の機能開発においては、プロジェクトの責任者として、開発以外のメンバーともしっかりコミュニケーションし合意形成を図ることを意識してプロジェクトを推進しました。 先ほどの話にも出ましたが 1 つの機能を実装するとなっても、実際の医療現場での使い勝手や、メドレーでのサポート、売り方など色々な視点で考えないといけないプロジェクトでした。そのため、そういったところをカスタマーサクセスのメンバー含む 10 人ほどと毎週 1 つ 1 つ納得がいくように詰めていくという作業をしていったりしました。 来田さんのように現役の医師の意見、顧客の意見なども取り入れられますし、カスタマーサポートには医療事務経験者の方もいらっしゃるので、多方面の視点を総合的に取り入れられるコミュニケーション を心がけていました。 手段はテキストや Figma でのデザイン、実際の実装を見せるなど適切な手段を選んでコミュニケーションコストを低くするようにして進行していました。 実際の機能実装にあたってはエンジニアも自身で考えて細かいボールを拾ったりしながら、実装 していってもらいました。この辺りはこのプロジェクトだからというよりも、メドレーの開発のベースになっている部分なので良いですよね。 メンバーのイメージ合わせがプロジェクト成功の鍵 新居 : 先ほどもお話がありましたが、プロジェクト途中の意思決定で関わるプロダクトが多くなったのでスイッチングが大変だったということでしたが、実際にそのタイミングになったときに全体のシステム設計なんかも変わったりしたんでしょうか。 田中 : 私はプロジェクトの途中から入ったんですが、そのタイミングで、宍戸さんや来田さんがちょうどシステム設計を考え直していたり、プロジェクト計画の引き直しをしているところでした。そこでまずは全体像を把握したいと考えてドキュメントなどを見せてもらったんです。各機能の細かい要件は分かったのですが、このプロジェクト全体としての各システムの責務や依存関係などがいまいち分かりづらく、メンバー間の認識も若干ズレが発生しているように感じました。なのでまずは**メンバーの認識を合わせ、各チームが何をするべきかを明確化するために、各システムの責務など全体感の整理整頓をして「見える化」**することから始めました。 このプロジェクトに限らずですが、プロジェクトとして抽象度の高い段階でのドキュメントは、文章のみの記載だと実は関係者にしっかりと伝わっていなかったということが多々あります。各自が頭の中でイメージした図に差異が発生している状態です。自分はそういう際に良く言っているのが**「大枠で良いので、まずは整理結果を図にしてみんなのイメージを合わせる**」ということです。こうして認識を合わせることで各チームが進めやすくなり後戻りリスクも軽減されるので、この段階でしっかりとイメージをすりあわせる事がとても重要だと考えています。 医療プラットフォーム CTO 田中さん 新居 : たしかにテキストで仕様なんかを書きながら説明しても、実は認識に齟齬があった…という経験があります。その後はどのようなプロセスでプロジェクト推進したんですか? 田中 : その後は各プロダクトにて実装予定の機能で、プロダクト間で共通化できそうな機能を共通サービスとして切り出すかどうかをみんなと考えていきました。結果として大きな機能では「ビデオ通話」部分を共通化するという意思決定をしました。他にも「問診票の提供」機能が候補としてあがっていましたが、共通化せずに各プロダクトの責務としてそれぞれ実装するという結論となりました。同じような機能に見えても、医科と歯科という業務ドメイン毎のコンテキスト差異を十分に検討した上での判断でした。 その差異がプロダクト機能としての強みとなり、安易な共通化はプロダクト成長の足枷となるリスクも存在 するためです。この判断も先ほどの全体の依存関係などが整理されたからこそ、意思決定できるものでした。 酒井 : 機能実装時に実感しましたが、やはり医科と歯科では細かい部分の要件などが異なる部分が多かったので、この段階で共通化の判断ができたのは大変助かりました。 PRM プロジェクトの大きな成果 新居 : 最後になりますが、このプロジェクトが終わっての成果はどのように考えていますか? 宍戸 : 冒頭に話した CLINICS の課題に関してはある程度、解決ができたと思います。 医療機関の業務に沿った形でプロダクトが進化 できた点はよかったと思います。特に問診周りなどは発熱外来などニーズの多様化に即して使い勝手が良くなったと思っています。 酒井 : 他にもビデオ通話部分など「オンライン診療」を提供するプロダクトとして「対面診療」だけではなく、もっと有用なプロダクトにできたかと思っています。 来田 : まだリリースしたばかりですが、初月の販売実績も期待していたよりも良く、そうした意味でも マーケットフィットした機能を出せたなという手応え が実感としてもありますね。 田中 : 予約やオンライン診療もドメインを統一した結果、会員登録数が増えていますので、そうした点も成果と言えます。 酒井 : あとは直接の成果というわけではないのですが、今回のプロジェクトの副産物として CLINICS の機能を整理整頓していった結果、 改めて「一気通貫で医療機関の業務をサポートできる」というトータルでの機能提供と各機能のシームレスな連携は CLINICS の強み だ、というのが再確認できたのは大きいです。 新居 : これからのさらなる CLINICS の発展に期待できますね!本日は長い時間お話ありがとうございました! おわりに メドレーとしてもかなり大規模なプロジェクトだった今回の PRM プロジェクトでしたが、みなさんのお話を聞いて各部署・各プロダクトで綿密に連携しながらプロジェクトを成功させようとしている様子が感じられるインタビューでした。 特にプロジェクト進行の話は非常に参考になりました。皆さんのご参考になる部分があれば幸いです。 こうしたプロジェクトで自分の力を発揮したい!と思った方はぜひお気軽に下記からご応募ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp
はじめに みなさん、こんにちは。エンジニアの新居です。クラウド診療支援システム CLINICS が 8 月から大幅に アップデート して、より患者と医療機関が密接に繋がるようになりました。 今回のアップデートで中心になっている機能領域として PRM(Patient Relationship Management) というものがあります。 この PRM が一体どういったものなのか、プロジェクトはどのように進んでいったのかを関係者にインタビューしていきました。 メドレーで大規模なプロジェクト開発をどのように行なっているかの一端をお見せできればと考えています! インタビュイー紹介 宍戸さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の開発責任者。前職は株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2017 年メドレー入社。 酒井さん 医療プラットフォーム第一本部 プロダクト開発室第二グループに所属。クラウド診療支援システム CLINICS の UI/UX 全般を担当。本プロジェクトのリード。前職は受託開発会社で様々なサイトやシステムのデザインに従事。2019 年メドレー入社。 田中さん 医療プラットフォーム CTO。医療プラットフォームの開発統括をしながら、自身は患者統合基盤の開発を担当。前職までは SIer や IT コンサルタントを経て、株式会社サイバーエージェントでサーバサイドエンジニアとして従事。2016 年メドレー入社。 来田さん 前職まではクリニックで精神科医として従事。CLINCS オンライン診療の立ち上げ時から関わる。現在は CLINICS のディレクションを中心として事業部と開発の架け橋となっている。 PRM とはどういうものなのか? 新居 : では今回中心となった PRM とはいったい何なのかというところから解説していただいて良いですか? 一般的にはそこまで知られてない言葉なのではないかと思うのですが…。 宍戸 : 元々 CRM(Customer Relationship Management)というマーケティングなどの文脈の用語があります。これは「自社サービスや商品を使っている顧客」と「サービスや商品を提供している企業」との関係性を管理することを指しますが、それをもとに適切なアクションをおこなうことで最終的には利益向上を目指すものと考えられます。 PRM は CRM と同様に医療機関と患者さんの関係性を管理することで必要なサービスを提供する ためのもの、と考えています。 CLINICS で基幹システムである電子カルテなどではなく、 オンライン診療・予約・問診など患者接点となる機能をターゲットにしてそれらを強化・改善 していったのが、CLINICS の今回の PRM プロジェクトになります。 CLINICS 開発責任者 宍戸さん 新居 : 確かに CRM は最近良く聞く言葉でしたが、PRM は患者と医療サービスを使う医療機関との関係を深めるためのものなんですね。 PRM プロジェクトの始まり CLINICS に感じていた課題とは 新居 : このプロジェクトは半年ほどかけていると聞きましたが、「PRM をやっていこう」となったのが半年前からだったんでしょうか? 宍戸 : 元々 CLINICS はオンライン診療のシステムとして 2016 年からサービスを提供してきました。2018 年からは CLINICS カルテとして電子カルテシステムも提供を開始しましたが、そのタイミング以降はオンライン診療部分に関してはそこまで大きくアップデートはしてこなかったんです。 現在はクラウド診療支援システムとして販売していますが、これまでは電子カルテなど基幹部分の強化がメインになっていたからです。しかし、数年開発・運用してきた中で、本来医療機関の診察のメインである対面診療の予約については課題が積み残されていたり、昨今の状況から来る オンライン診療のニーズなどを始めとして、患者接点を強化していく必要がある ということになり、今回取り組むことになりました。 酒井 : PRM というと本当にここ最近の潮流なのですが、 CLINICS はもともと「患者とつながる」をコンセプト としており、今回の機能強化などもこのコンセプトに基づき実施しています。その上で、患者とのタッチポイントで抜けていた部分や強化したほうが良い部分をより作り込んでいったという形です。 例えば、CLINICS は元々オンライン診療のシステムとして始まっており、対面診療予約に関して使いづらさを感じるケースがあったため、対面診療予約をより行いやすくするよう改修を行うなどです。 CLINICS デザイナー 酒井さん 新居 : 元々あったものをさらに患者とのタッチポイントを強化するために、改修・改善していったんですね。 プロジェクトの背景 どのくらいの規模のプロジェクトだったのか 新居 : 半年のプロジェクトということですが、規模としてはどの位の大きさだったんでしょうか? 酒井 : 私が経験したプロジェクトの中でも関わる部署など考えると一番大きかったと思います。開発はもちろんですが、CLINICS チームの セールス・カスタマーサクセス・マーケティング、広報もなのでほぼ CLINICS の全部署と関わっていました 。 田中 : 他にも、サービスとしては患者統合基盤・ 患者向けアプリ・ Dentis が関係しています。 宍戸 : QA チームにもしっかり入ってもらって QA を行なったりしてもらったので、本当に 医療プラットフォームのほぼ全てのチームと関わって開発していった 感じです。最終的にプロダクトの見せ方や売り方なども含めて調整していったので事業部とも密接に関わって作り上げていきました。 新しい PRM 機能をどうやって、プロダクトに融合させたのか 新居 : そこまで大きい規模のプロジェクトだということで、PRM の各種機能をどうやって上手くプロダクトに落としこむのか、実際にはどのようにみなさん推進されていったんでしょう。 宍戸 : プロジェクトの推進という意味では、最初は来田さんや( Dentis の運営を行なっている)プロダクト戦略室の平山さんと スモールスタートで意見を出し合いながらモックを作っていき、まずはビジュアル面から機能のイメージなどを刷り合わせ ていくのに、酒井さんに手を動かしてもらいながら進めていました。 その過程でこのプロジェクトにおける重要な意思決定がありました。CLINICS ではログイン不要のシンプルな予約機能を以前より提供していました。この機能は患者アプリとは元々違うドメインで運営していたものでしたが、今回のタイミングでこの両者のアプリケーションを患者アプリと同じドメインに統合して運営していこうという決定をしました。 患者アプリは CLINICS チームとは別チームで運営しているという背景もあり、チーム間の連携が格段に増え 、考えなければいけない事柄も多くなります。プロジェクトとして関係者が多くなってきたので、 今までの考え方を 1 度リセットしてプロジェクトの進め方を新しく模索する必要がありました 。 この点がかなり自分の中では大変でした。来田さんも大変だったと思います。こうした全体像を描いたり、全体スケジュールを決めたりというところでは、田中さんにアドバイスをもらいながらプロジェクトを進めていきました。 プロジェクトを推進するために必要だったこと 難しい意思決定をどのようにしていた? 新居 : 途中から一気にプロジェクトがスケールアップしたということなんですね。今回のプロジェクトは色々な部署も関わるし、たくさんの機能や改修が入っています。開発側と事業部側との意識合わせが、かなり必要だったんではないかと思うのですが、その辺はいかがだったでしょうか。 来田 : 例えば、今まで CLINICS では対面診療予約の際に患者のクレジットカード登録を必須としていたのですが、今回の改修でクレジットカード登録を不要に変更できるように対応を行いました。この対応は、CLINICS オンライン診療の初期から念願の機能だったりします。自分は事業部長である田中大介さんと今年の年明けくらいから話をして、メンバーに「よし、このプロジェクトをやっていこう」という話を持っていくようにしていました。やっぱり、大変なプロジェクトだというのはあったのでスタートを切れる状態に徐々にしていった感じです。 プロジェクトが佳境に入ってからは自分は各部署との調整がメインの業務でした。開発側と事業側とか CLINICS と Dentis だとかそういった立場の違いから来る要求の違いを納得できる仕様に落とし込む作業です。やっぱり、それぞれの立場でベストを尽くそうとみんな考えているんですが、全部を取り入れることはできないので。私が調整するときには それぞれ反対の立場の人たちの考えをきちんと伝えるようにということをしながら、仕様を決めていく ことを念頭に置いていました。 具体的な調整の例としては、開発する機能とセールスする際のプランの刷り合わせだったりですね。「機能としてはこうしたほうが使いやすいよね」というだけだと、セールス側としては「どこをウリにして売っていくのかというのが見えてこない」という話になったんですね。これはどちらの言い分も一理あるものなんですが、こういうところを時間をかけて 双方納得行くように決めていきました 。 CLINICS ディレクター・医師 来田さん 酒井 : この例でやったことは、まず現在のプランと機能を全部書き出して、組み合わせを整理するということでした。これをベースにして話を刷り合わせていったんですが、文字ベースだと人間分かった気になるんですが、実はそこまでピンと来ない。ですので、 LP のモックを作ってこれを見た顧客が導入したくなるようなウリになっているのかをすぐに判断できる状態 にしました。 宍戸 : こうしてちゃんと機能を解いていって細かい部分も含めて、時には CLINICS のコンセプトに立ち返って事業部・開発双方で時間をかけて認識を合わせることができたのは、大変でしたが良かったですね。 リリースまでのプロセスで顧客の声を反映 新居 : やはりここまでのプロジェクトだと最初の段階でちゃんとリリースまでを見据えた意識合わせをチーム内で行なっていたんですね。開発が進行していって、リリースするまではどのようにプロジェクトは進んでいったんでしょう。 宍戸 : リリースをする直前も、単純に仕様や機能について文書で説明しても理解するのは難しいので、酒井さんにディレクションをしてもらいながら、開発環境を使って QA チームやカスタマーサポートチームと一緒に認識合わせやリリースに向けた準備を行なっていきました。 来田 : あとは関係が構築できている 顧客に実際機能を見てもらって反応を伺う ということもしていきました。色々な使い方をしている顧客 10 件くらいにモックをベースにヒアリングをかけていきました。価格や導入時期なども含めた話を聞いていって、そこをすぐにプロジェクトにフィードバックする体制を構築していました。 酒井 : 実際に顧客の声をこうして聞けるというのは本当にプロダクトにとってありがたいことでした。特にこういう使い勝手などに直接関わる部分では、こうしたフィードバックの有無で完成度が違ってきます。 機能の正式リリース前から機能評価に協力していただける医療機関がいるのは、カスタマーサポートの皆さんが顧客と良好な関係を築けているおかげ だと思っています。こういった点はメドレーの強みだと思います。 プロジェクトで大切にしたこと 新居 : 開発段階からユーザーの声を吸い上げていけるのは、本当に良いですね。プロダクトの改善スピードなんかも段違いに早くなりそうです。他に大切にしていた部分などありますか? 酒井 : 今回の機能開発においては、プロジェクトの責任者として、開発以外のメンバーともしっかりコミュニケーションし合意形成を図ることを意識してプロジェクトを推進しました。 先ほどの話にも出ましたが 1 つの機能を実装するとなっても、実際の医療現場での使い勝手や、メドレーでのサポート、売り方など色々な視点で考えないといけないプロジェクトでした。そのため、そういったところをカスタマーサクセスのメンバー含む 10 人ほどと毎週 1 つ 1 つ納得がいくように詰めていくという作業をしていったりしました。 来田さんのように現役の医師の意見、顧客の意見なども取り入れられますし、カスタマーサポートには医療事務経験者の方もいらっしゃるので、多方面の視点を総合的に取り入れられるコミュニケーション を心がけていました。 手段はテキストや Figma でのデザイン、実際の実装を見せるなど適切な手段を選んでコミュニケーションコストを低くするようにして進行していました。 実際の機能実装にあたってはエンジニアも自身で考えて細かいボールを拾ったりしながら、実装 していってもらいました。この辺りはこのプロジェクトだからというよりも、メドレーの開発のベースになっている部分なので良いですよね。 メンバーのイメージ合わせがプロジェクト成功の鍵 新居 : 先ほどもお話がありましたが、プロジェクト途中の意思決定で関わるプロダクトが多くなったのでスイッチングが大変だったということでしたが、実際にそのタイミングになったときに全体のシステム設計なんかも変わったりしたんでしょうか。 田中 : 私はプロジェクトの途中から入ったんですが、そのタイミングで、宍戸さんや来田さんがちょうどシステム設計を考え直していたり、プロジェクト計画の引き直しをしているところでした。そこでまずは全体像を把握したいと考えてドキュメントなどを見せてもらったんです。各機能の細かい要件は分かったのですが、このプロジェクト全体としての各システムの責務や依存関係などがいまいち分かりづらく、メンバー間の認識も若干ズレが発生しているように感じました。なのでまずは**メンバーの認識を合わせ、各チームが何をするべきかを明確化するために、各システムの責務など全体感の整理整頓をして「見える化」**することから始めました。 このプロジェクトに限らずですが、プロジェクトとして抽象度の高い段階でのドキュメントは、文章のみの記載だと実は関係者にしっかりと伝わっていなかったということが多々あります。各自が頭の中でイメージした図に差異が発生している状態です。自分はそういう際に良く言っているのが**「大枠で良いので、まずは整理結果を図にしてみんなのイメージを合わせる**」ということです。こうして認識を合わせることで各チームが進めやすくなり後戻りリスクも軽減されるので、この段階でしっかりとイメージをすりあわせる事がとても重要だと考えています。 医療プラットフォーム CTO 田中さん 新居 : たしかにテキストで仕様なんかを書きながら説明しても、実は認識に齟齬があった…という経験があります。その後はどのようなプロセスでプロジェクト推進したんですか? 田中 : その後は各プロダクトにて実装予定の機能で、プロダクト間で共通化できそうな機能を共通サービスとして切り出すかどうかをみんなと考えていきました。結果として大きな機能では「ビデオ通話」部分を共通化するという意思決定をしました。他にも「問診票の提供」機能が候補としてあがっていましたが、共通化せずに各プロダクトの責務としてそれぞれ実装するという結論となりました。同じような機能に見えても、医科と歯科という業務ドメイン毎のコンテキスト差異を十分に検討した上での判断でした。 その差異がプロダクト機能としての強みとなり、安易な共通化はプロダクト成長の足枷となるリスクも存在 するためです。この判断も先ほどの全体の依存関係などが整理されたからこそ、意思決定できるものでした。 酒井 : 機能実装時に実感しましたが、やはり医科と歯科では細かい部分の要件などが異なる部分が多かったので、この段階で共通化の判断ができたのは大変助かりました。 PRM プロジェクトの大きな成果 新居 : 最後になりますが、このプロジェクトが終わっての成果はどのように考えていますか? 宍戸 : 冒頭に話した CLINICS の課題に関してはある程度、解決ができたと思います。 医療機関の業務に沿った形でプロダクトが進化 できた点はよかったと思います。特に問診周りなどは発熱外来などニーズの多様化に即して使い勝手が良くなったと思っています。 酒井 : 他にもビデオ通話部分など「オンライン診療」を提供するプロダクトとして「対面診療」だけではなく、もっと有用なプロダクトにできたかと思っています。 来田 : まだリリースしたばかりですが、初月の販売実績も期待していたよりも良く、そうした意味でも マーケットフィットした機能を出せたなという手応え が実感としてもありますね。 田中 : 予約やオンライン診療もドメインを統一した結果、会員登録数が増えていますので、そうした点も成果と言えます。 酒井 : あとは直接の成果というわけではないのですが、今回のプロジェクトの副産物として CLINICS の機能を整理整頓していった結果、 改めて「一気通貫で医療機関の業務をサポートできる」というトータルでの機能提供と各機能のシームレスな連携は CLINICS の強み だ、というのが再確認できたのは大きいです。 新居 : これからのさらなる CLINICS の発展に期待できますね!本日は長い時間お話ありがとうございました! おわりに メドレーとしてもかなり大規模なプロジェクトだった今回の PRM プロジェクトでしたが、みなさんのお話を聞いて各部署・各プロダクトで綿密に連携しながらプロジェクトを成功させようとしている様子が感じられるインタビューでした。 特にプロジェクト進行の話は非常に参考になりました。皆さんのご参考になる部分があれば幸いです。 こうしたプロジェクトで自分の力を発揮したい!と思った方はぜひお気軽に下記からご応募ください。 募集の一覧 | 株式会社メドレー メドレーの採用情報はこちらからご確認ください。 www.medley.jp