← AIバイブコーディングTips 一覧へ Architecture Decision / DRY vs Reproducibility

あえて共通化しない、を選んだ

複数の動画プロジェクトで使う共通の図解コンポーネントを切り出すとき、「各プロジェクトから直接import」ではなく「正本から都度コピーする」方式をあえて選んだ。DRY原則には反するが、過去に完成させた動画の再現性を壊さないための判断だった。

この記事で手に入るもの

「共通化する」と「過去の成果物を凍結する」が両立しない具体的な場面と、実際に使っているコピー用スクリプトの中身。そして、DRY原則に反する選択をするときに何をコードへ書き残しておくべきかのチェックリスト。

01 — 何が起きたか

「絵になっていない」という判定から始まった

姉妹メディアSURPLUS(経済学の読み物サイト)向けの解説動画をレビューしていて、ある動画の全53フレームを見直したところ、実体はテキストカードの並べ替えにすぎないレイアウトが41、本当に「絵になっていた」のは散布図と分布図の2枚だけだった。数字や条件を文章のまま画面に出しているだけで、図解として機能していなかったということになる。

そこで、散布図・折れ線・時間軸マップ・分布図といった、複数の動画プロジェクトで繰り返し使えそうな図解コンポーネントをまとめて作り、共通フォルダ(_shared/)へ切り出すことにした。ここで、単なるコード整理では済まない設計判断が1つ必要になった——各プロジェクトに、この共通コンポーネントをどう使わせるか、という問題である。


02 — 選択肢は2つあった

直接importか、コピーか

動画は個別のRemotionプロジェクトとして作られており、それぞれ独立したソースツリーを持つ。共通コンポーネントの持たせ方には、素直な案とやや遠回りな案の2つがあった。

01

直接import — 素直な共通化

共通フォルダを各プロジェクトのソースから直接参照する。DRY原則そのもので、コンポーネントを1箇所直せば全プロジェクトに即座に反映される。実装としてはこちらが自然だった。

02

正本からコピー — 都度複製

共通フォルダを「正本」とし、各プロジェクトは使うタイミングでファイルをコピーして自分のソースツリーへ取り込む。取り込んだ後は、正本側が更新されても自動では追従しない。

ここで選んだのは、あえて02の方。「共通化」という目的だけを見れば01が正解に見える。だが、この選択には見落としがちな副作用があった。


03 — なぜコピーを選んだか

過去の動画の"証拠"を壊してしまう

それぞれの動画プロジェクトは、完成後に出力したMP4のSHA-256(内容が変わっていないことを確認できるハッシュ値)をASSET_LEDGERという台帳に記録している。これは「この動画は、この構成・このアセットで確定した」という証跡であり、後から中身がすり替わっていないかを検証するための仕組みだ。

ここで直接importを選んでいたらどうなるか。共通コンポーネント側を1行直しただけで、過去に完成させ、ハッシュまで記録して"確定済み"としたはずの動画の再レンダー結果が変わってしまう。数ヶ月前に検品を終え、最終成果物として扱っていたものが、無関係な別プロジェクトの改修によって静かに書き換わる状態になる。これはASSET_LEDGERが保証しようとしていたもの——「このハッシュの動画は、この時点でこの通りに存在した」という記録そのものを無効化してしまう。

共通化(DRY)と、過去の成果物を凍結することは、両立しない場合がある。今回はこの2つが正面から衝突する場面で、後者——検証可能性のほうを優先した。


04 — 実際の運用

コピー用スクリプトが担っているもの

コピーは手作業ではなく、sync_shared.ps1という小さなスクリプトに任せている。中身は「正本フォルダの.ts/.tsxを、指定したプロジェクトのsrc\shared\へ上書きコピーする」だけのものだが、冒頭のコメントに選択の理由をそのまま書き残してある。

# _shared の正本を各 Remotion プロジェクトの src\shared\ へコピーする。
#
# なぜコピーなのか:
#   各プロジェクトは独立した node_modules を持ち、出力 MP4 の SHA-256 を
#   ASSET_LEDGER に記録している。共有フォルダを直接 import すると、
#   _shared を直した瞬間に過去作の再レンダー結果が変わってしまう。
#   コピーしておけば、各プロジェクトは自分が使ったバージョンを抱えたまま凍る。

使い方も単純で、更新したいプロジェクトを名指しするか、既にコピー済みの全プロジェクトを一括更新するかを選べる。-WhatIfを付ければ、実際に上書きする前に差分だけを確認できる。

つまり「更新したい」と明示的に指示したプロジェクトだけが最新の共通コンポーネントを受け取り、それ以外は自分が過去に取り込んだバージョンのまま止まり続ける。共通化の恩恵(新しい図解パターンを使いたいときはコピーし直せばよい)と、過去の成果物の凍結(何もしなければ何も変わらない)を、両立させる設計になっている。


05 — 自分の運用に当てはめる

持ち帰れるチェックリスト

「共通化すべきだ」という判断は、多くの場面で正しい。ただし、それが常に正しいとは限らない場面があるという話でもある。

「共通化」と「凍結」が衝突していないかをまず疑う過去の成果物にハッシュ・承認記録・公開日など「確定した」ことを示す証跡がある場合、それを後から書き換えてしまわないかを先に考える。
あえて非DRYを選ぶなら、理由をコードに書き残す「なぜコピー方式なのか」をコメントに残しておかないと、後で見た自分や別のAIエージェントが「これは直すべき冗長設計だ」と誤って"最適化"してしまいかねない。
全部を同じルールで縛らなくてよいまだ改修が続く進行中のプロジェクトは直接参照、完成して確定したものはコピーで凍結、というように対象を分けて判断する余地も検討する。