HHVM型チェッカーの深淵へ:大規模モノレポを「一瞬」で型安全にするIncremental Checkの極意
Hackという言語は、単なる「PHPの型付きラッパー」ではない。HHVMのJITコンパイラと密結合し、静的解析が実行時パフォーマンスに直結する、極めて高度なエンジニアリングの結晶だ。
大規模モノレポにおいて `hh_client` が鈍重になるのは、型チェッカーが「宇宙のすべて」を再計算しようとするからだ。しかし、型チェッカーの挙動、具体的には Incremental Check(差分チェック)のメカニズム を理解すれば、我々は解析グラフを意図的に制御し、チェック時間を劇的に短縮できる。
今日は、大規模プロジェクトのパフォーマンスを支配する「型定義の隔離」と「依存関係の断絶」について、実装レベルで解き明かそう。
—
1. 型チェッカーの「再帰的爆発」を防ぐ設計の鉄則
HHVMの型チェッカーは、依存関係のグラフを辿る。もし君が巨大な `Constants.hack` や `GlobalTypes.hack` を作り、すべてのコードがそれをインポートしていたら、たった1行の変更が全コードベースの再チェックを引き起こす。これが「ビルドが遅い」の正体だ。
最適化の核心は「抽象化による依存の遮断」である。 具体的な実装への依存を避け、インターフェースやOpaque Type(不透明型)を境界線として配置する。
—
2. 実践:Incremental Checkを最適化する「モジュール境界」パターン
大規模なWeb API連携を想定しよう。外部サービスとの通信モデルを直接全域に公開してはいけない。以下のように、型を抽象化して「境界」を作るのだ。
非効率な例(毎回全チェックが発生する)
// 外部APIの構造が全域に漏洩している
// このファイルが変更されると、依存する全てのファイルが再チェックされる
final class UserApiResponse {
public function __construct(
public int $id,
public string $email,
public dict
) {}
}
改善された設計(新タイプを導入し依存を隔離する)
`newtype`(Opaque Type)を活用し、型チェッカーが追跡すべき範囲を限定させる。
namespace App\Domain\User;
// 不透明型(newtype)を使用し、外部からは「何者か」を隠蔽する
// これにより、内部構造が変わっても依存先の型チェックはパスする
newtype UserId = int;
interface IUserProvider {
public function getUserId(): UserId;
}
// 実装クラスは内部的にのみ詳細を知る
final class UserProvider implements IUserProvider {
public function __construct(private int $rawId) {}
public function getUserId(): UserId { return $this->rawId; }
}
なぜこれが速いのか?
`newtype` を使うことで、型チェッカーは `UserId` が `int` であるという詳細を「境界」の向こう側へ押しやれる。内部の `int` の扱いが変わっても、型チェッカーは `UserId` というラベルさえ維持されていれば、依存先を再計算する必要がないと判断するからだ。
—
3. HHVM型チェッカーを最大効率で回すためのTips
① `.hhconfig` の最適化
モノレポのルートにある `.hhconfig` は、全ファイルの解析対象を制御する司令塔だ。以下の設定を確認せよ。
不要なパスを監視対象から外す(これだけで数秒〜数十秒短縮する)
ignored_paths = (vendor/.)
依存関係のグラフ構築を細分化する
enable_at_most_once_check = true
② `type aliasing` の多用を避ける
複雑な `shape` や `tuple` のエイリアスは、型チェッカーに無限の再帰計算を強いる。インターフェースを導入し、`shape` のような構造的型付けを避けることが、大規模プロジェクトでは「型安全かつ高速」な唯一の道だ。
—
4. チーフアーキテクトからの助言:コードは「疎結合」に解析されるべき
大規模モノレポにおいて、型チェックが遅いのは言語のせいではない。「型が密結合しすぎている設計」のせいだ。
1. 境界を設計せよ: `newtype` を使い、モジュール間の型依存を最小化する。
2. 型定義を分割せよ: 巨大な `types.hack` を作るのではなく、ドメインごとにファイルを分割し、型チェッカーの依存グラフに「切れ目」を作る。
3. 解析スコープを意識せよ: `hh_client` がどこで止まっているか `hh_client –log` で常に観測せよ。
型システムは、単にバグを防ぐためのガードレールではない。「どの範囲までが安全かを、コンパイラと会話するための言語」 である。
君たちのコードが、型チェッカーという強力な味方を手懐け、一瞬でビルドを終える堅牢なシステムになることを期待している。さあ、今すぐ不要な `use` 文を削り、型依存の鎖を断ち切るのだ。