はじめに
前回(②大原則編)で、設計の7つの大原則と、その親玉「変更に強くする」を腹落ちさせた私。いい感じに賢くなった気がして、調子に乗ってました。
特にDRY。「同じ知識は一箇所に」。これはもう完全に理解した。似たコードを見つけたら、まとめればいいんでしょ? 簡単じゃん。……と思っていたら、先輩に冷たい目で止められたのが前回のラスト。
:DRY守ってるだけなのに、なんでダメなんですか?
:その「似てたらまとめればいい」が、一番事故るやつやからや。
:……いや、意味わかんないっす。共通化しろって言うたやん。
:まあ聞け。お前みたいに「原則は知ってる、あとは当てはめるだけ」と思ってるやつが、一番きれいに転ぶんよ。
今回はこの 「線引き」 の話です。原則を知ってるのに、なぜか実務でうまく使えない——たぶんこのシリーズで一番、私が長いこと悩んできたところ。
原則を知ってても、使えない
正直に告白すると、私はエンジニア4年目で、ここに挙げた原則は全部「聞いたことある」レベルでは知ってました。SOLIDもDRYもYAGNIも。
でも、知ってるのに使えない。
- DRY:目の前の似たコード、これは共通化すべき? それとも放っておくべき?
- YAGNI:この機能、今作るべき? それとも「まだ要らない」?
- 抽象化:このクラス、インターフェース切るべき? やりすぎ?
全部、「どこで線を引くか」 がわからない。原則は知ってるのに、いざ手を動かすと毎回迷う。
そして、さっき先輩が言った通り、線引きをミスると「やらなさすぎ」より 「やりすぎ」の方が地獄 だったりします。良かれと思って共通化したコードが、いつの間にか全機能から参照される密結合の塊になって、一箇所いじると全部が連鎖崩壊する……ってやつ。
ミノ駆動さんの有名なクソコード動画「共通化の罠」が、まさにこれを的確に描いてて、初見で笑ったあと真顔になります。
「共通化、よかれと思ってやったのに、なんで地獄になるの?」── この感覚、心当たりある人多いんじゃないでしょうか。私はめちゃくちゃあります。
:先輩、これ僕の勉強不足ですよね…。原則の理解が浅いから、使いこなせないんだ。
:いや、ちゃう。それな、勉強不足ちゃうねん。線引きが難しいのには、ちゃんと構造的な理由があるんよ。
え、私のせいじゃない…?
なぜ線引きは「本質的に」難しいのか
先輩いわく、線引きが難しいのは努力や才能の問題じゃなくて、そもそもの構造が難しいからだそうです。理由は4つ。
理由1:文脈で正解が変わる
同じコードでも、状況次第で正解が逆になります。
:3日で捨てる試作品なら、多少汚くても動けば正解。10年使う基幹システムなら、最初から丁寧に設計するのが正解。同じコードでも、寿命や目的で答えがひっくり返るんよ。
「絶対の正解」が存在しないから、原則を機械的に当てはめても外れる。
理由2:原則同士がケンカする
前回「7つは全部地続き」と言いましたが、実務では原則同士がぶつかることもあります。
- DRY(重複を消せ)と YAGNI(先回りするな):「2回しか出てないけど、3回目が来そう。今まとめる?」
- 高凝集(きっちり分けろ)と シンプルさ:「完璧に分けるとファイルが爆発する」
:片方を立てると、もう片方が倒れる…
:そう。どれか1つに従うと、別のが破れるのが普通や。だから「全部守る」は無理で、毎回「今回はどっちを優先するか」を選ぶしかない。
前回「7つは全部同時には満たせない、どれをどこまで適用するかは毎回トレードオフ」と言いましたが、まさにこれです。
理由3:未来予測は、たいてい外れる
線引きの多くは「将来どうなるか」の予測に依存します。「この機能、将来拡張されるかな?」と。
:でもな、その「将来こうなるやろ」って予測、当たることのほうが少ないんよ。
:そんなに外れるんですか…!
:そう。未来を読んで打った手の大半は、的外れに終わる。だから「将来のため」の先回り設計は、そもそも分が悪い賭けなんよ。
理由4:答え合わせが、数ヶ月〜数年後に来る
これが一番きつい。設計判断が良かったか悪かったかは、変更が必要になった瞬間 に初めてわかります。
:「あの抽象化、正解やったな」とか「あれは過剰やったわ」って気づくの、リリースの数ヶ月後、下手したら数年後やろ。
:フィードバックが遅すぎて、学習できない…
:そういうことや。打った手の結果がすぐわからんから、上達しにくい。これが線引きが難しい、4つ目にして最大の理由や。
……というわけで、線引きが難しいのは私の能力のせいじゃなかった。構造的にムズいんです。ちょっと安心しました![]()
じゃあ、どうすればいいのか
:構造的に難しいのはわかりました。でも「難しいから諦めろ」じゃ困るんですよ。
:もちろん。完璧な正解は出せんでも、判断の質を上げるコツはある。代表的なやつを3つ教えたろ。
コツ① 3回ルール(Rule of Three)
DRYの線引きで一番実用的なやつ。「同じコードが何回出たら共通化するか」のヒューリスティックです。
- 1回目:とりあえず書く
- 2回目:コピペでOK(多少モヤッとしても我慢)
- 3回目:ここで初めて「共通化する?」を検討
:なんで2回目はコピペでいいんですか? もう重複してるのに。
:2回だけやと、それが 「本当に同じ知識」なのか「たまたま似てるだけ」なのか、判断できんからや。前回の住所の話、覚えてるか?
前回(②)のDRYで、「住所みたいに同じ"知識"があちこちに散らばってるのは、消すべき重複」という話をしました。でも厄介なのはその逆で、たまたま見た目が似てるだけのコードは、共通化すると事故ることがある。3回ルールは、この「本物の重複」と「似てるだけ」を見極めるための「待ち時間」なんですね。
:3回目が出てくると、「あ、これ毎回セットで出てくるな=同じ知識やな」ってパターンが見えてくる。逆に2回で慌ててまとめると、後で「やっぱ別物やった」ってなって剥がす羽目になる。
:早すぎる共通化は、むしろ損なんですね。
「早すぎる抽象化のコスト > 重複のコスト」
コツ② 迷ったら、具象に倒す
これも実務で効く指針です。迷ったら、共通化せずにベタ書きしておけ。
理由は、抽象化には 非対称性 があるから。
:ベタ書き(具象)から共通化(抽象)するのは、後からでも簡単や。3つ目が出てきたら、そのときまとめればええ。
:逆は?
:抽象から具象に戻すのは、地獄や。一回まとめたものを「やっぱバラそう」ってなったら、それに依存してる全部のコードを直して、テストも書き直し。やる方向とほどく方向で、重さが全然違うんよ。
具象 → 抽象(後でまとめる) :軽い。3つ目が出たらやればいい
抽象 → 具象(まとめたのを剥がす):重い。依存してる全部を直す羽目に
進む方向(具象→抽象)は軽くて、戻る方向(抽象→具象)は重い。だったら、軽い方に倒しておくのが安全。迷ったら具象、これだけで設計の事故がだいぶ減ります。
コツ③ 「戻すコスト」を毎回問う
3つ目は、前回の 可逆性 の実践版。判断する前に、毎回これを問います。
「この判断、もし間違ってたら、戻すのに何がかかる?」
:たとえばライブラリの採用。まだ依存が広がってへん早い段階なら、やめるのは軽い。でも全体に使い倒した後やと、引き剥がすのが地獄や。
:同じ「やめる」でも、タイミングで戻すコストが全然違う…
:そう。だから「正しいかどうか」だけやなくて、「間違えたとき、どっちが戻しやすいか」で選ぶ。戻しやすい方を選んどけば、判断ミスが致命傷にならん。
「絶対正しい選択」は選べなくても、「間違えても傷が浅い選択」なら選べる。これが不確実な中での現実的な戦い方です。
共通化、ありがちな2つの失敗
コツがわかったところで、逆に「やりがちな失敗」も知っておくと事故が減ります。2方向あります。
失敗1:「似てる」を「同じ」と勘違いする
前回ちらっと出した、この例。
calculateUserScore(user) { return user.points * 1.5; }
calculateProductScore(product) { return product.rating * 1.5; }
calculateOrderScore(order) { return order.amount * 1.5; }
3つとも * 1.5 してて、見た目はそっくり。「DRYだ!まとめよう!」と、こうしたくなる。
calculateScore(value) { return value * 1.5; }
:これな、やった瞬間は気持ちええ。でも後で詰む典型や。
:なんでですか? ちゃんと重複消せてますよ。
:「ユーザーのスコアだけ、係数を2.0に変えたい」って要件が来たらどうする? まとめたせいで、商品も注文も巻き添えになるやろ。
ここで効くのが、前回も出た判断基準です。
「この3つは、"同じ理由で"変更されるか?」
同じ仕様変更が来たら3つとも一緒に変わる、なら共通化してOK。でも「ユーザーのスコアだけ変えたい」が起こりうる=変わる理由が別々なら、たまたま今は似てるだけ。消しちゃダメ。
:見た目で判断すんな。「変わる理由が同じか」で判断せえ。これが単一責任原則(SRP)の本当の意味でもあるんよ。
失敗2:逆に、重複を放置しすぎる
「具象に倒せ」「3回ルール」を聞くと、今度は「じゃあ共通化しなきゃいいんだ」と振り切る人がいます。これも失敗。
:え、でも「迷ったら具象」って言いましたよね?
:迷ったらな。でも同じ知識が10箇所、20箇所に増えてもまだ放置、はやりすぎや。そこまで散らかってから直すと、リファクタのコストが膨らむ。
3回ルールは「早すぎる共通化を防ぐ」ためのもので、「永遠に共通化するな」じゃない。
:理想は、3〜5回くらいで「お、そろそろまとめどきやな」って 匂いを嗅ぎ取れる こと。早すぎず、遅すぎず。
:その嗅覚、どうやって鍛えるんですか?
いい質問です。そして、それが今回の結論に繋がります。
結論:線引き力は「言語化」で育つ
:結局、この「線引きの嗅覚」ってどうやったら身につくんですか? 近道は?
:残念ながら、近道はない。経験の量 × 振り返りの質、これでしか伸びん。本を読むだけじゃ絶対に身につかんネタや。
ただ、「振り返りの質」を上げる具体的な方法はあります。それが 判断の言語化 です。
設計判断するとき、頭の中だけで決めて終わりにせず、こう書き残す。
【検討した選択肢】
A. 共通化する B. 重複のまま残す C. 一部だけ共通化
【選んだ理由】
今は2箇所だが、関連機能で3箇所目が確実に出るのでA。
ただし実装時に再評価する余地を残す。
【切ったトレードオフ】
抽象化レイヤーが1段増える。将来仕様が分岐したときのリスクは残る。
:これをPRの説明とかADR(設計判断の記録)に残す癖をつけると、自分の判断パターンが見える化されるんよ。
:なんでそれが嗅覚に繋がるんですか?
:記録がなかったら、3ヶ月後に結果を見ても「当時何考えてたっけ」で終わりや。それやと答え合わせにならん。さっき言うた「答え合わせが遅すぎる」問題(理由4)やな。記録しとくから、遅れてきた答えを当時の判断と突き合わせて、ちゃんと学習に変えられるんよ。
書き残すことで、遅れてくる答え合わせを「あのとき何を考えてたか」と突き合わせられる。これが振り返りの質を上げ、線引きの嗅覚を育てる。地道だけど、これが王道なんですね。
まとめ
今回のポイント。
- 原則を知ってても線引きできないのは、勉強不足じゃなく構造的に難しいから(文脈依存/原則のトレードオフ/予測はたいてい外れる/答え合わせが遅い)
- 完璧な正解は出せないが、判断の質を上げるコツはある
- 3回ルール:3回出るまで共通化を待つ
- 迷ったら具象:抽象化は軽い、解体は重い。軽い方に倒す
- 戻すコストを問う:「間違えたら戻すのに何がかかる?」で選ぶ
- 共通化の失敗は2方向:「似てる」を「同じ」と勘違い/逆に放置しすぎ。 「変わる理由が同じか」 で判断
- 線引き力は 経験量 × 振り返りの質 でしか伸びない。判断の言語化(PR・ADR)が振り返りの質を上げる
線引きに「これさえやればOK」の魔法はない。でも、向き合い方の型はある。これを知ってるだけで、毎回の迷いが「意味のある迷い」に変わると思います。
で、次回
線引きの基本戦略が見えてきました。「迷ったら具象から始めて、共通項が見えてから抽象化する」。これで万事解決……かと思いきや、先輩が一言。
:……まあ、基本はそれでええ。でもな、「最初から」きっちり設計せなアカンものも、世の中にはあるで。
:え? 「迷ったら具象」じゃなかったんですか?
:それが通用せん領域があるんよ。後から変えると、死ぬほど痛いやつ。
:なんですかそれ、こわい。
:キーワードは 「契約」 や。……長くなるから、次回な。
次回は、「具象から始める」戦略が通用しない例外 ── 「契約」の設計 に踏み込みます。