- コードのリファクタリングは、コードベースを健全に保つうえで重要な役割を果たす
- しかし、誤ったリファクタリングは、かえってコードをより複雑にし、保守しにくくしてしまう可能性がある
- 良いリファクタリングと悪いリファクタリングを見分け、悪いリファクタリングの落とし穴を避ける方法を知っておく必要がある
リファクタリングの良い点、悪い点、そして醜い点
- 抽象化は良い場合もあれば悪い場合もある。重要なのは、いつどのように適用すべきかを知ること
- いくつかの一般的な落とし穴と、それを避ける方法を説明(原文コードは省略)
1. コーディングスタイルを大きく変えてしまうこと
- 開発者はリファクタリング中にコーディングスタイルを完全に変えてしまうミスをよく犯す
- 新しいライブラリを導入したり、まったく異なるコーディングスタイルを採用したりすることは、保守に悪影響を及ぼす可能性がある
- より慣用的で読みやすいコードに改善しつつも、まったく新しいパラダイムや外部依存を導入しないほうがよい
2. 不要な抽象化
- 既存コードを理解しないまま新しい抽象化を過剰に追加するのは問題
- 複雑さが増すだけで、理解しづらくなる可能性がある
- ロジックを小さく再利用可能な関数に分割しつつ、不必要な複雑さは持ち込まないほうがよい
3. 不一致(Inconsistency)の追加
- コードベースの一部だけを完全に異なる動作に更新すると、混乱や不満を招く可能性がある
- 新しいパターンを導入する必要があるなら、まずチームの合意を得るのがよい
- アプリケーション全体でデータ取得に対する一貫したアプローチを維持することが重要
4. リファクタリング前にコードを理解していない
- コードを学びながら同時にリファクタリングするのは良い考えではない
- バグを生み、性能を低下させ、機能を削ってしまうおそれがある
- リファクタリング前にコードを深く理解し、既存の挙動を保ちながら改善することが重要
5. ビジネス文脈を理解する
- ビジネスへの理解なしに技術選定を行うのは破滅的になり得る
- SEO に大きく依存する EC サイトでは、クライアントサイドレンダリングは悪い選択になり得る
- Next.js や Remix のようなサーバーサイドレンダリングのアプローチのほうが、SEO と性能の面でより良い選択になる可能性がある
6. 過度なコード統合
- 柔軟性を犠牲にしてまでコードを「きれいに」するために統合するのは良くない
- 抽象化する際には、常にそれが提供するユースケースを考慮すべき
- 元の実装が提供していたすべての機能を、その抽象化でも扱えるようにすべき
正しい方法でリファクタリングする
- コードのリファクタリングは必要だ。ただし、正しいやり方で行う必要がある
- 私たちのコードは完璧ではなく整理が必要だが、既存のコードベースとの一貫性は維持しなければならない
- コードを十分に理解してからリファクタリングすべきであり、抽象化は慎重に選ぶべき
- 成功するリファクタリングのためのヒント:
- 段階的に進める: 全面的な書き直しではなく、小さく管理しやすい変更を行う
- 重要なリファクタリングや新しい抽象化を導入する前に、コードを深く理解する
- 既存のコードスタイルに合わせる: 保守のためには一貫性が鍵
- 新しい抽象化を増やしすぎない: 複雑さが本当に必要な場合でなければ、シンプルに保つ
- 特にチームの合意なしに、まったく異なるプログラミングスタイルの新しいライブラリを追加しない
- リファクタリング前にテストを書き、進めながらテストを更新する。これにより、元の機能を維持できているか確認できる
- 同僚同士で、これらの原則を守る責任を持ち合う
より良いリファクタリングのためのツールと技術
- より良いリファクタリングのために、次のような技術やツールの活用を検討:
リンティングツール
- 一貫したコードスタイルの適用と潜在的な問題の発見のために、リンティングツールを使う
- Prettier によって一貫したスタイルで自動フォーマットできる
- ESLint ではカスタムプラグインを通じて、より細かな一貫性チェックを行える
コードレビュー
- リファクタリングしたコードをマージする前に、同僚からフィードバックを得るため徹底したコードレビューを実施する
- 潜在的な問題を早期に発見し、リファクタリング後のコードがチームの標準や期待に合っているか確認するのに役立つ
テスト
- リファクタリングしたコードが既存機能を壊さないよう、テストを書いて実行する
- Vitest は特に高速で堅牢、かつ使いやすいテストランナーで、基本的に設定不要
- 視覚的テストのために Storybook の利用を検討する
- React Testing Library は React コンポーネントのテストに適したユーティリティ群(Angular など向けの派生版もある)
(適切な)AI ツール
- 既存のコーディングスタイルやルールに合わせられる AI ツールによって、リファクタリングの取り組みを支援できる
- Visual Copilot は、フロントエンド実装時に一貫性を保つのに特に有用なツール
- 既存のコーディングスタイルに合わせつつ、デザインシステムのコンポーネントやトークンを正しく活用しながら、デザインをコードへ変換する助けになる
結論
- リファクタリングはソフトウェア開発に必要な要素だが、既存のコードベースやチームの力学に配慮しながら慎重に行うべき
- リファクタリングの目標は、コードの外部的な挙動を変えずに内部構造を改善すること
- 最高のリファクタリングはしばしば最終ユーザーには見えないが、開発者の仕事を大きく楽にしてくれる
- 可読性、保守性、効率性は向上させるが、システム全体を混乱させることはない
- コードについて「大きな計画」を立てたくなったときは、一歩引いてコードを徹底的に理解し、変更の影響を考え、チームが歓迎できる段階的な改善を行うべき
- 将来の自分や同僚は、コードベースをクリーンで保守可能な状態に保つための思慮深いアプローチに感謝するだろう
6件のコメント
テストコードもないまま、いきなりリファクタリングから始めてしまう方も時々います。
見ていると、危険極まりない綱渡りをしているようでスリルがありますね(笑)
4番目の内容、「リファクタリングの前にコードを理解していない」、これは本当に心に響く内容ですね。
あまりにも当たり前の話で、基本的な素養のような話ですね(笑)。うまく整理してくれていて読みやすいです。リファクタリングをすれば100%良いコードが出てくると思います。もしそうでないなら、昨日より自分の実力が後退したということではないでしょうか?
当たり前のことを整理しているのを見ると、誰かがめちゃくちゃにしていて本当に腹が立って書いた文章のようにも見えますね(笑)
リファクタリングをうまくやる方法というより、現場で実践的にコーディングをうまくやる方法に近いですね。
(ジュニアが来てレガシープロジェクトをリファクタリングすると言ってあちこち複雑にし、バグまで作ってイラッとして書いた文章っぽいですね……)
業務の単位とコードの単位を区別できなかったり混同したりする人は、キャリアの長短に関係なく、ああいうリファクタリングをするものです。修正しやすく保守できるコードというのは、結局のところ後から別の人が来ても業務の流れを読み取れるものでなければ満たせないわけですが、それはやはり実務の現場で経験しないと分からないものなのかと思ったりもします
Reactで過度な抽象化のサインとして、こんな事例を経験したことがあります。すでにUIフレームワークによってテーブルコンポーネントが抽象化されているのに、2つのテーブルのスキーマが似て見えるという理由で制御の反転を使い、何の役割もないコンポーネントを新たに作るのは避けるべきだと思います。アトミックデザインを適用すると言って10行程度の空っぽのコンポーネントを乱発するケースを見ると、いろいろな部分を見なければならず、とても不便でした。
DRY原則は、まったく同じ形で同じ役割をするコードをコピペするな、という意図だったはずですが、それを誤解する人はどうしても出てくるものだと思います。デザインパターンを盲信するな、という話がこういう文脈で出てきたのでしょうか