📖 はじめに — 理論は知っている。では「動いている 45 page」をどうするか
開発手法ガイドの Atomic Design は分類の理論を扱った。本章はその続きではなく、理論どおりに作れなかった現実を、動いているアプリのまま作り直した実録である。
CTS-EC のフロントエンド(Angular + Nx モノレポ)は、Smart/Dumb 分離の参照実装が home ページ 1 件だけ存在し、残り 45 page は未適用という状態だった。ページコンポーネントに DI・表示ロジック・HTML が同居し、Storybook 検収もできない。序章で述べたとおり、AI コーディングはこの種の「動くが構造が崩れたコード」を高速に量産する — そして放置すれば、この上にさらに 180 page が増築される予定だった。
フロントエンドの完成度はまだ 20%。つまり「本番前の今が、レールを敷き直す最後のチャンス」だった。
⚖️ 決定 1: 全 45 page を分離する(部分適用の誘惑を断つ)
最初の分岐は「どこまでやるか」だ。検討した 4 案を、ステアリングの決定ログからそのまま示す。
| 案 | 内容 | コスト | 効果 |
|---|---|---|---|
| A | 全 45 page を分離(採用) | 約 406〜657h | 負債ゼロ |
| B | 重要 page のみ(5〜8 件)分離 + 規約緩和 | 約 100〜200h | 部分的解消 |
| C | home を戻す(規約撤退) | 約 1h | Storybook 検収の放棄 |
| D | 新規 page のみ分離を強制 | 仕組みのみ | 既存 45 page の負債は残る |
案 D は一見合理的だ。「触るときに直す」は現場の定番でもある。だが 180 page 増を控えた時点でこれを選ぶと、規約に「例外ゾーン」が恒久化する。新規実装者(人間も AI も)は例外ゾーンを見て学習するので、レールは敷いた瞬間から侵食される。バックエンドで先行した負債一掃(BC 境界の機械ガード・第3章)と同じ思想で、例外ゼロの状態を作ってから機械ガードで固定する案 A を選んだ。
💡 決定 2: 「リファクタしない」— 新規移植アプローチ
本章の最大の輸出品がこれだ。既存 page を分離パターンへ「リファクタ」する案と比較した結果:
| 案 | アプローチ | 工数(45 page) | リスク |
|---|---|---|---|
| A | 既存ファイルを編集して分離 | 548〜938h | 高(責務再判定・テスト分割・HTML イベント全書換) |
| B | 退避 → generator → 移植(採用) | 336〜552h(約 40% 減) | 低(並行稼働・ロールバック容易) |
なぜリファクタより「作り直し」が安く、安全なのか。
- HTML イベント binding の全置換が不要 — 最初から output で書くので、既存
(click)="service.save()"を一つずつ剥がす作業が消える - spec 分割の頭の体操が不要 — 最初から container / presentational 別ファイルでテストを書く
- 旧コードが
.legacy/にそのまま残る — 移植中も並行稼働でき、参照元(写経元)として常に手元にあり、ミスってもロールバックは git mv を戻すだけ - TDD サイクルが綺麗 — 旧 spec を新 spec に移植した時点で Red、実装移植で Green。「リファクタ」ではなく「写経 with 参照」になる
「動いているコードを変形する」から「動いているコードを答えとして横に置き、正しい構造で書き直す」への転換。これは AI 実装との相性も良い — AI にとって「既存の絡まった責務を保ったまま分解する」より「雛形 + 参照実装 + 写経元」の方が、圧倒的に失敗しにくいタスクだからだ。
🏗️ 到達点の構造 — Container は配線だけ、HTML は libs/ui だけ
移植後の到達点がこの構造だ(これを固定する機械ゲートは次節)。

- Container(apps 側): DI・Route・配線のみ。HTML/CSS を持たず、inline template は
<ui-{name}-page [input]="..." (output)="..." />の 1 行 - Presentational(libs/ui 側): HTML/CSS を所有し、input/output のみで外界と接続。DI なし。だから Storybook でそのまま検収できる
- 命名はサフィックスなしの home パターン(
home.ts/HomeComponent/app-home)。配置場所で責務が分かるため、-containerサフィックスは認知負荷にしかならない、という判断
雛形は Nx generator @cts-ec/page が生成する(presentational 5 ファイル + container 2 ファイル + barrel 更新)。手作業なら 1 page あたり 1〜2h かかるボイラープレートが消え、45 page で 45〜90h の節約。それ以上に大きいのは、命名規則違反や import 漏れという「雛形のミス」が構造的に起きなくなることだ。180 page の増築を人間と AI が混在で進める以上、規約は generator に焼き込まないと形骸化する。
🔒 完了基準は grep — 「分離できた気がする」を排除する
「負債ゼロ」を主観レビューで判定すると、ゼロは維持できない。完了基準はすべて grep / find の機械検証にした。
| ゲート | 検証 | 完了値 |
|---|---|---|
| AT-1 | grep -rln "inject(.*Service|inject(.*Store" apps/pages/ | 0 |
| AT-7 | find frontend -name "*.component.ts"(Angular 旧命名) | 0 |
| AT-8 | find frontend -name "*.directive.ts" | 0 |
| AT-10 | find frontend/apps -name "*.service.ts" | 0 |
| AT-18 | ダークモード違反([class.X] の dark ペア欠落をファイル単位で検出) | 0 |
実行は CI + pre-push hook の二段。CI だけだと「push してから落ちる」ラウンドトリップが発生するので、ローカルの pre-push で先に止める。バックエンドのアーキテクチャテストと同思想の、フロントエンド版決定論ゲートである。
細部の学びも 1 つ。AT-18(ダークモード)は当初「同一行 grep」で書いたが、Angular の property binding は属性が複数行に割れるため誤検出が出た。ファイル単位で dark ペアの存在を検査する方式に切り替えて安定した。機械ゲートは書いて終わりではなく、誤検出と戦って初めて「常時 enforce」できる。
📊 結果 — 5 日間・134 タスク・正味 −12,000 行
| 項目 | 値 |
|---|---|
| 移植完了 page | 47(当初 45 + 分割で増えた分) |
| 期間 / タスク | 5 日間 / 134 タスク・コミット 16 件 |
| 削除した legacy | 118 ファイル / 約 43,500 行 |
| 正味の行数増減 | −12,000 行(追加 約 35,000 / 削除 約 47,000) |
| テスト | 約 4,800 → 5,462 件 PASS(+662) |
| CI ゲート | 5 → 9 ゲート常時 enforce |
| 旧命名(.component.ts ほか) | 全廃(0 件) |
「作り直したのに正味 −12,000 行」が新規移植アプローチの成績表だ。リファクタで同じ到達点に行くより 4 割安く、しかもテストは増え、ゲートは倍増している。
面白い副次効果もあった。旧命名一掃フェーズ(17 タスク)に着手したら、9 件はすでに片付いていた — 各 page の移植時に generator が新命名で生成するので、自然に消化されていたのだ。レールを先に敷くと、負債の一部は「意識して返す」前に消える。
🧠 移植で得た教訓 — 巨大 page と State 二重保持
順風満帆ではない。実録として重要なのはむしろここだ。
巨大 page は「事前分解設計」なしで触らない
最難関は product-detail(1,569 行・297 テスト)と staff-list(804 行・2 つの管理者 view を内包)。これらは移植前に専用の設計セッション(計 9 件)で input/output 設計を確定してから着手した。結果、移植中の手戻りはほぼゼロ。「巨大なものは、動かす前に紙の上で割る」 — 写経方式でも、写経元が絡まっていれば設計は必要だ。staff-list は責務の異なる 2 page への分割で各 400 行前後に落とした。
State 二重保持 — 最終レビューで見つかった設計バグ
完了直前の並列レビュー(3 エージェント)で重大な問題が 2 件出た。
- product-detail: Container が 21 個の linkedSignal を独立保持していたが、Presentational から変更通知の output が出ておらず、未保存変更ガードが実質機能していなかった。Presentational に
hasChangesChangeoutput を追加して根治 - store-api-settings: 「Dialog 状態は Presentational 内部完結」と設計書に明記したのに、Container 側にも同名 signal が並立していた。save output に必要データを全部載せて一本化
教訓は明快で、「Container と Presentational に同名 signal があったら設計バグ」。分離パターンの落とし穴は DI 混入のような分かりやすい違反ではなく、この「両側で同じ状態を持ってしまう」静かな二重化にある。以後、レビューチェックリストに恒久追加した。
as unknown as キャストは設計矛盾のシグナル
移植中に型キャストが 8 箇所蓄積していた。Store のメソッドシグネチャ不整合を「とりあえずキャストで通す」で放置した結果だ。振り返りの結論は「キャストを書いた瞬間が、設計矛盾に気づいた瞬間。その場で直すか、直せないなら起票する」。AI 実装は型エラーをキャストで黙らせる誘惑に強くない。だからこれも人の規律でなくレビュー観点として明文化した。
🧹 続編: 部品階層の「純化」— 60 部品を参照数で仕分ける
47 page の分離が終わると、次の負債が見えた。分離の過程で Dumb 側の部品が organisms に流れ込み、「1 つの page からしか参照されない部品」が部品階層を汚染していたのだ。
続編ステアリングでは、全 atoms / molecules / organisms 60 件の参照元 page 数を機械集計した(selector を grep で抽出し、参照 page 数を数えるだけ — ここでも定量が先)。
| 階層 | 参照 0 件 | 1 件 | 2 件以上 | 合計 |
|---|---|---|---|---|
| atoms | 6 | 1 | 6 | 13 |
| molecules | 7 | 2 | 3 | 12 |
| organisms | 15 | 18 | 2 | 35 |
| 合計 | 28 | 21 | 11 | 60 |
60 部品のうち、2 page 以上から参照される「本物の再利用部品」は 11 件しかなかった。そこでルールを反転させた — 「部品階層に置けるのは 2 page 以上から参照されるものだけ」。1 page 専用の部品は pages/{name}/parts/ へ移送し、atoms/molecules へのドメイン語混入(mall-category-row など 3 件)も禁止。それぞれ AT-19 / AT-20 として grep ゲート化した。
Atomic Design の分類論争(これは molecule か organism か)は不毛になりがちだが、「参照 page 数」という機械的な基準に置き換えると、議論は集計に変わる。これがこの続編の一番の発明だと思う。
🧾 まとめ — 第I部の「型」がここにも
- 動いている 45+ page の構造改革は、リファクタでなく新規移植(退避 → generator → 写経) が安くて安全 — 工数 40% 減・正味 −12,000 行
- 完了基準は grep / find の機械検証 9 ゲートで固定し、CI + pre-push で常時 enforce
- 巨大 page は事前分解設計、同名 signal の二重保持はレビュー観点に、キャストは設計矛盾のシグナル
- 部品階層は「2 page 以上参照」の数値基準で純化 — 分類論争を集計に変える
序章の言葉に戻れば、LLM コーディングは増幅だ。この章で敷いたレール(generator + 9 ゲート)の上では、180 page の増築を AI に任せても Atomic Design は崩れない — 崩れたら push が止まる。次章は同じフロントエンドの品質を「実機」で検証する話、Storybook × a11y 実機編へ。
📚 シリーズ記事(ステアリング駆動開発・実践編)
序章
第I部: 技術的負債ドメイン別の実録(総点検ガイド + 8 ドメイン・全 9 回)
- リファクタリング総点検ガイド — 7 つの観点と進め方
- DDD/SOLID/BC 編 — god class 一掃と境界の機械ガード
- SAGA 編 — 新アーキテクチャ挑戦と再発ゲート
- Atomic Design リファクタ編(本記事)
- Storybook × a11y 実機編
- テスト品質・網羅性編
- Python ジョブ群編
- パフォーマンス編
- セキュリティ編
第II部: 検証エンジン(クロスファミリー検証・全 6 回)
- 総論 — マルチ LLM の 2 系統と見取り図
- 裏取り編 — +18.1pt 論文の検証
- 設計編 — finder/verifier 分業と逆順禁止
- TDD×ハーネス編 — テスト保護と三層ゲート
- 実装編 — Claude Code の中から Codex を動かす
- モデル戦略編 — ティア割当と「買うか組むか」
終章
- クロスファミリー検証の最終型 —「正しく作る」から「自分で正しさを測り直す」へ — 検証エンジンに「計測」の層をはめ、自己修正ループを閉じる
番外編
- CI テスト 29 分 → 5.6 分 — 検証エンジンの平時運用実録 — 実測が有力仮説を殺し、退行前より速くなった 1 日
- GPT-5.6 sol 切替の当日実録 — もう一つのデフォルト追従 — CLI 更新が黙って替える finder と、当日中の再計測
- 遊休 90% の枠に仕事を振る — Codex 上流調査と verifier バッチ化 — 逆順禁止の境界を ADR で確定し、読む仕事を遊休枠へ移した 1 日
- ハーネスが単一プロジェクトを卒業する — プラグイン化と二層配布 — 4 プロジェクト展開への切り出しと、version + SHA ピンによる非強制追従
- 69分で5リリース — Opus 5 当日対応が暴いた共通ハーネスの死角 — 新モデル対応を起点に、配布・文書・CI の回帰を二消費者で検出した 69 分
関連記事
- Atomic Design(理論編) — 分類の基礎
- ステアリング駆動開発とは — 本実践の方法論基盤