TORIAI は、鋼材の 1D 切断計画を Web で作る B2B SaaS です。計算エンジンは育っていましたが、育った分だけ「同じ意味の式が複数の path にある」という負債も増えていました。
今回の refactor は、性能を上げるためではありません。端部ロス、刃厚、使用長、ロス長、K=1 fast path の式を helper に寄せ、古い override 構造が calc 実行を不安定にする余地を減らす作業でした。PR #30 の記録を、外に出せる範囲でまとめます。
なぜ refactor したか
きっかけは、2 件の不具合レビューでした。
1 つ目は、まとめ切り候補の sort が「パターン単体の歩留まり」を見ていて、「プラン全体の歩留まり」を見ていなかったことです。局所的にはきれいでも、全体では残材を捨てる plan が上位に来る可能性がありました。
2 つ目は、K=1 fast path と一般 path で端部ロスの扱いがずれていたことです。同じ入力でも、部材数が多く fast path に入ると結果が変わる。B2B の計算 SaaS ではかなり危険です。
さらに調べると、algorithmV2 が repeatPlans を後段で上書きし、algorithmV3 / algorithmV4_k1fast が calcCore 付近で似た式を持つ構造になっていました。全部を一気に作り直すのは危ない。そこで、override 構造は残しつつ、中の式だけを共通 helper に寄せました。
refactor の進め方
最初にやったのは grep です。endLoss、blade、loss、patYld、yld、findRepeatPlans を横断検索し、「同じ意味なのに別実装」の場所を洗いました。
次に、影響範囲を限定しました。候補集合、最適化 strategy、repeat 優先の sort 方針、worker bundle の作り方は変えません。動作を変える refactor ではなく、同じ動作を同じ式から出す refactor にしました。
そのうえで、regression test を厚くしました。K=1 fast path と一般 path の一致、worker bundle と直接 load の一致、古い hidden input が stale でも表示系が壊れないことを固定します。最後に段階的 commit と PR にして、AI 2 体の review gate を通しました。
具体例
-
barMetrics.jsに helper を集約しました。calcEffectiveLength、calcPatternSize、calcRawBarLoss、calcBarLoss、calcPiecesPerBarなどを 1 箇所に寄せ、「母材長から端部ロスを引く」「刃厚を足す」「ロス長を出す」という基本式を共有しました。 -
repeat plan の sort policy を
compareRepeatPlansにしました。repeatPlans.jsとalgorithmV2.jsにあった sort 重複を排除し、現行のrepeat desc -> yld descは変更しませんでした。yld第一キー化は仕様変更なので、別タスクに残しました。 -
K=1 fast path と一般 path の式を揃えました。
calcPiecesPerBarでfloor((stock - endLoss + blade) / (piece + blade))の意味を共通化し、blade = 0 / 12、endLoss = 0 / 300、混合定尺の case で fast/general の summary 一致を test しました。 -
古い override 構造は一気に消しませんでした。読み込み順や worker bundle に波及するためです。その代わり、
algorithmV3などは helper があれば helper を使い、なければ local fallback する形にして、中の二重定義だけを減らしました。 -
worker bundle parity test を追加しました。直接 load した
calcCoreとsrc/wasm/worker_bundle.js経由の結果を同じ input で比較します。generated artifact を持つ構成では、ここがずれると production だけ違う結果になります。 -
stale
#endlossfallback を regression にしました。過去互換の hidden input は撤去しませんでしたが、古い値が残っても切断図や print/cart fallback が crash しないことを固定しました。 -
A2 cross-repeat policy fixture を追加しました。「repeat 優先」という現在の仕様を test に残したので、将来
yld第一キーへ変えるなら、この test が意図的に動くはずです。
学び
今回一番効いたのは、AI 2 体の review gate でした。片方が修正し、もう片方が「構造として危ない場所」を WARN にする。そこで出た WARN を次の PR で回収する。この流れにすると、AI の速度をそのまま production に流し込まずに済みます。
過去には、設計確定前に UI を連続修正して 12 commit 迷走したこともありました。その反省から、今回は grep、影響範囲調査、regression test、段階的 commit の順にしました。計算 engine は、勢いで直す場所ではありません。
refactor は、目に見える機能を増やしません。ただ、B2B SaaS では「同じ入力なら同じ結果が出る」「印刷や worker でも同じ結果が出る」こと自体が機能です。helper 集約は、その土台を少しだけ硬くする作業でした。
まとめ
計算エンジンの refactor でやったことは、派手な最適化ではなく、重複 helper の集約、古い override の影響封じ、cross-path test の拡充です。production の負債整理では、削る勇気よりも、まず壊れ方を test に閉じ込めるほうが効く場面があります。
TORIAI は公開 repo で開発しています。鋼材切断計画、1D 最適化、B2B SaaS の実装に興味があれば、コードも見てください。
- TORIAI: https://toriai.app
この記事は Claude (Anthropic) と Codex (OpenAI) が下書き、最終的な公開内容は handle haganedev が確認予定です。記事中の数字 / コード / 設計の裏取りは AI / 公開 repo の commit から確認できます。