見出し画像

Vueコンポーネント設計とコーディングガイドラインの策定ー内定者アルバイトを終えて Now in REALITY Tech #104

はじめに

こんにちは。 REALITY サーバーチームで内定者アルバイトとして勤務している Kunado です。昨年の7月にサーバーチームにジョインして以来、内部向け管理ツールへの機能追加、 Web フロントエンドコーディングガイドライン策定、サーバーサイドパフォーマンス改善などに携わりました。

今回は Web フロントエンドコーディングガイドライン策定についてご紹介します。

背景

REALITY のサービスは iOS / Android 向けのネイティブアプリとして提供されていますが、アプリ内のお知らせやイベントのページ、公式 LP や Web コイン購入のページなどは Web ページとして提供しています。

このようなユーザー向けの Web ページに加えて、プロダクトマネージャーやアプリ内イベントの運用チームがデータを入稿するための機能や、業務効率化のための Web ベースのツールとして、社内向けに作成している管理画面が存在します。

ユーザー向けの Web ページと社内向けの管理画面のこれら2つのサービスはいずれも Vue.js および TypeScript という技術構成で実装されています。

これらのサービスは主にサーバーチームのエンジニアによって機能追加されてきました。 REALITY では API サーバーは主に Go 言語で実装されていますが、コーディングガイドラインも整備されている Go 言語と比較して、 Vue.js や TypeScript の習熟度は各エンジニアによってまちまちで、コード品質の担保ができていないという課題がありました。

問題意識の共有

筆者がサーバーチームにジョインしてすぐの頃は、まずはサーバーチームでの業務に慣れるために、管理画面の改修系の軽めのタスクから取り掛かることになりました。これらのタスクで既存のコードを読み書きしているうちに、コードの書き方についてもっとこうしたら良いのではないか?と思える点がいくつか出てきました。

あるとき、そのような話をシニアエンジニアリングマネージャのションローさんとしたところ、「その考えをまとめてサーバーチームの人たちと話してみたら?」と提案していただきました。そこで、当時自分が感じていた既存のコードベースに対する問題意識についての資料を作成して、サーバーチームの定例の場で議題に挙げることにしました。

このとき、サーバーチームの定例では以下のような項目について共有していました。

Vueコンポーネントの書き方について
* コンポーネント(UI) からフロントエンドのビジネスロジックを分離する
* コンポーネント内で持つ状態を最小限にする
* page : component ≠ 1 : 1 の関係
TypeScript の書き方一般について
* 変数宣言で var は使わず const を使う、やむを得ない場合に let を使う
* as や any の使用はできる限り避ける
* API レスポンスに対する型付け

このうち、例えば「コンポーネント(UI) からフロントエンドのビジネスロジックを分離する」の項目では以下のようなことを書いていました。

コンポーネント(UI)からフロントエンドのビジネスロジックを分離する (導入コスト: ★★☆)
UI を表現するためのコンポーネントと、フロントエンドでのビジネスロジックを分離します。 まず、フロントエンドのコードの役割は概ね、以下のように分類できると考えています。
1. ユーザーとのインタラクション
    a. ユーザーからの入力の受け取り
    b. イベントのハンドリング
2. 取得したデータの表示への反映
3. サーバーとのリクエストの送受信とデータの加工
以上のうち、コンポーネントの責務を ユーザーとのインタラクション と 取得したデータの表示への反映 とし、サーバーとのリクエストの送受信とデータの加工はコンポーネントから分離します。

これを以下のような方法で表現します。
- ユーザーからの入力の受け取りと取得したデータの表示への反映 : コンポーネントの状態 (コンポーネントのプロパティ)
- イベントのハンドリング: ハンドリングのためのメソッド (コンポーネントのメソッド)

 React などの宣言的 UI の設計思想の背景には、
> UI = f(state)
というものがあります。これは「UIは状態の写像である」と読み、 状態が UI を決定づけるという考え方です。 つまり、 UI の状態を UI に変化を与えるものと考え、ユーザーからの入力やイベントのハンドリングを起点として状態の変化を起こすことができるようにします。

フロントエンドのビジネスロジックを組み立てる処理を純粋関数として、それらの関数を用いて副作用 (コンポーネント内部の状態の更新) を伴う処理をコンポーネントのメソッドとして構成します。

この方針ではコンポーネント内部で持つ状態とメソッドを最低限にし、各メソッドは実際の処理を Pure JS 関数の処理と結びつけるのみにしてコンポーネントの役割を極力薄くして UI を表現する役割に徹底させます。

得られるメリット
このような方針で書くことによるメリットとして以下のような観点が挙げられます。
- テスト容易性とコードの予測しやすさ
- コードのメンテナビリティ

テスト容易性とコードの予測しやすさ
まず以上のように構成することのメリットとして、テストコードを書きやすくなるという点が挙げられます。
値を受け取り値を返す冪等な関数としてロジックが表現されているなら、関数に引数を与えてその結果を検証するだけでテストをすることができます。一方で class のメソッドとしてビジネスロジックを表現していく場合、インスタンスの生成とメソッド呼び出し、そしてその結果としてインスタンスの状態がどのように変化したかを検証することになります。これらを比較すると、関数でロジックが表現されていた方がテストコードに検証のための準備などのテストの本質以外の部分が入る余地がなくシンプルで書きやすいと考えています。
また、関数をベースにコードを構成することでインスタンスの内部状態に依存せずに引数の値によってのみ結果が変わるような形でロジックを実装することになり、結果を予測しやすい機能の見通しが良いコードの書き方になるはずです。

コードのメンテナビリティ
変化しやすいもの (ここではUI) と 変化しにくいもの (ビジネスロジック) とを分離し、変化しやすいものが変化しにくいものに依存するという構造にすることでコードのメンテナビリティが向上するということが考えられます。

以上のような内容を定例で議題にあげさせてもらった結果、チームの皆さんにも問題意識が伝わり、 Web フロントエンド向けにもコーディングガイドラインを作成しようということになりました。こうして、筆者が抱えていたタスクが捌けたタイミングでコーディングガイドラインの策定にタスクとして取り組むことになりました。

実際のガイドラインの項目

実際のガイドラインでは、コードレビューで見つけた場合にレビュアーに指摘して欲しいものを MUST 、ベストプラクティスとしてなるべく取り入れたいものを SHOULD として、2段階のレベルを設けて以下のような項目を置きました。

今回は Web フロントエンドのコーディングガイドラインのベースとして、特に守りたいものに項目を絞りました。

MUST (レビューで見つけたら指摘してほしいもの)
* 命名規則一般 (UpperCamelCase/lowerCamelCase)
* 変数宣言では var は使わず、 const や let を使う
* as の使用をできる限り避ける
* any の使用をできる限り避ける
* JS ネイティブの Date オブジェクトを使用しない

SHOULD (ベストプラクティスとしてなるべく取り入れたいもの)
* 値が存在しないことを示すときには null より undefined を使う
* switch 文で条件分岐の考慮漏れを防ぐ

このうち、例えば「JS ネイティブの Date オブジェクトを使用しない」の項目では以下のようなことを書いていました。

JS ネイティブの Date オブジェクトを使用しない
日時に関する処理を記述する際に JavaScript ネイティブの Date オブジェクトを使用しないでください。
luxon や dayjs などコードベースに導入されている日時操作ライブラリを使用してください。
Bad 👎
const now = new Date()
Good 👍
const now = DateTime.now() // luxon
const now = dayjs() // dayjs
このようにすべき理由
JavaScript の Date は罠が多すぎるなどで詳しい事情が述べられていますが、JavaScript ネイティブの Date オブジェクトは挙動のクセが強く、ある程度以上の規模のプロダクトでは代替となる日時管理ライブラリを用いることが一般的となっています。
そのため、JavaScriptネイティブの Date オブジェクトを直接用いるのではなく、コードベースに導入されている日時管理ライブラリを用いるようにしてください。

ガイドラインを導入するまでに苦労したこと

次に、ガイドラインを導入するまでに苦労したことを挙げます。

チームのメンバーとのコミュニケーションの取り方

上記の問題意識を提示したのはサーバーチームに入ってきて1ヶ月経ったくらいの頃でした。この頃は、チームメンバーの全員とは関係性をまだ十分に構築しきれておらず、そんな中で先輩方がこれまでに積み重ねてきたコードに対して新人の立場で意見を述べるのには勇気が必要でした。

当時共有したドキュメントの冒頭では以下のように書いており、チームの先輩方とどのような形でコミュニケーションをとるかについては配慮していました。

前提・前置き
* ここでの主張は個人の見解に基づいており、 Vue コンポーネントの一般的なベストプラクティスとして主張されているものと一致しない場合があります。
* 大前提として、どんなに素晴らしいコードでもそれがまだ現実に存在していないのであればそれは価値を生み出しているとは言えず、すでに動いていて現実の課題を解決し価値を生み出しているコードこそが正義であると考えます。すなわち、既存のコードが悪いと主張したり、ましてやそのコードを生み出した人の責任を問いたりといった意図はありません。
* ここでは既存のコードがこれからも価値を生み出し続けることができるようにするためには何をすべきか、メンテナブルなコードを書いていくには何ができるかといったことを論点としたいです。

ガイドラインにどんなことを書くべきか

ガイドラインの内容を考えるにあたって、ガイドラインに盛り込む内容の適切な基準の見極めと、その内容をチームのメンバーが納得できるだけのわかりやすく論理的な説明をすることにも苦労しました。

ガイドラインを導入する PR では、2人の先輩エンジニアに議論に付き合ってもらいながらガイドラインに盛り込むべき内容を検討しました。

PR上での議論の跡。コメントが55件、Commitsが28件と、ガイドラインにどんな内容を盛り込むべきか、どんな書き方をすべきかについては議論が続きました。

ガイドライン策定にあたっての議論では、コーディング規約の案として提案したものの採用されなかったものもありました。

たとえば「if 文の条件節は truthy/falsy な条件として簡潔に書くことができる」という内容の提案もありました。しかし、こちらの記事にもある通り truthy/falsy での条件判定は意図しない挙動になりうるという指摘があったため、コーディングガイドラインへの採用は見送りとなりました。

振り返ってみると、内定者アルバイトという立場でありながら、コードベースをより良い状態にしていこうと動くにあたって、チームで合意が取れるポイントを探っていくという貴重な経験をすることができました。これは、今後のキャリアにおいても非常に役立つ経験になったと感じています。

ガイドライン導入の効果

今回この記事を書くにあたってチームのメンバーに Web フロントエンド向けのガイドラインを導入後の効果について聞いてみると、以下のようなコメントをいただきました。

  • コーディングガイドラインができたことで、Web フロントエントエンドについて最低限満たすべきラインができた。

  • TypeScript をちゃんと書こうという意識が高まった。

  • PR をレビューする際に as や any についての指摘をしやすくなった。

定性的なものではありますが、チームとしてガイドラインの導入の効果が実感できているのならやってよかったと思えますね。

今後の課題

Web フロントエンドのコード品質改善について今後の課題を述べます。

ESLint のルールの有効化

今回コーディングガイドラインに盛り込んだ内容以外にも、 ESLint を用いれば最低限守りたいルールに違反したコードを検知したり、修正することもできます。

ただし、現時点では既存のコードでルールに違反している部分も多いため、すぐさまルールを有効化することはできません。ルールに違反している既存コードを手直ししながら、一つづつルールを有効化していく必要があります。

内定者アルバイトを終えて

最後に、 REALITY での内定者アルバイト全体について振り返ります。

先輩の姿からの学びと課題として感じていること

9ヶ月間の内定者アルバイトでは今回取り上げたフロントエンドのコーディングガイドラインの策定のように、自ら発見した課題については解消していけるように動こうと意識はしていました。しかし、他人を巻き込んでいくというような動き方をすることまではできていなかったという点が反省点だと感じています。今回取り上げた Web フロントエンドのコード品質に関するもののように、特にカバー範囲が広いものについては、自分一人で解決するのは難しいものなので、一緒に取り組んでくれる仲間を増やすことが必要になってくると思っています。

たとえば、サーバーチームの先輩エンジニアには、サクサク会を結成したうすぎぬさんがいます。

自分一人ではできないことを継続的にやっていくために、うまく周りを巻き込んでいくというような動き方ができるようになりたいと感じています。

終わりに

ここでの学びを糧に、4月以降の社会人生活も頑張りたいと思います。
改めて、サーバーチームのみなさん、期間中に関わってくれた全ての REALITY の会社のみなさん、9ヶ月間どうもお世話になりました。