WHITEPLUS TechBlog

株式会社ホワイトプラスのエンジニアによる開発ブログです。

コードの削除しやすさを考慮した設計の重要性

こんにちは、CX開発グループでWeb開発を担当している德廣です!

早速ですが弊社は創業から15年が経過し、その間にサービスが進化し続けてきました。それに伴い、システムも複雑化し、コードベースも肥大化してきました。
長い年月の中で追加され続けたコードの中には、現在のサービスに不要なものも少なくありません。
しかし、複雑な依存関係が増えるにつれ、不要なコードの削除が容易ではなくなっています。

今回の議論は、PHPやLaravelの最新化プロジェクトを振り返った際に、「不要なコードが見つかったが、削除するのが困難だった」という声が挙がりました。
そこで、チームで「なぜコードの削除が困難だったのか」「どうすれば削除が楽になるのか」を議論し、「削除しやすさを意識した設計の重要性」について再確認しました。
本記事では、その内容を紹介します。

0. 当記事の文脈における技術スタック

  • 言語: PHP
  • フレームワーク: Laravel
  • アーキテクチャ:
    • DDD (Domain-Driven Design)
    • クリーンアーキテクチャ
  • 使用IDE: PhpStorm

blog.wh-plus.co.jp

1. コード削除が困難になるケース

  • 使用場所・影響するユースケースが特定しづらいコード
    • グローバル変数や関数
    • HogeUtil のような無秩序に使用される共通クラス
<?
class HogeUtil {
    private static $year; // 静的プロパティで年を保持

    // 静的メソッドで年を設定
    public static function setYear(string $year): void {
        self::$year = $year;
    }

    // 静的メソッドで設定された年を取得
    public static function getYear(): string {
        return self::$year;
    }
}

// グローバルな場所で静的メソッドが使用される例
HogeUtil::setYear('2024');
echo "現在の年: " . HogeUtil::getYear() . PHP_EOL;

// 別の場所で年を変更
HogeUtil::setYear('2025');
echo "更新された年: " . HogeUtil::getYear() . PHP_EOL;
  • フレームワークや言語によって暗黙的に呼び出されるコード
    • LaravelのORMで使用されるアクセサやミューテータ
    • __toString() といったマジックメソッド
<?
// マジックメソッドの例
class ExampleClass {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    // __toString() マジックメソッドによる暗黙的な呼び出し
    public function __toString() {
        return $this->name;
    }
}

$example = new ExampleClass('Test');

// __toString() が暗黙的に呼び出される
// __toString()が使用されているか否かを静的解析やIDE上では検知できない
echo $example;
  • 循環参照があるコード
    • 他から明示的に参照されていなくても、互いに依存しているため、削除の影響範囲が把握しづらい
<?
class ClassA {
    public $b;

    public function __construct($b) {
        $this->b = $b;
    }
}

class ClassB {
    public $a;

    public function __construct($a) {
        $this->a = $a;
    }
}

// ClassAとClassBが相互に参照しているため、不要になった際に削除しづらい
$a = new ClassA(null);
$b = new ClassB($a);
$a->b = $b;

2. 削除しやすさを高めるための設計方針

  • staticメソッドの使用を抑える設計

    • 必要に迫られない限り使用しないようにし、共有カーネルや汎用サブドメイン化を検討する。
    • 使用する場合は、副作用を持たせないようにし、状態を持たない処理に限定する
      • 副作用を与えないような使用例: ログ出力やSlack通知
  • モジュール化と依存関係の明確化

    • DDDにおけるBCやドメイン、レイヤードアーキテクチャに従い、依存関係を明確化する。
    • ドメイン間の依存は、OHS/ACLを通してのみ行う。
    • 実際、現在のアーキテクチャに沿って作られた比較的新しいドメインのコードは削除が容易であった。
  • テストの充実による安全な削除設計

    • 適切なUnitTestが整備されていれば、不要なコードを削除しても、CIでテストが通過すれば問題ないと判断できる。
  • 静的解析やIDEによるコード追跡

3. 削除対象コードを可視化するための仕組み化

コードの削除を安全かつ効率的に行うためには、設計やコードレビューだけでは限界があります。
特に依存関係が複雑なプロジェクトでは、不要なコードを見落としてしまうリスクがあります。
そこで、チームではコードの依存関係を自動的に可視化し、削除対象を明確にするための仕組み化に取り組もうとしています。

具体的には、deptrac の導入を検討しています。deptrac を使うことで、ドメイン間やレイヤー間の依存関係を可視化し、削除すべきコードや、その削除が他の部分に与える影響を事前に把握することが可能です。
これにより、アーキテクチャのルールに反する依存が存在する場合を検知できるようになります。

deptrac の導入がうまく進めば、依存関係の確認が容易になり削除プロセス全体を大幅に効率化できると考えています。
導入の進展については、今後の続報で詳細をお伝えする予定です! (先の話にはなりそうなので気長にお待ちください。。)

github.com

まとめ

コード削除の容易さを意識した設計は、メンテナンス性と開発効率を大幅に向上させます。
不要なコードを削除しやすくするためには、staticメソッドの制限、モジュール化、テストの充実が重要だと考えています。
また、プロジェクトが長期化し、メンバーの入れ替わりが発生した際には、実装当時のエンジニアやPOが不在の場合、削除の判断がさらに困難になることもあります。
そのため、将来のメンバーを意識して、実装時から削除しやすさを考慮した設計を行うことが重要だと考えています。


さいごに

ホワイトプラスでは、ビジョンバリューに共感していただけるエンジニアを募集しています!
ネットクリーニングの「リネット」など、「生活領域×テクノロジー」で事業を展開しています。
弊社に興味がある方は、オウンドメディア「ホワプラSTYLE」をご覧ください。オンラインでのカジュアル面談も可能ですので、ぜひお気軽にお問い合わせください。