NGW開発BLOG
← 記事一覧へ
·#025

名前と実体が食い違う関数を掃除するフォント設定のリファクタ

帳票PDFのフォント設定関数群が長年の変更で荒れていた。名前が嘘をつく関数をどう安全に掃除したか、その過程で見えた命名の価値を綴る。

名前が嘘をついている

帳票PDFのフォントを設定する関数を開いて、しばらく固まった。関数名には昔使っていた明朝(小塚明朝)の名が入っているのに、中身が設定しているのは別の明朝(MS明朝)だった。過去に一度フォントを乗り換えたとき、呼び出し側を全部直すのが面倒で、実体だけをこっそり差し替えたのだろう。名前は古いまま、動作だけが新しい。

これがいちばんたちの悪い負債だ。バグではない。出力される帳票は正しい。だから誰も直さない。しかし次にこの関数を触る人は、名前を信じて「ああ、これは旧フォント用か」と判断を誤る。名前は最も手軽なドキュメントであり、嘘をつくドキュメントほど危険なものはない。

同じ関数群を通しで眺めると、症状はそれだけではなかった。中身がバイト単位で同じなのに別名で存在する重複が複数。どこからも呼ばれない未使用関数。「◯◯フォントに切替」と昔のフォント名を指したまま化石化したコメント。関数名自体にtypoが混じっているものまであった。長年の小さな変更が地層のように積もった結果だ。

ディスパッチャ1本に寄せる

方針はシンプルにした。フォント選択のロジックを、種別で分岐するディスパッチャ1本に集約する。明朝・ゴシック・数字・縦書きといった「使う側が本当に知りたい区分」を引数に取り、そこから実体のフォント設定(明朝=MS明朝、ゴシック=IPAexゴシック、縦書き=IPA明朝、外字抽出=IPAmj明朝)を呼ぶ。使う側は種別だけを指定すればよく、どの物理フォントに紐づくかはディスパッチャの内側に閉じる。

function setReportFont(string $kind): void
{
    match ($kind) {
        'mincho'   => applyMincho(),
        'gothic'   => applyGothic(),
        'number'   => applyNumberFont(),
        'vertical' => applyVerticalMincho(),
    };
}

この形にすると、先の「名前と実体の食い違い」が構造的に消える。使う側は物理フォント名を書かないので、実体を差し替えても使う側の名前が嘘になりようがない。乗り換えはディスパッチャの内側1箇所で完結する。

そのうえで実体側を掃除した。名前と実体が一致するようリネームし、重複は削除、未使用は削除、コメントは今の実フォント名に統一、typoは修正。派手さはないが、ひとつ直すたびに関数群が素直になっていく感触があった。

削除は「呼ばれていない証拠」を先に

この手の掃除で怖いのは削除だ。「たぶん使われていない」で消すと、忘れた頃に帳票が化ける。だから削除の前に、その関数の呼び出し元をコードベース全体で数えた。結果がゼロであることを確認してから、はじめて消す。当たり前のようでいて、この順番を守るかどうかが安全と事故を分ける。

さらにこのシステムは複数のコードベースに派生している。同じ整理を機械的にコピーするのではなく、派生先ごとに呼び出し件数を数え直しながら当てた。片方でゼロでも、もう片方ではまだ呼ばれているかもしれないからだ。変更のたびに構文チェックを通し、少なくとも壊れた状態でコミットしないことも徹底した。

AIとの協働作業での学び

動作を変えないリファクタは後回しにされやすい。壊れていないものを触るリスクだけが見え、直す利益が見えにくいからだ。だが「名前が実体と食い違う」状態は、静かに人を欺き続ける負債である。放置するほど、次に触る人の時間を確実に奪う。

命名は最も安いドキュメントだ。コメントと違って必ず読まれ、かつ古びると即座に害になる。名前と実体を一致させ続けることは、新機能の追加と同じくらい保守性に効く。そして削除は必ず「呼ばれていない証拠」を確かめてから。この二つは、地味だが裏切らないリファクタの基本だと改めて思う。