フロントエンドの開発における負債化の要因
概要
バックエンドのプロダクトに比べ、フロントエンド開発が負債化しているチームに出会うことは珍しくありません。特に、設計や実装方針が揺らぎやすいフロントエンド開発では、技術的な負債が蓄積しやすく、結果的にプロダクトの品質や開発効率が低下することが少なくありません。
これまで様々な現場に携わり、なぜフロントエンド開発において統一的な設計(ディレクトリ構造、コンポーネントの分離方針、状態管理の一貫性)が維持が難しい理由を考え、その理由を今回整理してみました。
本記事は、過去の経験を基にした一個人の意見です。そのため、何かお気づきの点やご指摘がありましたら、ぜひお知らせいただけると嬉しいです。また、主にReactに関する話になりますが、Vue.jsやSvelteにも適用できる話題を意識して書いています。なお、専門用語についての説明は省略していますので、その点はご了承ください。
この記事を読むことで得られること
- フロントエンド開発における負債化の典型的な原因と背景
- 負債を防ぐための具体的な設計・運用方法
- チーム全体で設計を統一し、運用を改善するためのヒント
想定読者
- フロントエンド開発者
- 日々の開発における負債化のリスクや、その解消方法について知りたい方。
- ソフトウェアアーキテクト
- 設計方針の決定やプロジェクト全体の構造設計に役立つ知見を求める方。
- エンジニアリングリーダー
- フロントエンドチームを率い、設計や運用の改善に取り組む立場の方。
- CTOや技術責任者
- チーム運営や技術選定において、フロントエンドの設計や運用の戦略的観点が必要な方。
主にフロントエンド開発者を対象にしていますが、CTOやプロジェクトマネージャーの方にも、チーム運営や技術選定の参考になる内容を含んでいます。
負債化の要因
- チーム体制面
- 技術面(設計)
- 技術面(開発)
の3つの観点から、フロントエンド負債化の要因を考えていこうと思います。この分類が一般的かどうかは定かではありませんが、負債化のリスクを管理しやすくするために、問題の所在を明確にし、具体的な改善策を立てやすくする目的でこのように整理しました。
チーム体制面
フロントエンドに精通したメンバーが少ない
- 専任のフロントエンド人材の不足
- 業務委託に依存した体制
- フロントエンド開発をカバーするために業務委託や外部リソースを活用する。しかし、業務委託は短期契約が一般的であり、契約が終了すると、また別の業務委託が新たに加入し、同じプロジェクトを引き継ぐことが繰り返されます。この入れ替わりによって、設計方針やコードベースの理解が継続的に断絶される。
フロントエンド開発は後回しにされがち
- プロジェクトの最優先事項がバックエンドのインフラやセキュリティ強化に置かれるため
技術の移り変わりと学習コストの高騰
- フレームワークの流行の変化
- 現在では、Reactがフロントエンド開発の主流となっていますが、Vue.jsやSvelteを採用するプロジェクトも増加しています。このようにフレームワークの流行が次々と変化する中で、新しい技術を学ぶコストが常に発生します。
- アーキテクチャーの変化
- UI設計中心のAtomic DesignやUIロジックとレンダリングロジックの分離を目的とするContainer/Presentational Patternから、機能ごとにコードを分割するFeature-Driven Architectureやモジュール単位で管理するModular Architectureへと、適用場面に応じて多様化してきています
- 状態管理ライブラリの流行の変化
- Redux
- Context APIの充実と、軽量な状態管理ライブラリ(JotaiやZustandなど)の普及により、小規模プロジェクトではファーストチョイスとされることが少なくなっています。
- Recoil
- GitHub を見る限り、約1年メンテナンスが行われていないようです
- 小規模プロジェクトではZustandやJotaiが選ばれることが多いようです。
- Redux
- バージョンアップ
- Hooksの導入
- クラスコンポーネントから関数コンポーネントへの移行
- Page RouterからApp Routerへの移行
- Context APIの導入
- Server Componentsの導入
- Hooksの導入
フロントエンドの軽視と捨てられる前提
- 技術的な決定が適当にされやすい
- バックエンド主導の開発体制
- フロントエンド要件が後回しにされ、設計の優先順位が低くなりがちです。
- フロントエンド技術の進化スピード
- 「捨てられる」前提
- 「どうせ数年で刷新する」という暗黙の前提のもと、持続可能な設計やリファクタリングが後回しにされる
- バックエンド主導の開発体制
- 長期的なメンテナンスが考慮されない
- コンポーネントや状態管理の設計が疎かになる
- フロントエンド開発者が明確な設計方針を持たず、場当たり的に実装するケースがあります。
知識共有不足による属人化
- 知識が特定のメンバーに集中する
- ドキュメント不足
- 「実装を見れば分かる」という意識が強い現場では、設計意図や運用方針が共有されず、結果的に新しいメンバーのキャッチアップが難しくなります。
技術面(設計)
そもそもフロントエンドの設計が移り変わっているので、その時々で最適な技術選定が難しいという前提はありますが、下記の要因が挙げられそうです。
技術の変化に伴う設計変更
ざっくりフロントエンドの設計をまとめた形になりますが、フロントエンドの技術変化に伴い設計が変化することによって、それにより負債が生じることになりました。
- MVC時代
- サーバー側でHTMLを生成し、クライアントに配信するMPA(Multi-Page Application)が主流。ページ遷移ごとにサーバーリクエストが発生。
- SPAの登場
- JavaScriptを活用してクライアントサイドでレンダリングを行い、スムーズな操作感を実現。初回ロードが重い一方でページ遷移が高速化。
- SSRの再評価
- 初回ロードの重さやSEOの課題を解決するため、サーバー側でHTMLを生成しつつクライアントでハイドレーションする手法が普及。
- SSGの進化
- ビルド時に静的なHTMLを生成してキャッシュ配信することで、パフォーマンスを向上しつつSEO対策も可能に。
- ハイブリッド化
- Next.jsなどでSSR・SSG・CSRを柔軟に使い分ける設計が一般化。リアルタイム性とスケーラビリティを両立。
ツールの依存とサポートの終了
jQueryやMoment.jsのように長く使われたツールも時代遅れになり、Recoilのように大企業が開発したツールでさえもメンテナンスが滞る可能性があります。
アーキテクチャパターンの流行と変化
フロントエンド設計におけるアーキテクチャパターンも進化してきました。その変化に対応しきれない場合、負債が発生することがあります。
-
過去のアーキテクチャ
- Atomic Design
- UIを構造的に設計する方法。粒度の判断が難しく、スケーラビリティの観点から採用が減少
- Container Presentational Pattern
- UIロジックとレンダリングロジックを分離する方法。モジュール化が難しい場合も
- Atomic Design
-
現在のアーキテクチャ
- Feature-Driven Architecture
- 機能ごとにコードを分割することで、スケーラビリティとメンテナンス性を確保
- Modular Architecture
- モジュール単位で管理し、再利用性を高める手法
- Feature-Driven Architecture
技術スタックの選択の難しさ
技術スタックの選択がプロジェクトの成功や維持管理に大きな影響を与えます。ツールやライブラリの進化が早いため、選択を間違えると技術的負債として長期間のプロジェクト運用に悪影響を与えます。ここでは状態管理ライブラリを具体例に挙げます。
- Redux
- 信頼性の高さや予測可能な状態管理等のメリットがあった
- しかし、軽量かつ柔軟な状態管理にトレンドが変化したのもあり、使用機会が減少
- useStateやuseReducer、Context API
- Zustand、Jotai等の直感的な状態管理ライブラリの登場
- Recoil
- Facebookが開発していたが、1年以上メンテがない
新しいツールや技術の導入によるスキルギャップ
- 急速な技術革新によるスキルギャップ
- フレームワークだけでなく、ツールに関しても新しいものが出てきています。導入もしやすいものも多いため、プロジェクトに最新技術を取り入れます。しかし、既存メンバーのスキルや経験がこれらに追いついていないケースの場合、実装速度の低下やコードの統一性の喪失します。
- ツールの導入によるディレクトリやコード構成の複雑化
- 新しいツールやプラグインをプロジェクトに取り入れた際、完全に移行し終えるまでの間、プロジェクト構造が複雑化します。その間は、初期に採用した技術と新たに導入した技術が並行して存在し、チーム間で共有するための認知コストが増大し、メンテナンス性も低下します。
技術面(開発)
設計が不十分だと、開発に負の影響が出るため、開発面の負債も技術面の負債と密接に関わっているが、ここでは開発フェーズにおける技術的負債と設計フェーズで生じた技術負債と分けて説明する。
コードレビュー視点の曖昧化
設計が十分に行われていない場合、以下のような問題が発生します:
- コンポーネントや機能の責務が曖昧
- ロジック、UI、データ処理の分離基準が不明確で、コードの責任範囲が不適切になる。
- コードレビューの基準が希薄
- 開発者ごとに異なる基準でレビューが行われ、設計方針の統一が困難になる。
といったような、状況下だと、コードレビューの指針が希薄化します。
Componentの肥大化
- 複数の責務を持つコンポーネント
- UIロジック、データ処理、状態管理などが1つのコンポーネントに詰め込まれ、結果として肥大化。
- 適切なディレクトリやファイル構造がない
- コンポーネントの責務分離やリファクタリングが困難になり、コードがさらにスパゲッティ化する
TypeScriptが弱い
- any型の多用
- asの多用
- 不適切な unknown 型の使用
- 可読性や意図が曖昧になる。
メンテナンスができず途中で捨てられたプラグイン
プラグインは容易に入れることができ、検証するのも簡単なため、とりあえず実際のプロジェクトに入れてみるケースが存在します。プロジェクトの経過とともに、メンテナンスがされなくなり、負債化するケースも存在します。
CSSの管理の甘さ
- CSSのディレクトリ設計が汚い
- スタイルが一箇所に集約されず、どこに記述すればよいか分からない。
- CSS設計が甘い
- BEMやSMACSSのような体系的な設計ルールが採用されていない。
- CSSフレームワークやツールの乱用
- 必要以上にCSS-in-JSやフレームワークを導入し、結果としてスタイルの重複や複雑化が発生。
等でスタイルを記載するのが辛くなるケースが存在します。
フロントエンドの負債化に対応するためには?
フロントエンド負債化を完全に防ぐ「銀の弾丸」はありません。しかし、日々の開発で当たり前のことを継続的かつ確実に実行することで、負債化を抑え、持続可能な開発を実現できます。以下に具体的な対策を挙げます。
- 設計をしっかり行い、継続的に設計を見直すこと
- 完璧な設計は存在は存在しないと思いますが、設計指針を定め、そのプロジェクトに合った最適な設計方針を策定することが重要かと。
- 負債化を防ぐために、ある程度の人的リソースを確保する
- 負債化を防ぐには、設計の見直しやリファクタリングに十分な時間と人的リソースを確保する必要があるため。
- ドキュメント整備と知識共有
- 設計の意図が分からないと、開発やコードレビューの指針も立てづらいと思うので。
- コードレビューとペアプログラミングの導入
- チーム内の知識共有を促進し、設計意図の確認や潜在的な問題の早期発見
- 技術的負債の定期的なリファクタリング
- 技術選定の慎重な判断
- 継続的な技術教育とキャッチアップの機会提供
- 型定義周りの議論を減らすために、ESLintや型生成ツールの導入
- 型定義のスタイルを自動的に統一し、レビュー時の議論や手間を削減
- コンポーネント設計の標準化
- コンポーネントの分類とレイヤリング
- コンポーネントの汎用性を高める
- コンポーネントの依存関係の分離
- データの受け渡しはProps
- 状態やロジックの管理はCustom Hooksなどに分離すること
- テストの充実化
- フロントエンドのテストも負債化する可能性を孕んでいるため、テスト戦略を明確にすることが重要です。テスト対象のスコープを絞るなど、対策を講じることも重要です。
- CI/CDの活用と自動化
- Pull Requestごとに静的解析、Lint、型チェック、ユニットテスト、E2Eテストを実行
- モニタリングとエラーログの可視化
Discussion
いい記事をありがとうございます!
TypeScriptが弱いの項目では、
any と同じぐらい as の多用にも注意が必要ですよね。
TypeScript初心者でも any の多用は避けるべきだと知っていることが多いですが
as も頼り過ぎないようにしないとリファクタリングで型エラーにならずに修正範囲の把握が遅れて作業のテンポが悪くなることがあります。
ありがとうございます!おっしゃる通り、asの多用にも注意が必要ですね。追記しておきます!