Medley Developer Blog

株式会社メドレーのエンジニア・デザイナーによるブログです

2020年度新卒エンジニア研修について

こんにちは。ジョブメドレーの開発チームでエンジニアをしている新居です。

はじめに

2020年4月に、新卒エンジニア3名が入社しました。 入社後は新卒エンジニア研修を実施し、先日8月25日の最終報告会をもって終了しました。

コロナウイルスの影響で入社間もなくフルリモート勤務となり、不慣れなところもありましたが、本年度の研修の取り組みを紹介します。

f:id:medley_inc:20200924130505p:plain

2020年度新卒エンジニア

研修の概要

メドレーでは昨年度から新卒エンジニアを迎えており、合わせて研修も開始しました。

初めての研修をどのような視点で計画・実施したかについては、昨年平木がこちらにまとめています。

2019年度エンジニア新卒の研修について - Medley Developer Blog

今年は人事部のご協力もいただきながら、昨年の内容を少しアップデートして行いました。

研修の目的は、新卒メンバーが同じ空間で互いに刺激し合いながら、社会人への思考転換をはかり、業務遂行に必要となる基礎知識とスキルを習得することです。

研修は以下の4つのフェーズに区切って行いました。

f:id:medley_inc:20200924130533p:plain

研修のフェーズ

研修の内容

ここから4つのフェーズ毎に内容を紹介します。

フェーズ1:社会人&メドレー基礎研修

1. オリエンテーション

  • メドレーの事業や組織、大切にしている行動規範などの概要説明
  • セキュリティ研修とコンプライアンス研修

2. ビジネス研修

  • ビジネスマナー研修
  • ビジネススキル研修
  • ビジネススタンス研修(外部研修)

フェーズ1では「医療ヘルスケアの未来をつくる」メンバーの一員として大切にして欲しいことを学ぶフェーズでした。

社会人としての最低限のマナーやスタンスは勿論ですが、メドレーで働く上で土台となるマインドをここで学び、医療ヘルスケア分野の課題を解決する一員として共にプロダクトを作るためのベースを築けたと思います。

フェーズ2:エンジニア基礎研修

1. 開発基礎1

2. 開発実践

  • チーム開発体験

3. 開発基礎2

フェーズ2から開発の研修がスタートしました。

開発基礎1

開発の研修に入る前に、エンジニアの執行役員 田中から「メドレーが求めるエンジニアとは」というテーマで講義を行いました。メドレーがどういうエンジニアを求めているのか、目指すところの視点合わせを行い、これから行う研修に対する意識や取り組みの質を上げることを目指しました。

メドレーが求めるエンジニア像についてはCTO平山の記事にも詳しく書いています。

メドレー平山の中央突破: THE エンジニア

その後ジョブメドレーとCLINICSの事業・プロダクトについての概要説明、Railsの基礎トレーニングを行いました。メドレーのプロダクトはRails製です。Railsウェブアプリケーション開発に慣れていない新卒メンバーは、Railsの基礎トレーニングでRuby on Railsチュートリアルを使って基礎からみっちり学びます。

f:id:medley_inc:20200924130918p:plain

Ruby on Railsチュートリアルの進め方

ここで大切なことは、漠然とひたすら写経してチュートリアルを進めるのではなく、毎日のフィードバック会でしっかりその日の進捗や学びを共有し、不明点はメンターに質問してもらうようにすることです。

メンターは、新卒メンバーが理解が浅いまま進めてたり、理解していて欲しいところがいい加減になってたり、進め方や学びの方向性がズレてる場合などはアドバイスを入れて軌道修正することを心掛けました。

また「実際の開発ではこうだよ」といった実務を踏まえたアドバイスや、意識していることも伝えていきました。

20新卒Sさんの感想

「毎日のフィードバック会の中で、その日学んだ技術がジョブメドレーではどのように利用されているのか、どういったことに留意して利用しているのかなどを確認できたので、現場の人の感覚を少しずつ知ることができた。」

新卒メンバーは毎日リズム良く進捗を出し、メンターは新卒メンバーのフォローと引き上げを意識し、成果を最大化できるよう努めました。

開発実践

続いて開発実践ではチーム開発を行いました。前回までは各自個別に進めていましたが、ここではジョブメドレーに関する課題解決を目的としたプロジェクトに対して、チームで向き合う研修でした。

f:id:medley_inc:20200924131002p:plain

開発実践の目的と達成すべきこと

実務ではチーム開発が基本となり、チームメンバーとの協働は必須です。学生時代に業務レベルのチーム開発を経験しているのは稀ですし、実務に入る前にチームでプロジェクト推進するとはどういうことかを知るのは、とても価値があると思います。

加えて、チームで課題をどう解決していくのかも、新卒メンバー同士で話し合って決める必要があるので、課題解決力も養われたと思います。

今回はプロトタイプを作って成果発表するところまででしたが、プロジェクト推進においてはスケジューリング、要件定義、各種設計、開発フローやコミュニケーションフローの整備、実装、テスト、などなど、やることは尽きません。

また、このプロセスの中で密なコミュニケーションが必要不可欠となるので、新卒メンバー間のチームワークも向上し、お互いのことを更に知ることができたと思います。

この段階で、チームでプロジェクトを推進することの全体感を知り、プロジェクト推進の苦悩苦闘を実体験できたのは大きな経験値になったと思います。

20新卒Oさんの感想

「各機能の影響を互いに受けないためにブランチを細分化するブランチ戦略やGitHubを用いたコード管理について理解できた。一方で、UIを考える際にチームの各メンバーの認識のズレが生じたことや、序盤は各メンバーの進捗の詳細を把握できていなかったことから報・連・相の重要性を再認識した。」

20新卒Tさんの感想

「初めてのチーム開発であったことから、実装の序盤は、どのファイルなら編集しても他のメンバーの作業に影響がないのか、動作確認のための画面を作るファイルは自由に作成して良いのか、どのテーブルから関係する他のテーブルの情報を含めた情報を取得をするかなどに悩んでいた。今になって振り返るとチーム内で話せば解決することに対して、技術的にも心理的にも難しさを感じた。」

開発基礎2

フェーズ2最後の開発基礎2では、書籍『Webを支える技術 -HTTP、URI、HTML、そしてREST』の輪読会を行いました。もっと前のフェーズでの実施も検討しましたが、開発基礎1と開発実践を経た後の方が書籍の内容の納得感が高くなるだろうという判断で、この段階での実施となりました。

その後、中間報告会に向けたドキュメンテーション研修とプレゼンテーション研修を行いました。仕事を進めていく上では、背景や目的を正しくステークホルダーへ共有しながら進めていくことが必要で、伝えたいことを適切に文章として整理し、他者へ分かりやすく伝えていくことが求められます。中間報告会では、そういったことを意識しながらこれまでの研修でやってきたことや成果、学びをレポートにまとめ、プロダクト開発室の室長と副室長、メンター陣の前でプレゼンテーションして報告しました。

フェーズ3:事業部OJT

1.代表取締役医師 豊田の講義

  • 日本における医療制度の課題とそれに対するメドレーの位置付けについての講義

2.ジョブメドレー開発OJT

  • ジョブメドレーの実際の開発Issueに対応し、ジョブメドレーの開発プロセスを体験

フェーズ3の最初は豊田代表の講義を受けました。基礎的な研修を終え、ジョブメドレー開発OJTに入る前にメドレー社の社会的意義などを改めて代表から伝えていただきました。

続いて、ジョブメドレー開発OJTは以下の狙いを持って進めました。

f:id:medley_inc:20200924131055p:plain

ジョブメドレー開発OJTの狙い

実際に行ったことは、手始めに小さい不具合系Issueの対応に取り組んだ後、サーバーレスポンス改善のIssueに取り組むというものでした。

サーバーレスポンスの改善は、1人1画面を担当し、「プロファイルツールで分析 → 改善できそうな箇所を調査 → 改善方針の検討 → 提案 → 実装 → レビュー → リリース」というサイクルを回しました。

1つ目のIssue対応でも心掛けたことですが、言われたものを実装するのではなく、ある課題を解決するためにどうしたら良いかを自分で主体的に考えることを重視しました。実装力と同じくらい課題解決力も大切にしているためです。

とはいえ成果が何も出せないというのは、精神衛生上良くないので、日々の朝会と夕会にて進捗や状況をしっかり確認しながら適宜フォローやインプットを行い、ヒントになりそうな過去Issueのリンクを渡して参考にしてもらったり、メンターの適切なバックアップも必要です。

今回のOJTを通じて実務を知ることで、実際にエンジニアとして仕事をするイメージが沸き、同時に仕事をする上で足りないことも明確になります。自分の課題と向き合うことで、直近の自身の学習プランの軌道修正もできます。

課題は簡単なものではありませんでしたが、最終的に1人1つ以上のサーバーレスポンス改善PRをリリースすることができました。

Sさんの感想

「速度パフォーマンスの悪いコードに対する嗅覚、オブジェクトを生成しすぎていないか、無駄に通信を走らせていないかなどを学べた。」

Oさんの感想

「Issueを本質的に解決するメドレーの開発姿勢について学んだ。Issueを表面的に解決するのではなく、背景や目的、関連するコードなどの不明点を調査し、十分に理解・納得した上で修正することの意識付けができた。また、Issueに関連する一部分のコードを修正すれば良いという狭い視野を持たず、修正による影響範囲を考慮した上で全体の最適化を考えることが重要であると学んだ。」

フェーズ4:最終報告

1. 最終報告会

  • 最終報告会の準備と実施

最後のフェーズ4では、これまでの研修でやってきたことや成果、学びをレポートにまとめ、役員陣の前でプレゼンテーションして報告しました。

役員陣の前なので緊張は最高潮になりますが、自分達が研修を通じて何を得てどう成長したか、今後どういうエンジニアを目指していくのか、などを役員陣の前でアピールする貴重な機会となりました。

1人10分の枠内で役員陣にどういう情報をどういう表現でプレゼンテーションするかを考える機会にもなったので、情報整理力や表現力が培われる場にもなりました。

さいごに

2020年度の新卒エンジニア研修も無事終了することができました。改めて、新卒メンバーのみなさん、関係者のみなさん本当にお疲れ様でした。

振り返ると、メドレー社員としてのマインドや開発の基本的なことをしっかりインプットしつつ、新卒メンバーが主体的に課題解決に取り組む研修ができたように思います。

必要な技術スタックを1から10まで懇切丁寧に資料に落とし込み、講義形式で行う研修も身になることは多いですが、メドレーの研修のような課題解決を実体験する研修はより実務に繋がる実践的な研修だと思います。

少しハードな面もあるかもしれませんが、このような研修を乗り越え、医療ヘルスケア分野の課題解決に取り組みたい学生エンジニアのみなさん、少しでも興味を持っていただけると幸いです。

もちろん中途エンジニアの方もぜひお気軽にお話をしましょう。

www.medley.jp

Fargate上で動くコンテナアプリケーションにSessionManagerで接続する

自己紹介

株式会社メドレーのエンジニア阪本です。

9月に入っても暑い日が続く中、皆さんはいかがお過ごしでしょうか。

前回のブログでも書きましたが私は野球観戦(虎党)が趣味で、毎日の試合結果に一喜一憂しています。 このブログの執筆時点ではセ・リーグは首位巨人にマジックが点灯し、残試合50を切った状況でのゲーム差10。優勝を目指す阪神にとっては厳しい状況となりましたが、残り直接対決をすべて勝てば可能性は見えてくるはず。ここは諦めずに応援しようと思います。

次の記事を書く頃には何らかの決着が着いていると思いますので、その時には喜びのメッセージが書ける事を願っています。

はじめに

突然ですが、皆さんはFargateを使いこなしていますか? 今までECS(EC2)での運用がメインでしたが、ジョブメドレーのシステムでもFargateに触れる機会が増えてきました。 筆者自身は初めてのFargateでしたが、EC2の存在が無くなることに不思議な感覚を覚えつつも、管理するものが減って運用が楽になったと実感しています。

しかし、EC2が無くなった事によりサーバ内にターミナルで入る手段を失いました。 公式のガイドでは

質問2:コンテナの中に ssh や docker exec で入ることは出来ますか?

こちら現状サポートしておりませんが、コンテナワークロードでは「同一の環境でしっかりテストを行う」ことや「ログを外部に全て出す」ことがベストプラクティスとされていて、コンテナに入る必要性を出来る限り減らす方が将来的にも好ましいです。

と「事前にテストを十分に実施する」「ログは(S3やCloudWatchLogsなど)外部に出力する」 さえ出来ていれば入る必要が無いはずと記載されているものの・・・心配性な自分は不測の事態が発生した時の事を考えてサーバに入る手段を求めてしまいます。

そこで代替手段を模索していた所、SessionManagerを使えば可能との文献を見つけました。 すでにSessionManager自体はEC2に対して運用を行っていたため導入の敷居は低いと考え、この手段を採用することにしました。

Session Managerとは?

Session ManagerとはAWSのSystems Managerサービスの1機能で、AWS上で管理しているサーバ(マネージドインスタンス)に対してターミナルのアクセスを可能にするものです。

ターミナルへのアクセス手段は、よくある手段だとSSHがありますが この方法はSSHポートを外部に開放する必要があるため攻撃の対象になりやすく、あまり好ましくありません。

それに比べてSession ManagerはSSHポートの開放は不要でサーバから見てアウトバウンド方向のHTTPS通信の確保で済む為、外部からの攻撃も受けず安全にアクセス経路の確保が実現できます。 ※通信経路の確保以外にもIAMロールの設定などが必要となります。詳しくはこちらを参考

AWS上で管理しているサーバ」と先に記載しましたが、このサーバはEC2インスタンスに限らないのが本記事の重要なポイントになります。 オンプレミスなサーバもAWS上で管理されているのであれば、SessionManagerエージェントをインストールする事により管理対象に含めることが可能になるのです。

今回はEC2で動いていたSessionManagerエージェントをFargateで動くコンテナにインストールする事によりコンテナ自体をサーバと見立ててマネージドインスタンスとし、SessionManagerでアクセスする事を目指します。

f:id:medley_inc:20200918142724p:plain

対応

Systems Managerインスタンス枠をスタンダードからアドバンスドに変更する / AWSコンソールでの設定

オンプレミスなサーバにSessionManagerにてアクセスする場合、Systems Managerのオンプレミスインスタンスティアをスタンダードからアドバンスドへと変更する必要があります。

f:id:medley_inc:20200918142745p:plain

この変更作業はAWSコンソールから行う事になりますが、アドバンスドへの変更に当たって既存のマネージドインスタンスに影響することはありません。 ただし、この逆の場合、スタンダードに戻す際には考慮が必要となるのでこちらを参照ください。

SSM Agentのインストール / コンテナに対する設定

ここからは実際に動くコンテナに対する設定になります。 AWSのドキュメント手動でインストール SSM エージェント EC2で Linuxのインスタンスを参考に、OSに合った方法でSessionManagerエージェントのインストールを行います。

インストール作業には通信などの時間が必要となるので、事前にコンテナイメージにインストールする形としました。

コンテナをマネージドインスタンスとして登録する / コンテナに対する設定

コンテナをマネージドインスタンスとして登録するため、コンテナのスタートアップ(Entrypoint)にて下記スクリプトを実行します。

# 1. ハイブリッドアクティベーションの作成
SSM_ACTIVATE_INFO=`aws ssm create-activation --iam-role service-role/AmazonEC2RunCommandRoleForManagedInstances --registration-limit 1 --region ap-northeast-1 --default-instance-name medley-blog-fargate-container`

# 2. アウトプットからアクティベーションコード/IDの抽出
SSM_ACTIVATE_CODE=`echo $SSM_ACTIVATE_INFO | jq -r '.ActivationCode'`
SSM_ACTIVATE_ID=`echo $SSM_ACTIVATE_INFO | jq -r '.ActivationId'`

# 3. コンテナ自身をマネージドインスタンスへの登録
amazon-ssm-agent -register -code $SSM_ACTIVATE_CODE -id $SSM_ACTIVATE_ID -region "ap-northeast-1"

# 4. SessionManagerエージェントの起動
amazon-ssm-agent &

これらのコマンドについては各行ごとに説明します

1.ハイブリッドアクティベーションの作成

マネージドインスタンス登録に必要なアクティベーションコード/ID取得のため、ハイブリッドアクティベーションの登録を行います。 後々AWSコンソールにてインスタンスの識別ができるように、--default-instance-nameオプションにてmedley-blog-fargate-containerという名前を付与しています。

このコマンドのアウトプットにて下記のようなJSONが返される為、一旦変数に格納しています。

{ "ActivationId": "<<アクティベーションID>>", "ActivationCode": "<<アクティベーションコード>>" }

また--iam-roleオプションでサービスロールAmazonEC2RunCommandRoleForManagedInstancesを使っています。 このロールがまだ存在しない場合、AWSコンソールからハイブリッドアクティベーションからIAMロール→「必要な権限を備えたシステムのデフォルトコマンド実行ロールを作成」により作成してください。

f:id:medley_inc:20200918142823p:plain

2. アウトプットからアクティベーションコード/IDの抽出

前項のアウトプットからアクティベーションコード/IDを個々の変数に分離しています。

3. コンテナ自身をマネージドインスタンスへの登録

取得したアクティベーションコード/IDを利用し、コンテナ自身をマネージドインスタンスとして登録します。 環境変数AWS_DEFAULT_REGIONのようにAWSのCredentialにてデフォルトのRegion設定があった場合でも、このコマンドではオプションでRegion指定が必須となるのでご注意ください。

4. SessionManagerエージェントの起動

最後にSessionManagerエージェントを起動します。 これによりAWSとの通信が確立され、SessionManagerが利用できるようになります。

この状態でマネージドインスタンス画面にて目的のインスタンスが「オンライン」であれば設定が完了です。

f:id:medley_inc:20200918142901p:plain

接続確認

設定が完了したので、実際にSessionManagerにて接続してみます。 AWSコンソールのセッションマネージャからセッションの開始を選択し、名前が事前に登録したmedley-blog-fargate-containerであるものを選択します。 外見は他のEC2と変わらない表示ですが、この手段で登録したものはインスタンスIDがmiから始まるIDになります(通常のEC2はiから始まるID)。

f:id:medley_inc:20200918142910p:plain

後は普段通りにセッションを開始するとコンテナ内へのアクセスが開始されます。 これでEC2の無いFargate上のコンテナに対してもターミナルに入ることができました。

運用してみての感想

Fargate上のコンテナに直接入ることにより、今まで出来なかった topコマンドによるプロセス状態の確認 dfコマンドによるDisk容量の確認 などの環境調査が出来るようになりました。 利用頻度が多い訳ではありませんが、調査の選択肢を増やす事により有事の際の早期対応に繋げれられていると感じています。

注意点

ここからは運用に当たっての注意点について数点紹介します。

料金

EC2でのSessionManagerと違い、この方法で登録したサーバへのSessionManager通信は料金が発生します。 正確には 「SessionManagerエージェントが起動しAWSと通信が確立している状態」 での時間課金でSessionManagerでの接続有無は問いません。 よって、常時必要ではない場合はSessionManagerエージェントを止めて節約する方法を検討してください。

アクティベーションやマネージドインスタンスがリストに残る

コンテナ終了時でもAWSコンソールのハイブリッドアクティベーション一覧やマネージドインスタンス一覧に表示が残ってしまうようです。 一覧の上限の記載は見つからなかったものの、無限に増えるとも思えないので適時Lambdaを使って削除しています。

SessionManagerでの操作について

EC2インスタンスに対してのSessionManagerと同様ですが、セッション開始直後のユーザはssm-user固定になるようです。 特定ユーザでの操作が必要となる場合、suコマンドでのユーザ切り替えが必要になります。

さいごに

今回のようにメドレーでは新たな技術を取り入れつつ、サービスの安定を目的とした様々な施策を導入しています。 こういった働き方に興味を持たれた方、まずは下記リンクからお気軽にお話しませんか?

www.medley.jp

iOSDC 2020にシルバースポンサーとして協賛します

f:id:medley_inc:20200914173618p:plain

みなさん、こんにちは。エンジニア・デザイナー採用担当の平木です。

メドレーではiOS開発コミュニティに微力ながら還元したいと、2017年よりiOSDCに協賛をさせていただいておりますが、今年もシルバースポンサーとして協賛させていただくことになりました。

過去の参加レポート

developer.medley.jp

developer.medley.jp

iOSDC初のオンライン開催

今年のiOSDCは初のオンライン開催ということで、例年とはまた違った楽しみ方ができそうです。

公式スタッフブログに記載されていますが、今年はノベルティグッズが配送されるとのことで、既に届いている方もいらっしゃるようです。弊社のパンフレットも同封させていただいてます。

今年のタイムテーブルも大変興味深い内容が多く、今から視聴が楽しみですが(スポンサーチケットを頂いてはいますが、筆者も自腹でチケット購入しています)ニコニコ生放送での視聴ということで、例年とは違い、コメントなどで盛り上がりがあるのではないかと、大変期待しています。

Ask the SpeakerではDiscordを使用するということで、対面とはまた違ったコミュニケーションが取れそうです。参考URLや図などがすぐに出してもらえたりなど、さらに学びが深くなるのではないかと思っています。

最後に

今回は直接会場でみなさんとの交流はできませんが、弊社のエンジニアも参加予定ですので、一緒に楽しんでいきたいと思います。後日イベントレポートもこちらで公開したいと考えています。

また、メドレーではこうしたイベントにも興味をお持ちのiOSエンジニアを絶賛募集中です。お気軽に下記よりご連絡いただければ、カジュアルにお話をしたいです!

www.medley.jp

OpenAPI や Protocol Buffers のおかげで開発がかなり捗っている話

こんにちは、インキュベーション本部エンジニアの加藤です。 主に CLINICS アプリの開発を担当しています。

はじめに

CLINICS アプリの開発では OpenAPIgRPC を利用しています。 OpenAPI と gRPC の間には何の関係もないのですが、どちらも API の仕様をスキーマ言語で記述するという点では共通しています。 今回はこの API スキーマが開発にもたらすメリットについて紹介していこうと思います。

API ドキュメントとしてのスキーマ定義

既存のコードに機能を追加する際や修正を加える際に気にすることの多い部分は API の仕様ではないかと思います。 「リクエストやレスポンスはどのようなデータなのか」「この値は必須なのか、任意なのか」「データの型は数値なのか、文字列なのか」「フォーマットは決まっているのか」など、多くのことを把握する必要があります。

しかし、こういった API 仕様のドキュメントが整備されていないということも多いのではないでしょうか。 また、ドキュメントはあるけれどリクエストとレスポンスのサンプル JSON が貼ってあるだけだったり、時間が経つうちに実装とドキュメントが乖離してしまいアテにならなくなってしまっている場合もあります。 参考にできるドキュメントがない場合は、直接バックエンドの実装を見に行くこともあるのですが、目的のコードを探し出して読み解くのは意外と時間がかかります。 特に自分以外のエンジニアが実装した機能の場合、API の仕様を実装から紐解くのはかなり骨が折れる作業です。

CLINICS アプリの開発ではこういった手間を減らすために OpenAPI や gRPC を利用しています。 REST APIスキーマは OpenAPI を使って記述しています。 gRPC を利用している部分は Protocol Buffers で RPC のインターフェースが記述されるため、これが APIスキーマになっています。 OpenAPI や Protocol Buffers の定義を参照することで API の仕様が容易に把握できるため、非常に便利です。

OpenAPI や Protocol Buffers などのスキーマ言語の良い点は、宣言的な DSLスキーマを定義できる点です。 実装言語に依存しない形での記述ができ、自然言語のような曖昧さもないためより正確に API 仕様を記述することができます。

ここからは OpenAPI や Protocol Buffers の実際に記述例を挙げながら、どういった形でスキーマを記述していくのか簡単に見ていきたいと思います。

OpenAPI

OpenAPI は REST API のインターフェースを記述する仕様です。 エンドポイントのリクエストパラメータやレスポンスの構造などを JSONYAML で記述していくことができます。 かつては Swagger という名称だったため、そちらの名前で知っているという方もいらっしゃるかもしれません。

実際の OpenAPI の定義は以下のような形になります。

openapi: 3.0.2
info: { title: OpenAPI Sample, version: 1.0.0 }
paths:
  '/users/{user_id}':
    get:
      parameters:
        - name: user_id
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:   { type: string }
                  name: { type: string }
                  age:  { type: integer, nullable: true }
                required: [id, name, age]
        '404':
          description: Not Found
        '500':
          description: Internal Server Error

例としてユーザー ID からユーザーのデータを取得する API を記述してみました。 上記の定義を見ると、

  • GET: /users/{user_id} というエンドポイントがある
  • ユーザーの ID (user_id) を string でパラメータとしてパスの中に埋め込む
  • レスポンスは 200, 404, 500 が返ってくる可能性がある

といったことが読み取れるかと思います。 また、レスポンスは以下のような JSON が返ってくることもスキーマから読み取ることができます。

{
  "id": "12345678-1234-1234-1234-123456789abc",
  "name": "user name",
  "age": 25
}

ここでは OpenAPI の記述の仕方についてあまり深く触れませんが、この他にもデータのフォーマットや省略された場合のデフォルト値などの記述なども可能です。 より詳しくはこちらを参考にしてみてください。 これらをうまく使うことでより正確に API の仕様を記述することができます。

Protocol Buffers

Protocol Buffers は主に gRPC で用いられるデータのシリアライゼーション形式です。 専用のスキーマ言語を用いてデータの構造や RPC のメソッドの定義を .proto ファイルに記述していきます。

実際の .proto ファイルは以下のような形になります。

syntax = "proto3";
package example.protobuf;

service UserService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
}

message GetUserRequest {
  string id = 1;
}

message GetUserResponse {
  User user = 1;
}

message User {
  string id = 1;
  string name = 2;
  int32 age = 3;
}

少し独特な見た目をしていますが、message として構造体のようなデータ構造が定義されていることが見て取れると思います。 各メッセージにはフィールドが定義されており、型とフィールド名が宣言されています。

スキーマ言語を使うメリット

スキーマはドキュメントの代わりとして使えるだけでなく、他にもいくつか使い道があるので紹介していこうと思います。

スキーマからソースコードを自動生成できる

スキーマ言語による記述は人間にとって読みやすいだけでなく機械的に処理できる形式でもあるので、スキーマからソースコードを自動生成することも可能です。 プログラミング言語や使っているフレームワークによらず、サーバー側ではリクエストのバリデーションを、またクライアント側ではレスポンスの JSON をクラスや構造体に詰めるような処理を書くことが多いのではないでしょうか。 事前にスキーマ定義を書いている場合はスキーマを見ながらこういったコードを書き起こしていくことになるのですが、せっかく machine-readable な形のスキーマがあるのだから自動でコードを生成したくなるのが自然な考えでしょう。 自動で生成してしまえば定型のコードを書く手間を減らせるだけでなく、手書きするとどうしても起こしがちな JSON のキー名を間違えるといったミスを防ぐこともできます。 また、Web, iOS, Android などの複数プラットフォームでサービスを展開している場合でも、それぞれの実装が乖離しないように管理していくことも容易です。

gRPC の場合は Protocol Buffers からソースコードを生成するのがほぼ前提となっています。 protoc コマンドを利用すると .proto ファイルから各言語のサーバー・クライアントコードを生成することができます。

OpenAPI の場合、openapi-generatorswagger-codegen などのツールを利用することで各種言語のコードを自動生成が可能です。 openapi-generator や swagger-codegen には多くの言語とフレームワークのサーバースタブと API クライアントのジェネレータが用意されています。

既存のジェネレータを使うだけでも十分に強力なのですが、さらに自前でコードジェネレータを書けばプロジェクト固有のルールでコードを生成するといったことも可能です。 OpenAPI のドキュメント自体はただの YAML/JSON なので、パースしてスキーマ定義を再帰的に読んでいけばコード生成に必要な情報を集めることができます。 Protocol Buffers からコードを生成する場合は protoc のプラグインを書くのが簡単でオススメです。

モックサーバーを使ってクライアント開発を進められる

コードの自動生成の他にもスキーマからモックサーバを立ち上げるといった活用方法もあります。 Prism のようなスキーマ定義から固定のデータを返すモックサーバを立ち上げるツールを利用すれば、クライアント側の開発もしやすくなります。 特にフロントエンドとバックエンドで担当が分かれているような場合は、開発初期はモックサーバーを使って開発を始め、バックエンドの実装ができてきた頃に結合するという流れを踏むことで、フロントエンドもより早いタイミングから開発に着手することができるようになります。

実際に Prism を使ってモックサーバーを立ち上げるとこんな感じになります。

f:id:medley_inc:20200821181330p:plain

f:id:medley_inc:20200821181342p:plain

スキーマファースト開発

このような開発スタイルはスキーマファースト開発・スキーマ駆動開発などと呼ばれています。 明確な定義や出典があるわけではないのですが、スキーマファーストな開発は以下のような開発スタイル全般を指しています。

  • スキーマは実装言語によらない machine-readable な形で記述する
  • ドキュメントをスキーマから自動生成する
  • クライアント/サーバーのコードをスキーマから自動生成する

実際にスキーマファーストな開発を実践するためにはスキーマをメンテナンスするモチベーションを高く保つ必要があります。 コードも書いた上でスキーマも書かないいけないとなるとおそらくスキーマが更新されなくなっていくので、可能な限りスキーマからコードを生成する努力が必要になります。 一方、うまく実践すればスキーマという形で 100% 信頼できる API ドキュメントが手に入り、クライアント/サーバー間での齟齬も減らせるので、実践してみる価値はあるのではないかと思います。

まとめ

今回は CLINICS アプリの開発でのスキーマ言語の活用例について紹介しました。 実際に開発の中でスキーマ言語を使うことで API 仕様について把握する労力が減り、エンジニア間のコミュニケーションも取りやすくなったと感じています。 また、一部のクライアントコードは OpenAPI や Protocol Buffers から自動生成しているのですが、かなり手間が省けると同時に人間の手で書くと起こりがちなミスも防ぐことができています。 OpenAPI であれば既存の REST API の仕様を書き起こすところから使い始められるので、興味のある方はぜひ一度使ってみてはいかがでしょうか。

最後に

インキュベーション本部では「医療ヘルスケアの未来」をつくる新規事業の立ち上げに挑戦しています。そんな私たちと一緒に働いてみたいと思った方は、ぜひ以下の採用情報もチェックしてみてください。

www.medley.jp

CLINICS開発チームで横軸の取り組みをした話

こんにちは、19新卒の桶谷です。現在はCLINICSで検査周りの開発を担当しています。

最近は、私自身が基礎疾患持ちということもあり、社会情勢を考慮してフルリモートで業務に勤しんでいます。オンラインコミュニケーションは対面と比べ、意識しなければならない点が多く、慣れが必要であると日々感じています。少しでも対面に近い環境を実現するために、一眼レフカメラWebカメラとして代用したり、ダイナミックマイクを使用してクリアな音声を届けられるように作業環境の改善に取り組んでいます。

今回はCLINICS開発チームで課題となっていた「オンボーディングやキャッチアップに時間がかかる問題」を解決するために行った、横軸の取り組みについて紹介します。

はじめに

CLINICS開発チームではクラウド診療支援システム「CLINICS」の機能として主に電子カルテ、オンライン診療、予約システムなどの機能の開発を行っています。医療システムの開発において医療業務知識は必要不可欠であり、例えば「会計」や「検査」といった医療機関で行われる業務に関する理解が要求されます。そのため、各々が日々キャッチアップに勤しんでいる一方、ひとつひとつの業務が複雑であるために、複数の機能を横断したプロダクト理解、全体把握が難しい課題がありました。そこで、この問題を解決するべく「横軸勉強会」と題して、CLINICSのドメイン知識共有会を行うことにしました。

f:id:medley_inc:20200812191102p:plain

ドメイン知識共有会の概要

ドメイン知識共有会は各々が携わった中で蓄積してきた業務知識や開発の背景を共有する会です。本会は毎週1時間トピックに沿ってオンライン(Google Meet)で実施します。あらかじめトピック、発表者、ファシリテーターを指定し、発表者には簡単な資料を用意してもらいます。加えて参加者には以下のことを意識してもらいました。

  • 発表者は対話型の発表を意識しフラットな雰囲気をつくる
  • 参加者はチャットを用いて思ったことをつぶやく

各トピックは医療システムを開発する上で必要なドメイン知識を切り口として、技術寄りの内容となっています。発表者によっては勉強会後の宿題が用意されたり、おすすめの本紹介があったりと多種多様です。発表形式をあえて定めないことで、各回ごとに発表者の個性が出るというのも面白みの一つだと思います。特に宿題があった回では、後日にフィードバック会が設けられ知識の定着に繋がりました。

実際に行ったトピック f:id:medley_inc:20200812184411p:plain

実際の様子 f:id:medley_inc:20200812201630p:plain

ドメイン知識共有会を終えて

今回ドメイン知識共有会を通して、機能の実装背景や概要を幅広く知ることができました。「会計業務の複雑さ」や「受付から診察までの動線設計」などのこれまでは共有する機会がなかった個々が持っている知識・知見を学ぶことができ、チーム全体でドメイン知識に関する認識合わせをする良い機会となりました。

最後に勉強会の振り返りを行ったところ、以下のような意見がありました。

  • ドメイン知識共有会を通してチーム力が上がってきている
  • 勘違いしていた知識を訂正できた
  • 相互理解が深まりチーム全体としてコミュニケーションのハードルを下げることができた
  • 各専門領域に関するドキュメントを一箇所にまとめることができた

ポジティブな意見が多く、取り組みの結果としてチームの基礎力向上にも繋がったと感じています。加えて、ドメイン知識共有会用に作成した資料はドキュメントとして残るため、オンボーディング資料などに活用ができそうです。しかしながら、以下のような問題もあります。

  • 発表者の準備コストが高い
  • 概要説明で終わってしまい即日業務で使用できる内容ではない
  • 学んだことを深堀りできていないため知識の定着に繋がらない

上記については、例えば発表者を2人ペアにして準備コストを分散させたり、宿題を出して次回にフィードバックする、開発チーム以外も気軽に参加ができるようにライトなトピックを追加するなど、今後のドメイン知識共有会のなかで改善していきたいと思います。

さいごに

今回はCLINICS開発チームで行った横軸の取り組みについて紹介させていただきました。

横軸でお互いをフォローしあうような「情報共有の場」を作ることで、より良い組織になっていくと感じています。今後、さらにプロダクトの機能が増えるとともに事業部全体として、関わるメンバーが増えるため、横軸での知識共有の仕組みは事業部を含めて必要であると考えています。開発チームに限らずに、プロダクトに関わるメンバーを巻き込み、組織コミュニケーションの改善を続けていきたいです。

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

www.medley.jp

Terraform のテスト環境を Terraform Workspaces で構築した

株式会社メドレーのエンジニアの笹塚です。 主にジョブメドレーのインフラを担当しています。

  • 直近では、コンテナ化されていなかった環境の移行などをしていました。
  • 休日は主にゲームをやっています。今は、日本語版がリリースされたばかりの「レムナント:フロム・ジ・アッシュ」に夢中です。

今回は Terraform のテスト環境を、Terraform Workspaces を使用して構築した事例を紹介します。

背景

ジョブメドレーにはいくつかのサービスがあり、Terraform によるコード化が進んでいるサービスと、Ansible、 itamae などで部分的にコード化はされているものの、Terraformの使用が遅れているサービスが混在しており、これらのサービスについても Terraform への移行を進めています。

Terraform への移行とあわせて、メンテナンスを担当するメンバーを増やす必要があるのですが

【作業担当】

  • Terraform のコードから、実際に稼働させる環境を作るのは慣れていても難しい。

【レビュワー】

  • Terraform のコードの差分だけで、内容をすぐに把握するのは難しい。

などの理由から、作業担当、レビュワーともにハードルが低いとは言えず、Terraform によるインフラの変更内容を事前に確認できる環境構築が必要だと感じるようになりました。

検討内容

事前に確認できる環境を作るにあたり、まず最初に検討したのは、各ステージのコードを共通化することでした。 ジョブメドレーの関連サービスでは、大きくわけて3つのステージで構成しています。

Sandbox(個人の検証用) → QA(リリース前の検証用) → Production

この各ステージのコードを共通化できれば、Production には QA 環境までで確認済みのコードを apply することができます。

ですが、Sandbox 環境、QA 環境、Production 環境はそれぞれ似てはいるものの、全てが同じ構成ではありません。

f:id:medley_inc:20200731151334p:plain

Terraform の HCL では

などの制限があり、構造の差分をコードで吸収しにくく、共通化できたとしてもメンテナンス性を下げる可能性が高いです。 また、すでに Terraform でコード化されている状態からの移行作業も楽ではないでしょう。

そこで、ステージごとのコードを共通化するのではなく、AWS Organizations で別アカウントを作り、各ステージのコードを試せる環境を作ることにしました。

f:id:medley_inc:20200731151544p:plain

<目標とする>
Terraform のコードをプロダクションアカウントで実行する前に、テストアカウントで実行し、設定した内容を AWS マネジメントコンソール や AWS CLIで確認することできる。

  • ストアカウント上で設定を確認した後、プロダクションアカウントに同じコードを apply することができる。
  • コストを抑えるため、テストアカウント上のリソースは、確認が終了したら destory することができる。

<目標としない>

  • ストアカウント上でサービスが稼働することは目標としない。
  • 必要なデータセットの作成など用意するものが増えるので、目標には含めませんでした。

設定作業

必要な作業は大きくわけて2つです。

  1. Terraform Workspaces の設定
  2. アカウントごとに必要なコードの追加、修正

Terraform Workspaces の設定

Terraform と AWS provider の設定を変更することで、workspace を切り替えることができます。

以下が作業イメージです。

workspace の追加

$ terraform workspace new production
$ terraform workspace list
  default
* production

Terraform の環境変更

terraform {
 required_version = "= 0.12.28"
 backend "s3" {
   bucket               = "example-state"
   workspace_key_prefix = "workspace"
   dynamodb_table       = "terraform-state-lock"
   key                   = "example.tfstate"
   region               = "ap-northeast-1"
   profile              = "production"
 }
}

provider "aws" {
 region  = "ap-northeast-1"
 version = "= 2.70.0"
 profile = var.workspace_profile[terraform.workspace]
}

variable "workspace_profile" {
 type = map(string)
 default = {
   default    = "test"
   production = "production"
 }
}

この例では、default workspace はテスト環境、Production を実際にサービスが稼働する環境のアカウントになるように設定しています。 それぞれ default は、aws config の test profile、Production は production profile を参照しています。

s3://example-state/example.tfstate がテスト環境、s3://example-state/workspace/production/example.tfstate が、プロダクション環境の state ファイルになります。

Terraform のコード内からは、現在の workspace 名を terraform.workspace で参照することができます。

ここまでの設定で

$ terraform workspace select [workspace名]

で workspace を切り替えることができるようになりました。

あとは、アカウントごとに必要な変更をしていきます。

アカウントをまたいだ共通のリソースの定義

全アカウントでユニークにする必要があるリソース(S3 bucketDNS など)の場合は、名称の分岐処理が必要です。 テストアカウントでは、リソース名に prefix をつけて作成するようにしました。

# 定義
 variable "example_bucket_name" {
 type = map(string)
 default = {
   default    = "test-example-bucket"
   production = "example-bucket"
 }
}

# リソースでの参照時
resource "aws_s3_bucket" "example" {
 bucket = var.example_bucket_name[terraform.workspace]
 ..
}

ストアカウントで起動するインスタンスタイプや台数の変更

ストアカウントでは EC2 のインスタンスを起動させなくても良い場合には、台数を変更するようにしました。

resource "aws_autoscaling_group" "example_web" {
  name             = "example-web"
  max_size         = 12
  min_size         = 6 
  desired_capacity = terraform.workspace == "production" ? 6  : 0}

インスタンスの起動が必要な場合も、同様の分岐でインスタンスタイプの変更を行っています。

コード化をしないリソースの扱い

プロダクションアカウントで一旦コード化を保留したリソースについては data source で参照しますが、テストアカウントにはリソースが存在しないため作成しなければいけません。 テストアカウントのリソースは確認が終了した段階で destroy したいので

  • ステージ用の Terraform コードとは別に、必要なリソースを作成するコードを用意する
  • 必要なリソース用のコード → ステージ用のコードの順に apply する

ようにしました。 data source で参照できれば良い範囲でのコード化なので、この追加のリソースも最小限のコストになるようにしています。

以上の設定で、同じ Terraform のコードを workspace を切り替えて plan、apply ができるようになりました。

運用してみて

プロダクションアカウントに apply する前に、テストアカウントで事前に apply することができるようになったので、作業中の試行錯誤もしやすくなりました。

レビュワーも、テストアカウント上で実際に apply された結果を確認することができるようになり、apply の差分だけではわかりにくかった変更内容を確認できるようになりました。

これなら新しく担当するメンバーの不安を、少しかもしれませんが解消できそうです。

まとめ

今回は Terraform のテスト環境を、Terraform Workspaces を使用して構築した事例を紹介させていただきました。 テストアカウント上のリソースの自動 destroy や、 plan、apply の自動化については触れられていませんが、また別の機会に紹介できればと思います。

長らく運用しているサービスでは、サービスを稼働させたまま解決しなければいけない課題が数多くあります。 それらの課題を、一つずつ着実に解決していくことに楽しさを見いだせる方、ぜひメドレーで一緒に働きましょう!

www.medley.jp

PWA, PRPL Pattern の概要と採用状況の調査

こんにちは。メドレーにてジョブメドレー開発エンジニアをしています、矢野と申します。

  • ジョブメドレーでは、主にバックエンド ( Ruby on Rails ) の改修を担当してます
  • 直近では 「サイトパフォーマンス改善施策」 として、Rails コードのリファクタリングによる TTFB 高速化に取り組んでました
  • 「もう絶対にコケないのが分かってる」ビルドやテストを、手元のコンソールで何度も叩いて「わー。ちゃんと通る!」っていう時間が好きです

今回は、上記の「サイトパフォーマンス改善施策」の文脈で調査した、PWA の実装パターンである PRPL Pattern という リソース提供の設計アーキテクチャ について紹介します。

PRPL Pattern とは

f:id:medley_inc:20200716154256p:plain

ref. Apply instant loading with the PRPL pattern - web.dev

PRPL Pattern は、Google I/O 2016 で提案された PWA - Progressive Web Application の構築・配信のための設計アーキテクチャです。

Web サイト・アプリケーションが、回線強度やスペックが高くないスマートフォンなどのデバイスでもストレスなく機能するよう、リソース配信とアプリ起動時のパフォーマンス ( = 高速化 ) に重点を置いています。

PRPL meanings

では具体的に「どうやって速くするの?」ということで、PRPL が提唱している 4 つのサイトレンダリング手法について見ていきます。

  • Push: <link preload> および HTTP/2 を使用して、初期 URL ルートの重要なリソースを Server Push する
  • Render: クライアントが初期ルートをなるべく早くレンダリングする
  • Pre-cache: 残りのルートをクライアントが Service Worker でプリキャッシュする
  • Lazy load: クライアントはオンデマンドで残りのルートを遅延読込みして作成する

PRPL は上記 4 つの頭文字をとったものですね。 PRPL は HTTP/2 の Server Push や、PWA の Service Worker など、Web プラットフォームの最新技術を駆使してサイトパフォーマンスをあげよう!というプラクティスです。

PWApps とは、最新の Web 技術を有効に活用し、漸進的 ( Progressive ) に高度なユーザー体験を提供しようとする概念です。この PWApps の概念を具体化する一つの手法として、「 PRPL 」 ( パープル ) と名付けられた開発・提供パターンが提案されました。

(中略)

Web Components や、 Service Worker、 HTTP/2 Server Push といった Web の最新技術をフルに活用し、レスポンス性の高いユーザー体験を提供しようというものです。

ref. Googleが新たに提唱する Progressive Web Apps の新たな開発パターン「 PRPL 」とは?

周辺知識 - HTTP/2

まずは、周辺知識からおさらいしていきます。PRPL は HTTP/2 の Server Push を利用する、という話でした。そもそも HTTP/2 とはどんなものでしょうか。

  • Hyper Text Transfer Protocol と呼ばれる TCP 上の通信プロトコルの次世代バージョン
  • 普段私たちが Web サイトを閲覧する際に利用しているプロトコル
  • HTTP/1.1 が 1997 年に策定され、2015 年にようやく /2 が標準化
  • Express, Apache, Nginx など各 Web サーバ、各ブラウザも対応してきている

ref. HTTP の進化

現行の一般的なバージョンは HTTP/1.1

HTTP は普段私たちが Web サイトを閲覧する際に利用する通信プロトコルです。HTTP/2 はその次世代バージョンになります。HTTP/1.1 には以下のような特徴があります。

  • ステートレスな通信
  • テキストベースで情報をやりとりする
  • 原則 1 リクエストに対して 1 レスポンスである
  • → 複数リソースを得るために何度もリクエストしてコネクションを貼り直す必要があり パフォーマンス上の課題がある

これに対して、1.1 の次期バージョンである HTTP/2 は以下のような特徴があります。

  • 通信時にヘッダを圧縮し使い回す省エネ設計 = 一部ステートがある
  • バイナリベースで情報をやりとりする
  • ストリームという概念で、1 コネクション中で Request / Response を多重化できる
  • 1 コネクションの中で複数リソースを並行して Request / Response できる!
  • リソースの Server Push が可能

HTTP/2 自体が HTTP/1.1 の課題であった通信のオーバヘッドを改善する規格であることがわかりますね。また「 request / response の多重化」により、1.1 と比較してどの程度「速く」なるのかについては、以下 Akamai 社のブログサイトが参考になります。

HTTP/1.1

f:id:medley_inc:20200716154352p:plain

HTTP/2 ( request / response の多重化 )

f:id:medley_inc:20200716154439p:plain

ref. HTTP/2を活用するパフォーマンス最適化 ADAPTIVE ACCELERATION

HTTP/2 の採用事例

f:id:medley_inc:20200716154502p:plain

ref. caniuse.com

上記対応状況からも分かる通り、モダンブラウザでは一通り対応しています。実際のプロダクションでも 日本ではメルカリさん、世界的なサービスだとTwitter, Facebook, Instagram などSNS サービスや、Slack, Dropboxなどが HTTP/2 に対応しているようです。

ジョブメドレーはまだ HTTP/1.1 でのサービス提供しか行っていませんが、ゆくゆくはバージョンアップ対応を行っていきたいと思っています。

周辺知識 - PWA

次に、PWA についておさらいします。PRPL は PWA の Service Worker を利用した実装パターンという話でしたが、そもそも PWA や Service Worker とはどんなものなのでしょうか。

PWA - Progressive Web Application

  • Web プラットフォームの新機能を使って「ネイティブアプリとウェブアプリのいいとこ取りした、UX の高いウェブアプリ」という概念
  • ウェブアプリの特性 ( Secure, Linkable, Indexable … ) を保ちつつ、ネイティブアプリの多機能さ ( インストール、プッシュ通知、オフライン動作 … ) を最新のブラウザ機能 = JavaScript API で実現する
  • 必ずしも SPA であったり、最新機能の全てを使っている必要はなく、「斬新的に Web 新 API でネイティブな機能を取り入れていける」というコンセプト

ref. プログレッシブウェブアプリの紹介 - MDN

PWA を構成する新機能たち

  • HTTPS: HyperText Transfer Protocol Secure 、 SSL / TLS による通信の暗号化
  • Service Worker: Web ページで動作するスクリプトから独立したイベント駆動型の worker
  • Cache API: Request / Response オブジェクトのストレージキャッシュ
  • Push API / Notifications API: サーバーからアプリへの通知送信
  • マニフェスト: アプリストアを通さず Web アプリをホーム画面にインストール可能

ref. プログレッシブウェブアプリ - MDN

上記以外にも、PWA には様々な API ・機能が存在します。その中でも PWA の、そして PRPL アーキテクチャの中核を成す重要な機能が Service Worker です。

Service Worker とは

  • ブラウザのバックグラウンドプロセスとして動作する Worker
  • Web ページで動作するスクリプトとは独立して動作する
  • サーバサイドでいうところの Worker プロセスと同じような使い方ができる

ref. Service Worker の紹介

Web ページの JavaScript プロセスとは切り離された文脈で、予め登録しておいた処理を、様々なイベントに応じて発火させることができるイメージですね。

プッシュ通知など、いわゆる「ネイティブアプリのような機能」は、この Service Worker を利用することで実現しています。

PWA の採用状況

Google からの提唱当初 ( PWA も Google のプロジェクトです ) こそ、先進的すぎてなかなか受け入れられなかった PWA ですが、2020 年現在は各ブラウザの対応状況も少しずつ向上されています。

日本では SUUMO、日経電子版、一休.com、世界的なサービスだと Instagram などが PWA による Web サイトを提供しているようです。

特に、既にネイティブアプリで大成功している Instagram が、回線・端末スペックの低い新興国をターゲットとした PWA をリリースしているという点はプロダクト観点からもとても興味深いですね。

PRPL パターンの利点

さて、話を戻して PRPL パターンが HTTP/2 や Service Worker を使って、具体的にどのようにサイトパフォーマンスを向上するのか?という点を見ていきます。

Server Push + Service Worker による Pre-cache

PRPL の 4 要素を、改めてもう少しわかりやすく記載してみると以下のようになります。

  • Push
    • 初回コネクションで HTTP/2 Server Push で 必要リソースをまとめて Push
  • Render
    • 上記で受け取った HTML リソースを元に初期画面をレンダリングする
  • Pre-cache
    • 上記初期画面で利用されるリソースは、Server Push により非同期的に Service Worker が Pre-cache する
    • また、今後利用しそうな追加リソースについても、非同期・投機的に Service Worker が事前に DL 、キャッシュする
  • Lazy load
    • 初期画面以降で必要になった画像などのリソースを、画面スクロールなどを検知し、表示に必要になったタイミングで遅延読み込みする

上記の太字箇所を画像で説明すると、以下のようになります。

HTTP/2 ( Server Push )

f:id:medley_inc:20200716154542p:plain

HTTP/2を活用するパフォーマンス最適化 ADAPTIVE ACCELERATION

HTTP/2 の Request / Response の多重化だけを利用したケースと比較すると、「ブラウザがページを解析して、必要リソースを Request する」よりも前に「サーバが必要リソースを強制的に Push 」しているのがわかります。

この Push されたリソースを Service Worker が受け取り → キャッシュ化することで 「ページ解析が終わった時点では既に必要リソースがブラウザにキャッシュされている」 状態となり、アプリの初回起動が速くなる、というのが Push & Pre-Cache の速度改善の仕組みです。

Service Worker で必要になりそうなリソースの事前キャッシュ

また、初期画面に必ず必要なリソース以外については、「このあと必要になりそう・なるはずの追加リソース」ということで、Service Worker に投機的に事前 DL → Pre-cache されることも可能です。

このあたりのキャッシュ戦略・導入事例は以下の一休さんの記事が詳しいです。

一休.comにService Worker(Workbox)を導入しました

PRPL Pattern の採用状況

さて、そんなパフォーマンスに嬉しい PRPL Pattern ですが、HTTP/2、PWA 自体の普及率も高くなくまだまだプロダクションでの採用事例は少ない印象です。

  • Google I/Oで日経電子版が事例として紹介された話
    • PRPL パターンを参考にした Service Worker を使ったキャッシュ、HTTP/2 Push でのリソース配信などが採用されている
  • ライブラリでは有名どころだと Gatsby が標準対応、当たり前だが Google の Polymer ライブラリも PRPL パターンで実装されている

とはいえ、PWA 化している Web サイトであればパターンの適用はそこまで難しくありません。

また Next.js の PWA 化ライブラリ next-pwa では、Next.js 本体の Code Splitting 機能と連携した「 Service Worker での追加コード読み込み」をサポートするなど、このようなアーキテクチャパターンの潮流は今後も派生していくのかな?という気がしています。

まとめ - HTTP/2 + PWA + PRPL Pattern

まとめです。

  • PWA とは
    • Web プラットフォームの新機能を使った「ネイティブアプリとウェブアプリのいいとこ取りした UX の高いウェブアプリ」という概念
  • PRPL パターンとは
  • プロダクトで使えるのか
    • Web サイトの PWA をする/しているのであれば、サーバの HTTP/2 化をして、リソースの Push、Pre-cache を導入するのはパフォーマンス観点で十分検討できるのでは
    • 但し、SPA + SSR 構成のサイトでは、Next.js などフレームワークのコード分割に寄せるのが今の所無難そうではある

今回は調査のみで、プロダクトへの実践投入は行いませんでしたが、今後プロダクトの PWA 化が企画されるような場合は、ぜひ導入してみたい技術だなと感じました。

以上、ここまで読んでくださり、ありがとうございました。