はじめに
食べログテイクアウトチームの@nakiaです。
お店向けのWebシステム(以下「店舗管理画面」)のフロントエンド開発・運用をしています。
「食べログテイクアウト」がリリースされてから、早いもので1年が経ちました。
今年は新型コロナウィルスの流行によって生活やビジネスをとりまく環境も大きく変わりましたね。
相変わらず忙しない日々が続いていますが、せっかくの機会なのでフロントエンド技術選定の振り返りをしてみようと思います!
書くこと
- フロントエンドの技術選定の背景・選定結果
- 実際に1年運用してみた感想(よかったこと・大変なこと)
書かないこと
- 選定した技術の具体的な導入・実装方法
フロントエンドの技術選定
※技術選定の結果や選定理由についてはadventCalendar2019の記事でも言及があります。併せてご覧ください。(内容重複あり)
店舗管理画面は、食べログテイクアウトアプリから入るテイクアウトやデリバリーの注文を管理するために使われます。
そのため、採用するフロントエンド技術には以下の要件が求められていました。
-
サクサク動かせること :
お店の人はとにかく忙しいです。画面が重くて注文受けられない…なんてことはNGです。 -
(素早くローンチしたいとはいえ)ある程度の品質が担保されていること :
受注まわりの機能は店舗の売上はもちろん食べログテイクアウトの事業KPIにも影響する機能なので、当然品質は大事です。
さらに 新規事業開発 というコンテキストでの運用を見据えると、次のような要件も出てきました。
-
フロントエンドのスキルスタックがない人にもコードを書いてもらいたい :
新規事業は少数精鋭のチームでプロダクト開発にコミットするという方針のもと、フロントエンドエンジニアを何人も抱えることはできません。Railsエンジニアやネイティブアプリエンジニアにもフロントエンド開発をしてもらう可能性がありました。 -
とりあえず動くものを素早くリリースして、後追いで機能追加したい
新規事業は素早く世に出し、ユーザーの反応をみながら改善していくことが重要です。
環境とか基盤が整えられること、変更のしやすさ、スケールのしやすさが求められました。
上記に加え、フロントエンドの観点からは次のことを重視しました。
- 壊れにくいアプリケーション
- 見た目とロジックの分離
- Rails(サーバー側のコード)と癒着しない
- 少なくともあと3年以上は使える技術
選定結果
慎重な選定の結果、次の技術が採用されました。(ざっくりとカテゴリに分けてみました)
UI設計:コンポーネント設計(AtomicDesign)
- 構成要素を部品ごとに分けて構成し設計する → 変更しやすく捨てやすそう
- どれが汎用的なコンポーネントか(=atoms)、どのコンポーネントならstoreに接続してよいか(=pages)が明確にわかる → 毎回設計に悩まなくて良さそう
- アプリケーションで使えるコンポーネントをカタログ化 → UIデザイン時に使えそう
- propsごとに異なる見た目を定義できる → 動作確認が簡単にできそう
言語:TypeScript
- 型のチェックとコンパイルの両方を担ってくれる → 堅牢性を担保できそう
- JSがフォローできない部分(バグや脆弱性につながるミスの未然検知)を補ってくれる
フレームワーク:Vue/Nuxt
-
他のモダンフレームワークに比べると初心者に優しい+日本語ドキュメントが充実 → フロントエンド経験が少ないメンバーもキャッチアップできそう
-
SingleFileComponent形式
- HTML・CSS・JSを1ファイルに記述し「 コンポーネント 」という単位の責務に統合できる → コンポーネント設計と親和性が高そう
- 慣れ親しんだ書き味でコーディングできる→ すぐにコードを書き始められそう
-
Vueで作ったアプリケーションに 「規約」 をもたらしてくれる → 堅牢性を担保できそう
- Vueはライブラリに近い使い方をするので、中規模以上のアプリケーション開発では何かしらの規約やルールがないとカオスになるリスクがある
-
オールインワンになっている → すぐにアプリケーションを動かせそう
-
ビルドまわりが隠蔽されている
- webpackerなどRailsのエコシステムを使わなくて済む → Railsと癒着せずに済みそう
-
Vueのエコシステム(Vue Router, Vuex, VueSSRなど)とデフォルトで連携できる
- 特に Vuex (状態管理ライブラリ)はSPA時の複雑な状態管理を簡単にしてくれそう
-
Vueにはない、アプリケーション開発において必要な機能が補われている(middlewareやpluginsなど)
-
-
他社でも採用事例が多い→ 3年以上は使えそう
レンダリングの方法:SSR+SPA
- Nuxtのサーバー(Node.js)ででViewをレンダリングして返し(Server Side Rendering)ページのroutingはフロントエンドで行う(Single Page Application) → サクサクページ遷移できそう
- 初回表示時のみSSRを使用することでSPAの欠点である初期表示の遅さをカバー
※(不採用)Rails+Webpackerで一部だけSPAのような構成
→モジュールやアセットの管理方法が複雑&世間にナレッジが少なく、メリットが感じられなかったので避けることにしました。
運用してみた感想
さて前置きが長くなりましたが、ここからは実際に1年運用してみた感想を書いていきます!
##良かったこと
フロントエンドの機能を実装できるメンバーが増えた
1年間で、チーム内にフロントエンドの機能実装を経験したが自分以外に 3名増えました !
- 3名全員、もとはフロントエンド以外の領域を得意とするエンジニア(Rails,iOS,Android)でVue/Nuxt未経験
- ペアプロなどつきっきりのレクチャーはほぼせずに、開発を完遂
→選定時の狙い通り、初学者があまり学習コストをかけずに開発できている と思います!
新規事業のスピード感についていけてる
デザイナーさんはstorybookを見ながらコンポーネントを意識したUI設計をしてくれます。
- デザイナーとエンジニアが共通の概念を持って会話できるので、 コミュニケーションがスムーズ
- コンポーネント化されていることで 修正の影響範囲がわかりやすく、 途中から参画したメンバーでも 既存仕様の調査が短時間で済む
- 汎用的なコンポーネントを再利用することで全体のトンマナが自動的に統一されるので 実装コストが削減
→ 週単位で機能をリリースする新規事業のスピード感に問題なくついていけていると思います!
重大な障害は(今のところ)まだ起きていない
1年経って初期開発時からたくさん機能が追加・変更を行いましたが、事業KPIに影響するような大きな障害は起きていません。
-
TypeScriptによって一定の品質・堅牢性が保たれている
- 万が一コードに不備があったとしてもgitにpushしたタイミングでCI上でエラーを吐いてくれる
- 些末なミスに気を取られず機能自体の実装に集中できる
-
storybookのおかげで、Railsと結合しなくても 実装途中でさくっと表示確認(目視のUIテスト)ができる
-
煩雑になることが懸念された状態管理まわりも、Vuex+独自のルールぎめで秩序が保たれている
- 注文情報やメニュー情報など、 複数のコンポーネントから参照される情報が単一のstoreで扱える ので管理しやすい
- 「storeの更新はAtomicDesignのpagesからしか行わない」などルールを決めていたのでアプリケーション内のどこからstoreを更新しているか管理しやすい
大変なこと
正直、選定した技術によるデメリットは今のところありません。
ですが、良かったところだけ語っても記事としてちょっと物足りないので、 個人的にちょっと大変だなと感じる運用 を紹介します。(大事なことなので何度も言いますが、技術選定のデメリットではないです)
Vuex × TypeScriptの相性が悪いので補う必要がある (※開発当時はVue2)
Vueコンポーネント内からVuexのgetters, state, actions, mutationを使おうとするとインテリセンスが効かない、型安全が保たれないと問題があります。
- Vuexの型を拡張した自前の型定義用意することでTypeScriptのメリットを享受できるようにした
→自前ゆえのメンテナンスコストが発生しています。- TypeScriptをバージョンアップしようとしたときにVuexの型拡張が影響を受ける
- Vuexのヘルパーに対応するためには追加で定義を実装する必要がある
なお、Vuex×TypeScriptの相性問題については(未検証ですが)バージョンアップすれば改善されていそうな雰囲気(※)があります。
今後は機能開発と並行して環境改善にも取り組んでいきたいと思います!
いろいろなテストを書く必要がある
テイクアウトに参画する前はフレームワークも使わず(jQuery)、ユニットテストもほぼない環境で開発をしていたので、当時と比べるとどうしてもやることが多く感じてしまいます…
storybookはどうにか維持できているのですが、テスト実装(Jest)のほうはまだ不慣れで進捗がイマイチです。
コンポーネント設計だと1機能ごとにcomponents,pages,storeなど複数ファイルに変更が及ぶため、それぞれテストを書くのが結構大変だなと感じます。 (コンポーネント設計が悪いのではなく、シンプルにマンパワーが足りていない)
とはいえ、継続的インテグレーションを実現するためにテスト実装自体は必要なことです !
→それぞれの責務を理解し、最低限押さえておきたい重要な箇所ところ(storeとか)からカバーしていこうと考えています。
- components:コンポーネント単体の表示・挙動
- pages:ページ単位の制御(storeへのアクセスやルーティングのテスト)
- store:storeの変更が意図通りに動いているか
計測用JSの埋め込みがちょっと面倒
店舗管理画面はクローズドなシステムですが、発生したイベント計測して分析・検証を行っています。
計測の手段としてGoogleAnalytics(GA)とAdobeAnalytics(AA)を検討し、現在はGAを使用しています。
GoogleAnalyticsの導入
後の運用を考慮してGoogleTagManager(GTM)を使うことにしたのですが、意外と気にすることが多くて大変でした…
- GTMに対応しているNuxtのプラグインを探して追加(公式のものがなかったのでnuxt-communityで探しました)
- 計測用のJSがTypeScriptで怒られないように設定
- Content Security Policyで怒られないように設定 etc.
AdobeAnalyticsの導入
ユーザー向けアプリの計測はAAで行っているため「店舗管理画面の計測もAAに寄せたほうが便利かも?」ということで今後置き換える可能性もあるのですが、以下のような懸念があります。
- 現状NuxtでAA計測するためのいいかんじのプラグインがない
- 計測用のJSを読み込むためのpluginを自前で作ることになる
- 型を拡張するためのd.tsファイルも作る必要あり
GAでもAAでもNuxtのフレームワークのルールに従った設定が必要で、単純にJSをimportする以外に対応が必要です。
→ Nuxtに限らず何らかのフレームワークを導入する場合やTypeScriptを使用する場合、この対応は避けられないので、時間をとって向き合うしかないですね。
なお、計測の基盤さえできてしまえば日々の運用(計測内容の追加など)はさほど難しくないです。
おわりに
最後にネタバレする形となってしまいましたが、自分がプロジェクトに参画したのは食べログテイクアウトのリリース直後なので、技術選定には参加していません!!!(?!?!)
※立ち上げ期のメンバーが選び、作った基盤を引き継いで運用している立場からの感想でした
今回の記事を書くために過去のissueを改めて読み返したのですが、当時のメンバーが本当に色々なことを考慮してくれたことがよくわかりました。
今まで大きなトラブルもなく開発・運用できているのは、当時のメンバーが新規事業という特性を考慮し、先を見据えて環境を用意してくれたおかげです。ありがとうございました!
これからも開発は続きますが、堅牢さ・メンテナンスのしやすさをキープできるよう、しっかり運用していきたいと思います
明日は@sadashiさんの「 リモートワークでモブプロをやってみた 」です。お楽しみに!