AKARI Tech Blog

燈株式会社のエンジニア・開発メンバーによる技術ブログです

効果的なリファクタリングの進め方

効果的なリファクタリングの進め方

はじめに

今回のAKARI Tech Blogは、AI SaaS事業部 デジタルビルダー Devの渡邊が担当します。

私が燈(AKARI)に入社して最初の1週間で提案・実施したリファクタリングにより、開発工数を30-50%削減できた経験をお話しします。この記事では、リファクタリングを成功させるための説得材料の作り方と、具体的な実施方法について紹介します。

ビジネス側との合意形成の重要性

リファクタリングを検討している開発者にとって、最初の壁は「ビジネス側の理解を得ること」です。「直接的な利益を生まない」という理由で提案が却下された経験をお持ちの方も多いのではないでしょうか。

確かに、私たち開発者は「より良い開発体験」を求めています。しかし同時に、リファクタリングが必要だと感じる状況は「コスト削減の機会」でもあります。これを定量的に示すことができれば、ビジネス側の懸念も解消できます。

定量的な説得材料:利益率の向上

リファクタリングの提案で最も重要なのは、以下の3点を明確にすることです:

  1. 投資回収期間リファクタリングにかかったコストをいつまでに回収できるか
  2. 削減可能なコスト:回収後、どれだけ開発コストを削減できるか
  3. 利益への貢献度:結果として利益率をどの程度改善できるか

開発者として私たちができるのは、売上を直接上げることではなく、開発コストを下げることです。もし利益率の向上が見込めない場合は、リファクタリングのタイミングではないかもしれません。

定性的な説得材料:リスクの軽減

数値化しにくいが重要な要素として、以下の2点があります:

1. 開発者体験の改善

劣悪な開発環境は優秀なエンジニアの離職につながります。システム開発が事業の中核である企業にとって、エンジニアの離職は大きな損失です。

2. 属人性のリスク

リファクタリングが必要な状況では、往々にして特定の人しか理解できないコードが存在します。その人が離職した場合、事業継続性に重大な影響を与えます(実際に「サーバーにアクセスできなくなった」という話も聞いたことがあります)。

これらを脅しではなく、企業戦略上の重要なリスクファクターとして説明することが大切です。

実施した具体的施策

目標設定:コード理解の高速化

私が設定した目標は「該当コードの場所と依存関係を瞬時に把握できる状態を作る」ことでした。

現代では、AIツールの活用により単純なCRUD操作の実装は1-2日で完了できます。しかし、既存コードの理解や依存関係の把握に多くの時間を費やしていては、生産性は向上しません。コードを探す時間を最小化し、実装に集中できる環境を作ることが重要です。

特定した3つの課題と解決策

課題1:ファイル構造の煩雑化

まず取り組むべきは、ファイル構造の統一です。 これは処理の切り出しよりもシンプルですが、特に中規模以上のプロジェクトでは最も効果が大きい施策のひとつです。

「どこに何があるのか」が明確になることで、ファイル探索にかかる時間を大幅に削減できます。さらに、適切な場所に機能を配置することがチーム内での暗黙的なルールとなり、そのモジュールを利用すること自体が“正しい設計”であるという品質担保にもつながります。

ここで重要なのが、SSOT(Single Source of Truth) の考え方です。例外を作らず、一つの情報源(責務)に対して唯一の場所を定義することで、設計の「完全性」を保てます。私はこれを「完全性(Consistency)」と呼んでおり、リファクタリングにおいて最も重視しています。

クリーンアーキテクチャの採用については、MVC・DDD など様々な選択肢がありますが、重要なのは「サービスの性質に合った構造」を選ぶことです。 私のケースでは MVC構成 を採用し、コントローラ・サービス・リポジトリの責務を明確に分離することで、保守性を高めることができました。

問題点

  • ファイルとフォルダが並列に配置され、構造が不明瞭
  • 「common」ディレクトリが複数存在し、真の共通部分が不明

解決策

フロントエンド(React)

src/
  components/            # このサービス専用のコンポーネント
  services/              # サービスごとの分離
    invoice/
      hooks/             # このサービス専用のhooks
      components/        # このサービス専用の画面部品
      features/          # フロントエンド パスごとの分離
        invoiceSearch/   # 検索
            hooks/         # このパス専用のhooks
          components/    # このパス専用の画面部品
          index.tsx      # 画面ルートファイル
          invoiceDetail/
            components/
          index.tsx
    purchase/
      components/
      features/
      hooks/

バックエンド(NodeJS/GraphQL)

src/
  controllers/
    invoice/                   # サービスごとの分離
      invoiceSearch/           # フロントエンド パスごとの分離
        getInvoices.ts         # フロントエンドで使う各エンドポイント
        bulkInvoiceSubmit.ts   # フロントエンドで使う各エンドポイント
    purchase/
        purchaseSearch/
            getInvoices.ts
            bulkPurchaseSubmit.ts
  services/
    invoice/                   # サービスごとの分離
      invoiceSearch/           # フロントエンド パスごとの分離
        getInvoices.ts         # フロントエンドで使う各エンドポイント
        bulkInvoiceSubmit.ts   # フロントエンドで使う各エンドポイント
    purchase/
        purchaseSearch/
            getInvoices.ts
            bulkPurchaseSubmit.ts
  schemas/
      invoice/                   # サービスごとの分離
          invoice.ts
          invoice_format.ts
      purchase/
        purchase.ts
            purchase_format.ts

課題2:仕様の曖昧さ

曖昧な仕様を放置すると、その影響は依存先のロジックやUIにまで波及します。 特に中〜大規模の開発では、機能を追加するたびに「仕様がどこまで保証されているのか」が曖昧になり、結果としてバグの温床や二重実装が発生しがちです。

リファクタリングは、そうした仕様の明確化を行う絶好の機会です。 「この関数は何を返すのか」「この値はどの範囲を取るのか」といった定義を、ドキュメントや型、テストコードを通して明文化することで、設計そのものがドキュメントとして機能するようになります。

また、仕様の曖昧さを排除することで、チーム内の認識齟齬を減らし、レビューやQA工程の効率化にも直結します。

問題点

  • 類似処理で微妙に異なる実装
  • 設定値による仕様のコンフリクト

解決策

  • PdMとの密な連携による仕様確認
  • リファクタリング期間中に仕様の明確化を実施
  • ドキュメント化の充実

課題3:処理の複雑化

Webアプリケーションは本質的に「DBの内容をユーザーが操作しやすい形で表示し、CRUDを提供する」ものです。 しかし実際の開発では、DB設計とUI構造の乖離が原因で、処理が複雑化していくケースが多く見られます。 たとえば、画面上の操作フローがDB構造に直結していなかったり、同じデータを異なる形式で扱うコンポーネントが乱立している場合、変更の影響範囲が読めなくなります。

この問題を解消するには、UIとDBの責務を再定義し、それらを繋ぐサービス層でロジックを適切に分離することが有効です。

問題点

  • 過度に複雑な処理フロー
  • データ変換処理の多重化

解決策

  1. DB設計の見直し
    • ER図の作成
    • テーブル構造の最適化(10以上のテーブルを統合・分離)
    • ユーザーの操作したいデータ構造とDB設計の整合性確保
  2. 処理の簡素化
    • 不要なデータ変換処理の削除
    • 抽象化レベルの適正化
    • DataLoaderパターンによるN+1問題の解決

得られた成果

定量的成果

  • 開発工数の削減:30-50%の工数削減を実現
  • 開発スピードの向上
    • 通常1週間かかる機能 → 2-3日で完成
    • 通常3日かかる機能 → 1日で完成

定性的成果

  • チーム全体の生産性向上:新規参画メンバーのオンボーディング期間短縮・スキルキャッチアップの向上
  • 保守性の向上:バグ修正時間が平均60%短縮
  • キャリア成長の機会:余った時間で上流工程(要件定義、クライアントMTG)に参画

特に最後の点は重要です。工数削減により生まれた時間を使って、マネジメントよりな活動に取り組むことができました。

リファクタリング成功のポイント

1. 小さく始める

最初から完璧を目指さず、影響範囲の小さい部分から着手しました。成功体験を積み重ねることで、チームの理解と協力を得やすくなります。

2. 段階的な実施

Phase 1: ファイル構造の整理
  ↓
Phase 2: 仕様の明確化
  ↓
Phase 3: DB設計の最適化
  ↓
Phase 4: 共通処理の抽出

3. 測定可能な指標を設定

  • 通化の可視化(Duplicated Lines)の導入
    • コードの共通化や整理の効果を測定するには、重複コードの削減が分かりやすい指標になります。おすすめのツールは jscpdです。OSSとして提供されており、プロジェクト全体のコピペ率を検出できます。プロジェクト規模ごとのおおよその目標値は以下の通りです。
      • 小規模:5%
      • 中規模:7%
      • 大規模:10%
  • 新機能追加・バグ修正にかかる平均時間
    • リファクタリングの目的は、コードの見通しを良くし、変更コストを下げることにもあります。実施前後で「新機能追加」や「バグ修正」にかかる平均時間を比較すると、改善効果を把握しやすくなります。プロジェクトが中規模以上の場合は、リファクタリング対象箇所に限定して計測するのも有効です。そうすることで、リファクタリングした部分とそうでない部分の開発効率を、同じ時間軸で比較できます。

まとめ

リファクタリングは「技術的な自己満足」ではありません。適切に計画・実施すれば、ビジネス価値を生み出す投資となります。

成功のカギは:

  1. 定量的・定性的な効果を示す(投資回収期間、コスト削減率・属人性、保守性)
    • リファクタリングの目的は「きれいなコード」ではなく、「ビジネス継続性の向上」です。投資対効果を示し、経営・ビジネスサイドにも納得感を持ってもらえる形で合意形成を行いましょう。そのような合意形成が必要なくとも、一定の投資効果考えておくことが重要です。
  2. 段階的に実施する(小さな成功を積み重ねる)
    • リファクタリングが必要になる背景には、「こんなもんでいいだろう」の積み重ねによる技術的負債があります。小さく確実に進めましょう。
  3. 効果を可視化する(測定可能な指標の設定)
    • 1で設定した目標に対して、どの程度達成できたのかを可視化しましょう。実行前と比べ、どのように開発効率や品質が向上したのかを示すことで、初めて「リファクタリングが成功した」と言えます

リファクタリングによって得られた時間を使って、さらなる価値創造に取り組むことができます。これこそが、リファクタリングの真の価値ではないでしょうか。

最後に

この記事が、リファクタリングを検討している方々の参考になれば幸いです。技術的負債と向き合うことは決して簡単ではありませんが、適切なアプローチと説得力のある提案があれば、必ず実現できます。

皆さんの開発現場でも、より良い開発体験と高い生産性を実現できることを願っています。

We’re Hiring!

燈では、こうした新しい技術や面白いアイデアを形にすることに興味のあるエンジニアを募集しています! 少しでも興味を持っていただけたら、カジュアル面談でお話しさせていただけると嬉しいです!