株式会社メドレーDeveloper Portal

2020-10-20

フロントエンド開発環境の継続的なリファクタリング

こんにちは、第二開発グループエンジニアの西村です。主にCLINICSの開発を担当しています。

はじめに

CLINICS は電子カルテ、オンライン診療、予約システム、患者アプリなどを含む統合アプリです。CLINICS がローンチしてから現在に至るまで常に新機能開発と定常改善が行われており、開発環境のメンテナンスは後手になりがちでした。今回はそういった状況を改善すべく、開発環境のメンテナンス、リファクタリングを行った過程から得られたプラクティスについて紹介していこうと思います。

モチベーション

プロダクトの新規開発時に行われる技術選定は非常に難しく、業務要件やチーム状況など総合的に考慮してその時点でのベストな選択をする必要があります。

しかし、選択した技術で長期運用をしていくうちに、メンテナンスが行き届かなくなったコードやライブラリが出てしまいます。

CLINICS ローンチ当初はオンライン診療のみを提供していました。SPA で構成されていましたが、1つの package.json で効率的に開発できていました。他に、現在ほど TypeScript が主流ではなかったので JavaScript のコードがメインで実装されてました。

新たなアプリケーション(電子カルテや予約システムなど)を導入するタイミング、すなわちプロダクトが小規模から中規模に変遷するタイミングや、フロントエンドの時流によって、開発環境を改善できた部分もありますが、しきれていない部分も出てきました。

その改善しきれていない部分を残す状態が続くと Developer Experience(DX)の低下に繋がってしまいます。ですので、私たちは改善しきれていない部分を取り除いていき、よりモダンとされる開発環境へリファクタリングをしていこうと考えました。

DX を向上していくことで技術的なノイズに時間を取られないようになります。そして提供する機能そのものについて考える時間が増え、結果的に CLINICS をより良いプロダクトへ進化させていくのが当リファクタリングの目的です。

課題整理

改善していくためには、現状整理、課題整理を行わないことには何も始まりません。フロントエンド開発環境をメンテナンスするタスクは、プロダクトの機能(ユーザに提供される機能)に直接プラスの影響があるわけではありません。自ずと通常の機能開発や定常改善に比べ優先度は落ちるため、スキマ時間で改善をしていくことになります。こうしたスキマ時間を有効活用するためには、タスクの難易度の理解、タスクを適当に分割、フェージングの計画を行うことが極めて大事です。

そのように考慮した課題の中で、本記事で記載するのは以下の2つです。

  1. ライブラリを定期的にアップデートする運用が固まってない
  • 運用方式が固まっていないことで、放置されてしまいがちです。率先してアップデートするメンバーがいたとしても、属人化の課題が残ってしまいます
  • 放置されてしまったことにより、最新版との差分が大きくなりアップデートするコストも大きくなってしまいます
  • 結果的に、ライブラリのセキュリティフィックス対応や新しく提供された機能をすぐに適応できない環境になってしまいます
  1. 複数の SPA の依存を1つの package.json で管理している
  • 電子カルテ・オンライン診療、社内管理 Web アプリ、患者 Web アプリはそれぞれ別の SPA として作られています
  • それらを1つの package.json で管理しているためそれぞれの SPA が同じ依存パッケージを使わなくてはなりません。小規模のときはこのような構成で十分でしたが、規模が大きくなるにつれて柔軟性が失われると共に、ライブラリのアップデートがもたらす影響範囲が広がってしまうため、容易にアップデートできなくなってしまいます

本記事では記載しませんが「Redux の書き方が混在している」「フロントエンドのテストが少ない」「網羅的に TypeScript 化できていなく JavaScript がまだ残っている」などの課題も挙げられました。

こういう課題は、どのプロダクトにも存在すると思います。それはローンチ当時の技術流行であったり、プロダクトの期待規模、少数メンバに適した設計など要因は様々あり、プログラムのリファクタリングと同様にプロダクトの成長に伴ってリファクタリングしていくことが正だと信じています。

モチベーションにて記載した通り、これら複数の課題は開発環境のノイズであり、除去することによって、より良い DX が得られると考えています。他にこのようなリファクタリングを行うことによって、プロダクトをより堅牢にできるという側面もあります。

上記の2つの課題に対してそれぞれ「ライブラリを定期的にアップデートする運用手段を設けた」「package.json を SPA 単位に分割した」話をこれからしていきます。

フロントエンド開発環境のリファクタリング

ライブラリの定期的なアップデートをする運用手順を設けた

手動アップデート

ライブラリをアップデートするにはコマンドを叩くだけだと考えていましたが、依存している別のライブラリに影響が本当にないかなど調査する必要があると知り、ライブラリのアップデート方法を模索するところから開始しました。

ライブラリではないですが、Node.js のアップデートをしようとすると、node-sass や Firebase が影響していたりして、芋づる式で根っこにあるライブラリのアップデートをする必要が出てきたりするので、一つ一つ問題がないか調査するのが大変でした。

何より、アップデート対象ライブラリのリリースノートに Breaking Changes が書かれていなかったり、semver が守られているかわからなかったりと、プロダクトに影響がないか調べる必要があり、問題の切り分け方が難しかったのです。

ここで得られたライブラリアップデートの安全性担保のプラクティスとして webpack によるビルド結果が変わらないケースと、QA テストによって担保するケースがあることがわかりました。前者は webpack による成果物が変わらないのであれば今回のアップデートが安全であるといえ、後者はエンジニアと QA エンジニアによってライブラリの影響範囲にハレーションがないことを確かめて安全であるといえるというものです。

renovate の運用開始

数カ月間は上記のようにライブラリのアップデートを手動で行っていましたが、確認工数が増えてしまい、他のタスクの時間を圧迫してしまうほどでした。

そこで、アップデートを自動化する renovate dependabot を視野に入れました。renovate は、dependabot に比べて高機能でかつ、無料であるという理由で選定しました。

運用当初、renovate が Pull Request を作成してくれたり、diff によりライブラリの変更点が見やすかったりと、恩恵を感じていました。しかし、徐々に「アップデート対象が多く、それぞれがどういうライブラリで、影響範囲がどこなのか」ということの調査に時間が取られるようになってしまいました。

ここで得られた調査時間を短縮するプラクティスとして「本番影響のあるもの」「開発向け」「ビルド周り」と renovate から来る Pull Request を整理することです。このような整理を行うことで、本番影響のあるものに注力してレビューできるようになり、苦にならずにアップデートをできるようになりました。

結果

ライブラリアップデートの運用手順を設けることによって、今まで以上に堅牢な環境になりました。それから、renovate によって自動的に重要な(本番影響のある)ライブラリのみに集中してレビューを行うことによって、少ない工数でアップデートしていけるようになりました。

package.json を SPA 単位に分割

課題整理で記載した通り、電子カルテ・オンライン診療、社内管理 Web アプリ、患者 Web アプリはそれぞれ別の SPA として作られていますが、1つの package.json で管理しています。ですのでそれぞれの SPA が同じ依存パッケージを使わなければなりません。

弊害として package.json に対して1つの変更があったときにすべての SPA に影響が出てしまいます。ですので、この肥大化した package.json をそれぞれの SPA に分割しようとしました。

package.json を SPA 単位に分割することは責務分離という側面もあり、ライブラリだけでなく、共通していた定数、ロジック、コンポーネント、webpack.config.js、babel.config.js と tsconfig.json などすべてをそれぞれの SPA に依存のない形に閉じるようにしました。これらの分割する作業は非常に泥臭いもので、本記事に記載するほどのものではありませんが、得られた結果について記載していこうと思います。

結果

まず、責務分離ができたので、1つの SPA に対する変更があったときに、他の全ての SPA に対する影響が出なくなりました。よって、1つの SPA に対して新たな Web フレームワークやライブラリを試すことが容易になりました。他にも、1つの webpack ですべての SPA をシーケンシャルにビルドしていたのに対して、現在はパラレルでビルドできるようになりビルド時間が短縮されたため、今まで以上にコミットからデプロイまでのイテレーションが小さくなりました。

これらの結果からフロント開発環境の改善および DX 向上が果されました。

今後の課題

持続的なリファクタリングをする仕組み作り

「ライブラリの定期的なアップデートをする運用手順を設けた」はまさに持続的にライブラリをアップデートするための手段です。「package.json を SPA 単位に分割する」もそれぞれの SPA をメンテナンスしやすい環境作りとしては欠かせない作業でした。

しかし、このままリファクタリングを中断すれば、プロダクトの規模が大きくなるときやフロントエンドの時流によって再びメンテナンスしづらい環境になってしまいます。

なので、持続的なリファクタリングをするためには仕組み作りが欠かせないと考えています。そのためには、属人化によらない仕組みづくり、メンテナンスしやすい環境改善、エンジニアそれぞれのフロントエンド開発環境に対するリテラシを高める取り組みを行っていく必要があります。そのため、現在横軸勉強会などで CLINICS フロントエンドの実装背景や、リファクタリングしやすい書き方などのナレッジを共有しています。

まとめ

フロントエンド開発環境のメンテナンス・リファクタリング自体はあくまでもユーザに新しい機能を提供しているわけではなく、粛々と行っていくものです。しかし、課題を洗い出し、向き合って、解決していったことによって得られたプラクティスは多くあり、フロントエンドのエコシステムに対する理解も多く得られました。

これらのリファクタリングを行うことによって DX が向上していき、技術的なノイズに悩む時間が減り、エンジニアはよりプロダクトの機能開発に専念できるようになっていると信じています。

今回私たちが課題を解決したことによって、持続的にリファクタリングをしやすい土台作りをしたという側面もあると思います。今後の課題として、この土台を基にそれぞれのエンジニアが意識を持ってメンテナンスできるような仕組みづくりも行っていきたいと思います。

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

www.medley.jp

株式会社メドレーDeveloper Portal

© 2016 MEDLEY, INC.