DRYという名の問い ── 「なぜ同じものが二か所にあるのか」と自分に問い続けた一日

370行のファイルが28行になるまでに、何を問い、何を捨てたか。DRY原則とは技術論ではなく、設計者の思考習慣だと気づいた記録。

この記事で言いたいこと:DRY原則(Don't Repeat Yourself)は、「同じコードを書くな」という戒律ではない。「なぜ同じものが二か所にあるのか」と問い続ける姿勢のことだ。その問いを諦めたとき、重複は正当化され、設計は静かに腐敗していく。

1. 370行のファイルが6テナント分、積み上がっていた

あるとき、手元のリポジトリにある複数のディレクトリを眺めていて、ぞっとした。それぞれのディレクトリにindex.htmlがあり、開いてみると全部で370行。中身はほぼ同じだった。テナントごとに異なるのはアプリ名とテーマカラーとスラグ、合わせて18行だけ。残りの352行は完全に同一だった。

テナントが6つあるので、合計2,220行のHTMLが存在していた。そのうち108行だけが意味のある差異で、残りの2,112行は複製だった。

誰かが意図してこうしたわけではない。機能追加のたびにコピーして差し替えた。緊急の修正が入るたびに全件に同じ変更を手で適用した。そうやって積み上がった結果だった。

2. 「とりあえず動く」が「静かな負債」に変わる瞬間

この構造の何が問題か。動いているうちはわからない。テナントが2、3件のうちは、修正を複数箇所に適用するコストも許容範囲に見える。「とりあえず動く」が優先される場面では、この判断は正しいことすらある。

問題が顕在化するのは、修正が発生したときだ。UIにバグが見つかる。デザインを変えたくなる。新しい機能を追加したい。そのたびに「全テナント分」という修飾語が頭につく。6件ならまだいい。60件になったとき、600件になったとき、人は気づく。これは設計ではなく、複製の管理をしていたのだと。

DRYの違反は、最初は小さな不快感として現れる。「また同じ変更をしている」という感覚。それを無視し続けると、コードベースに静かな負債が積み上がる。気づいたときには、直すコストが作ったコストを超えている。

3. 問いの立て方が解決策を決める

「どうすれば全テナントに一括で変更を反映できるか」という問いと、「なぜ同じものが複数箇所に存在しているのか」という問いは、同じ問題を見ているようで、まったく異なる解に向かう。

前者は複製の管理を効率化しようとする。自動同期のスクリプトを書き、CIで全テナントのHTMLを一括更新するワークフローを組む。これは問題を「解決」しているように見えて、実は複製という構造を正当化している。

後者は複製そのものを問う。「テナントごとに異なるのは本当に18行だけか。ならばその18行だけをファイルに書き、残りはどこか一か所に持てないか」。この問いから出発すると、設計が根本から変わる。

問いの質が、解の質を決める。
「どうするか」より「なぜそうなっているのか」を先に問え。

4. 制約が設計を壊していた

ではなぜ最初から28行にしなかったのか。技術的な制約があった。複数のPWA(Progressive Web App)をホーム画面に独立してインストールさせるには、それぞれが異なるURLパスを持つ必要がある。同一のパス配下に置いた場合、OSは「同一アプリ」と判断してしまう。

この制約への最初の回答が「テナントごとに別リポジトリを用意する」という設計だった。別リポジトリにすれば別の静的ホスティングとしてデプロイされ、完全に独立したURLになる。マルチPWAの要件は満たせる。

しかし「別リポジトリ=別ファイル」という思い込みが生まれた。それがHTMLのコピーを生み、コピーが複製を生み、複製が管理コストを生んだ。制約に対応するために取った手段が、別の問題を引き起こしていた。

5. Service Workerという解法

解決の糸口は、ブラウザのService Workerにあった。PWAにおいて、ナビゲーション(URLへのアクセス)はService Workerが介在できる。テナントのURLにアクセスがあったとき、Service Workerが別の場所にあるHTMLを返すことができる。

これを使えば、テナントのindex.htmlはPWAのスコープを確立するための「入口」として機能するだけでいい。実際のUI(DOM、JavaScript)は一か所にある本体から読み込む。テナントファイルが保持すべき情報は、アプリ名・テーマカラー・スラグの18行だけになる。

UIの唯一の真実:/hello-briefing/index.html
ロジックの唯一の真実:/hello-briefing/app.js
テナントファイルが持つべきもの:それ以外のすべて、ではなく、それ以外だけが知っていること。

Service Workerは4行のシェルになった。中身は「本体のsw.jsを読み込め」という1行だけだ。バグが出たとき直すのは本体の1ファイルだけ。テナントが100件になっても、変えるファイルは1つだ。

6. DRYの本質は「知識の単一化」だ

DRYは"Don't Repeat Yourself"の略だが、その本質は「コードを繰り返すな」ではない。原典では「すべての知識はシステム内で単一の、曖昧さのない、権威ある表現を持つべきだ」と定義されている。

「UIはここにある」「ロジックはここにある」「このテナントのアプリ名はこれだ」──それぞれの知識が、システムの中でただ一か所にしか存在しない状態。それがDRYの目指す姿だ。

コードの重複は症状であって、病因ではない。病因は「知識が複数の場所に分散していること」だ。だから複製を機械的に排除してもDRYにはならない。「この知識の権威ある場所はどこか」を問わなければ、形だけ変えて問題は残り続ける。

7. 「ダサい」という感覚を信頼する

技術的な議論の中で、「走査して差し替えるのはなんかダサいよな」という言葉が出た。これは重要なシグナルだ。「ダサい」という感覚は、多くの場合DRY違反を指し示している。

「また同じことをしている」「なんか回りくどい」「この変更、全部に反映しないといけないのか」──こういった違和感は、設計が何かを見落としているサインだ。それを「仕方ない」と流すか、「なぜそう感じるのか」と問い直すかで、設計の質が分かれる。

経験を積むほど、この「ダサい」という感覚は精度が上がる。それはコードの美醜ではなく、知識の重複を嗅ぎ取る鼻だ。信頼していい。

8. 問いを諦めないことが、設計を守る

今日、370行のファイルは28行になった。Service Workerは4行になった。テナントが100件に増えても、UIを変えるときに触るファイルは1つだ。この状態に名前をつけるなら「唯一の真実」だ。変更は一か所に集まり、そこから全体に伝播する。

しかしこれは、最初から見えていたわけではない。「動いているからいい」「今は急ぎだから」という判断が積み重なって複製が生まれた。それを「なぜ同じものが二か所にあるのか」という問いが崩した。

DRYは戒律ではない。問いだ。完璧なDRYは存在しないし、すべての重複を排除すべきでもない。だが「なぜここに同じものがあるのか」という問いを持ち続けること──その習慣が、設計を少しずつ正直にしていく。

DRYとは、コードを書かないことではなく、「なぜ同じものが二か所にあるのか」という問いを諦めないことだ。

トキストレージは「存在証明の民主化」をミッションに、声・記憶・記録を1000年残すデジタルインフラを構築しています。設計の思想を、プロダクトに宿らせることを目指しています。

トキストレージを知る すべてのエッセイを読む