1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI時代に"設計"と向き合ってみる ②|DRY・YAGNI・DIP…結局ぜんぶ同じこと言ってた

1
Last updated at Posted at 2026-06-17

はじめに

前回(①地図編)で、設計という言葉が広すぎて混乱してた私は、「縦の地図(スコープのレベル)」と「横の地図(用語の分類)」という2枚の地図を手に入れました。

でも記事の最後で、新しい絶望が待ってたんですよね。

:sweat_smile::先輩、これレベルも種類もこんなにあったら、全部ひとつずつ勉強しなきゃいけないんですか…? さすがに無理なんですけど:sob:

:sunglasses::あー、安心せえ。実はな、全部のレベル・全部の領域に共通して効く「大原則」 ってのがあるんよ。

:sweat_smile::え、そんな都合のいいものが…? ほんとですか?

ほんとでした。今回はその 「設計の大原則」 の話です。

そんな都合のいいものが、あった

調べてみると、設計の世界には 何十年も語り継がれてる、抽象度を問わず効く原則 がいくつかあるらしい。Lv1のコードでもLv5のシステム設計でも、同じように効くやつ。

:sunglasses::お前が地図で見た DDDだのSOLIDだの、ああいう個別の知識な。あれの理解が早くなるのは、根っこにこの大原則があるからや。先に根っこを押さえる方が、結局は近道なんよ。

なるほど。じゃあ、その大原則とやらを見せてくれと。

まず、7つ全部の名前だけ並べる

先輩が出してきた大原則は、7つ。とりあえず名前だけバサッといきます。

# 原則 ざっくり一言
1 関心の分離(SoC) 違うことをするやつは、違う場所に置け
2 低結合・高凝集 仲間は近くに、他人は遠くに
3 依存性逆転(DIP) 変わりやすいものに、変わりにくいものを依存させるな
4 YAGNI 今いらんものは作るな
5 DRY 同じ知識を2箇所に置くな(ただし条件あり)
6 可逆性 後で戻せる方を選べ
7 認知負荷低減 読む人の脳みそを使わせるな

:sweat_smile::うっ…7つもあるじゃないですか。結局これ全部覚えなきゃいけないんでしょ? 地図のときと同じで、また暗記ゲーが始まる予感:sob:

:sunglasses::待て待て。お前のその「全部バラバラに暗記しよう」とする癖、それが一番アカン。

:sweat_smile::え?

:sunglasses::この7つな、ぜんぶ同じことを言ってるんよ。

…は?

種明かし:7つは、ぜんぶ「変更に強くする」の手段

先輩いわく、7つの原則の上には、それを束ねる たった一つのゴール があるそうです。それが、

「変更に強いソフトウェアを作る」

これだけ。

software.png

なんで「変更」が主役なのか。先輩の説明はこうでした。

:sunglasses::ソフトウェアって、作って終わりちゃうやろ。リリースしてからも、仕様変更、機能追加、バグ修正…要件は必ず変わる。生き物みたいなもんや。

:sweat_smile::たしかに、一回も変更されないコードなんて見たことない。

:sunglasses::そう。だから「最初から完璧で、二度といじらない設計」なんて存在せんし、目指してもムダ。本当に目指すべきは、変化が来たときに柔軟に対応できる構造。これが設計の本当のゴールなんよ。

ここで7つを見返すと、全部この一点に向かってるのがわかります。

  • 関心が混ざってないから、一箇所の変更が他に波及しない
  • 結合が低いから、ある部品を変えても他が壊れない
  • 変わりやすい技術に依存してないから、技術を変えても本質が無傷
  • 今いらんものを作ってないから、変更時に邪魔な負債がない
  • 重複がないから、変更が一箇所で済む
  • 可逆だから、間違えても戻せる
  • 認知負荷が低いから、変更時に安全に手を入れられる

:sweat_smile::あ、ほんとだ。全部「変えやすさ」の話してる…!

:sunglasses::やろ? だから7つをバラバラに覚える必要はない。「これは"変えやすくする"ためにどう効くんやっけ?」 って親玉から考えれば、全部自然に繋がる。

ちなみにこの7つ、全部同時には満たせないんですよね。どれをどこまで適用するかは毎回トレードオフです。そのトレードオフの中身は、ほぼ全部 「今の楽さ」と「将来の変えやすさ」のバランス取り だったりします。大原則、ぜんぶ地続きなんですね。

原則①:低結合・高凝集

ここからは、7つの原則を一つずつ、例え話で腹落ちさせていきます。まずは 低結合・高凝集 から。

実は私、前から「結合度はなんとなくわかるけど、凝集度ってイメージ湧かないな」と思ってたんですよね。先輩に聞いてみました。

:sweat_smile::先輩、凝集度ってなんですか。「高い方がいい」って言われても、ピンとこなくて。

:sunglasses::そうやな。例えるなら、冷蔵庫の整理や。

:sweat_smile::冷蔵庫?

先輩の冷蔵庫トークが始まりました。

:sunglasses::凝集度が低い冷蔵庫ってのはな、一段の棚に「牛乳」「ネジ」「電池」「醤油」がごちゃ混ぜに入ってる状態や。

:sweat_smile::それはもう冷蔵庫じゃない。

:sunglasses::逆に凝集度が高いのは、「調味料の段」「飲み物の段」「野菜室」ってちゃんとテーマで分かれてる状態。同じ目的のものが、同じ場所にまとまってる。これが高凝集や。

コードで言うと、こういうこと。

// 凝集度が低い(なんでも屋クラス)
class UserManager {
  createUser() { ... }
  sendEmail() { ... }        // メール送信?
  generatePdfReport() { ... } // PDF生成??
  calculateTax() { ... }      // 税金計算???
}

「User」を名乗ってるのに、メールもPDFも税金も詰め込まれてる。さっきの「ネジが入った冷蔵庫」です。これを、それぞれの正しい家に引っ越しさせると、

// 凝集度が高い(テーマで分かれてる)
class UserService {        // ユーザー操作だけ
  createUser() { ... }
  updateUser() { ... }
  deleteUser() { ... }
}

class EmailService {       // メールだけ
  sendWelcomeEmail() { ... }
  sendPasswordReset() { ... }
}

class PdfReportService {   // PDF生成だけ
  generateUserReport() { ... }
}

class TaxCalculator {      // 税金計算だけ
  calculate() { ... }
}

ポイントは、機能を消したわけじゃないってこと。メールもPDFも税金も、ちゃんと残ってます。ただ「ごちゃ混ぜの段」から引っ張り出して、それぞれテーマの合った棚に移しただけ。こう分けると、各クラスが「同じ目的の仲間」だけで構成される。これが高凝集です。

で、ここからが大事なんですが、凝集度と結合度は表裏一体なんですよね。

:sunglasses::冷蔵庫をテーマごとに整理する(高凝集)と、どうなる? 「醤油どこ?」ってときに調味料の段だけ見ればええ。他の段を引っかき回さんで済むやろ。これが低結合や。

:sweat_smile::逆に、ごちゃ混ぜ冷蔵庫だと、醤油探すのに全部の段をひっくり返すことになる…

:sunglasses::そう。それが「ここ直したらあそこも壊れた」の正体。散らかってるから、あちこち触らなあかんくなるんよ。

覚え方は前回も出た 「仲間は近くに、他人は遠くに」。同じ目的の機能は集める(凝集度UP)、違う目的のものは離す(結合度DOWN)。この2つは別々の原則じゃなくて、同じ整理整頓の裏表なんですね。

そしてこれも「変更に強くする」に繋がります。整理されてれば、変更のとき触る場所が局所で済む=壊すリスクが減る、というわけ。

原則②:認知負荷を下げる

次は 認知負荷低減。個人的には、これがAI時代に一番効いてくる原則だと思ってます(その話は後の記事で)。

認知負荷ってのは、人間の頭の中にある小さなホワイトボードの使用量みたいなものです。

そのホワイトボードは意外と小さい。だから設計は、その限られたスペースを圧迫しないように工夫する行為とも言えます。

ざっくり言えば、「読む人が、理解するために頭の中で同時に保持しなきゃいけない情報の量」 のこと。

:sunglasses::人間の脳のメモリって、めちゃくちゃ少ないんよ。一度に7個くらいしか覚えてられん。なのに、それを使い切らせるコードを書くやつがおる。

:sweat_smile::耳が痛い。

たとえば、こんな関数。

function p(a, b, c, d, e) {
  if (a && (b || c)) {
    return d ? e * 1.1 : e * 0.9;
  }
  // ...さらにネストが続く
}

これを読むには、a〜eが何者かを全部覚えつつ、条件分岐を頭の中で追わなきゃいけない。読み手の脳のメモリがすぐ満タンになります。

設計の質は、読み手が同時に保持しなきゃいけない情報量で測れる。だから、

  • 引数は3つ以内に(覚える変数を減らす)
  • ネストは浅く(条件の積み重ねを減らす)
  • 名前で意図を語らせる(pじゃなくてcalculatePrice
  • 「これを理解するには、まずあれを理解して…」の連鎖を断つ

:sweat_smile::これ、要は「読む人に優しくしろ」ってことですか?

:sunglasses::半分そう。でもな、もっと実利的な理由がある。認知負荷が高いコードは、変更するときに事故るんよ。頭がパンクした状態でいじるから、考慮漏れが出る。バグの温床や。

:sweat_smile::あー、結局ここも「変更に強くする」に繋がるのか。

そうなんです。読みやすさは優しさの問題に見えて、実は 「安全に変更できるか」 の問題でもある。これも親玉に直結してます。

原則③:関心の分離(SoC)

3つ目は 関心の分離。違うことをするものは、違う場所に置け、という原則です。

低凝集の冷蔵庫と近い話に聞こえますが、こっちは「違うものを混ぜるな」という分離の側に軸足があります。

:sunglasses::家を想像してみ。キッチンとトイレ、同じ部屋にあったら嫌やろ。

:sweat_smile::絶対嫌ですね。

:sunglasses::料理する場所と用を足す場所は、目的が違うから分けるのが当たり前。なのにコードになると、平気で「画面の表示」と「業務ルール」と「DB保存」を一つの関数に詰め込むやつがおる。それ、キッチンにトイレ置いてるのと同じやで。

たとえばWebアプリなら、ざっくり「見た目(プレゼンテーション)」「業務ルール(ドメイン)」「データの出し入れ(データアクセス)」は、別の層に分ける。これが関心の分離の基本形です。

:sweat_smile::これも結局「変更に強くする」ですか?

:sunglasses::もちろん。画面のデザインだけ変えたいときに、業務ルールのコードを触らんで済む。トイレのリフォームでキッチンを壊さんでええ、ってことや。

ちなみに前回の地図で出てきた「レイヤードアーキテクチャ」も「ヘキサゴナルアーキテクチャ」も、根っこはこの関心の分離の具体化だったりします。原則がアーキテクチャパターンの土台になってる、いい例ですね。

ちなみに、高凝集と似ているように見えますが少し視点が違います。

高凝集は「仲間を集める」話。関心の分離は「そもそも別の目的を混ぜない」話です。

高凝集が部品の中の整理整頓だとすると、関心の分離はシステム全体の部屋割りに近いイメージですね。

原則④:依存性逆転(DIP)

さあ来ました。正直、私が7つの中で 一番わからなかった のがこれです。

実際、この原則だけは他より少し長めに説明します。なぜなら私自身がここで何度もつまずいたからです。

依存性逆転

言葉からして難しい。「変わりやすいものに、変わりにくいものを依存させるな」。…なんのこっちゃ。

:sweat_smile::先輩、これだけマジでわからないんですよ。「依存」とか「逆転」とか、字面で脳が止まる。

:sunglasses::よし、コンセントで説明したろ。

先輩、急に家電の話を始めました。

:sunglasses::お前の家の電子レンジ、発電所と直接ケーブルで繋がってると思うか?

:sweat_smile::いや、繋がってないでしょ。コンセントに挿してるだけで。

:sunglasses::そうやろ。もし電子レンジが「東京電力の○○発電所」に直結する設計やったら、どうなる? 電力会社を乗り換えた瞬間、家中の家電が全部死ぬ。

:sweat_smile::それは困る。

:sunglasses::困るやろ。でも現実はそうならん。なぜか。間に 「コンセントという規格」 が挟まってるからや。家電は「コンセントの形」さえ守れば動く。発電所が何やろうが、電力会社が変わろうが、知ったこっちゃない。

これがDIPの正体でした。コードで見てみましょう。まず、発電所に直結してる悪い例

// 悪い例:業務ロジックがMySQLに直結している
class OrderService {
  place(order: Order) {
    const db = new MySQLClient();   // ← MySQLを直接 new してる(発電所直結)
    db.query("INSERT INTO orders (id, total) VALUES (?, ?)", [order.id, order.total]);
  }
}

OrderService(業務ロジック)の中で、MySQLClient(具体的な技術)を直接 new して、INSERT文まで書いてます。これだと「注文を保存する」という本質が、MySQLという特定の技術にベッタリ依存してる。電子レンジが発電所に直結してる状態です。MySQLをやめてPostgresにした瞬間、この OrderService も書き換えるハメになります。

これを、間に「規格(インターフェース)」を挟むとこうなる。

// 規格(コンセントの形)を定義する:「saveというメソッドを持て」とだけ決める
interface OrderRepository {
  save(order: Order): void;
}

// 業務ロジックは「規格」だけを見る。MySQLという単語が消えた
class OrderService {
  constructor(private repo: OrderRepository) {}  // ← 規格を受け取る
  place(order: Order) {  // ← 注文を確定するメソッド
    // 「注文を保存する」としか言ってない。保存先がどこかは知らない
    this.repo.save(order);
  }
}

// 技術側:規格が決めた save の中身を、MySQL用に実装する
class MySQLOrderRepository implements OrderRepository {
  save(order: Order) {
    const db = new MySQLClient();
    db.query("INSERT INTO orders (id, total) VALUES (?, ?)", [order.id, order.total]);
  }
}

// 別の技術でも、同じ規格に従えば差し替えられる
class PostgresOrderRepository implements OrderRepository {
  save(order: Order) {
    const pg = new PostgresClient();
    pg.query("INSERT INTO orders (id, total) VALUES ($1, $2)", [order.id, order.total]);
  }
}

見てほしいのは、MySQLOrderRepositoryPostgresOrderRepository の関係です。OrderRepository(規格)が「save を持て」とだけ決めていて、実際のINSERT文を書くのは技術側。MySQLとPostgresで中身(SQLの書き方)は違うけど、どっちも save という同じ形にハマってる。これが「技術が規格に合わせる」ということです。

そして肝心の OrderService を見ると、MySQLともPostgresとも書いてない。ただ「規格(OrderRepository)の save を呼ぶ」だけ。だから保存先をMySQLからPostgresに変えても、OrderService は一文字も変わりません。

// 呼び出す側(OrderServiceを使う側)で組み合わせる
const service = new OrderService(new MySQLOrderRepository());
service.place(order);

業務ロジックの外側で「どの技術を使うか」を1箇所だけ決める。OrderServiceはそれを知らない。だから差し替えるのはここ1行だけ:

new MySQLOrderRepository()  →  new PostgresOrderRepository()
// OrderService は一文字も触らなくていい

ポイントは、「変わりやすいもの」と「変わりにくいもの」を見極めること。

  • 業務ロジック(「注文を保存する」みたいな本質)→ 変わりにくい
  • 使う技術(DB、UIフレームワーク、外部API)→ 変わりやすい

普通に書くと、最初の悪い例みたいに、本質である業務ロジックが変わりやすい技術に依存してしまう。コンセントなしで発電所直結してる状態です。

これを、間にインターフェース(規格)を挟んで 依存の向きをひっくり返す

Before: OrderService → MySQL
After:  OrderService → 規格 ← MySQL

MySQL側が規格に従う形に変わる。だから「依存性"逆転"」。

:sweat_smile::あー! 業務ロジックがDBに依存してたのを、間に規格を噛ませて「DBの方が規格に合わせる」形にするから、逆転なのか。

:sunglasses::そういうことや。こうしとけば、MySQLOrderRepositoryPostgresOrderRepository に差し替えるだけで、OrderService は指一本触らんで済む。コンセントの規格さえ同じなら、発電所を乗り換えても電子レンジはそのまま使える、ってことやな。

前回の地図で「クリーンアーキテクチャもヘキサゴナルアーキテクチャも、根底はこの原則」と書きましたが、まさにこれ。DIPは、アーキテクチャパターンの心臓部なんですね。ちなみにこのDIP、よく聞く「SOLID原則」の"D"にあたります。SOLIDの残り4つのうち、単一責任原則(SRP)は関心の分離や低結合・高凝集としてすでに出てきています。開放閉鎖・リスコフの置換・インターフェース分離の3つは、オブジェクト指向のクラス設計を前提とした話が多いため、この記事では省いています。

(ちなみに、こうやって「後で技術を選び直せるよう、間に規格を挟んで決定を遅らせる」考え方は、後の記事でまた出てきます)

原則⑤:YAGNI

5つ目は YAGNI(You Aren't Gonna Need It = どうせ要らないって)。「今いらんものは作るな」。

これは 旅行のパッキング がしっくりきます。

:sunglasses::旅行のとき、「使うかもしれんから」って、あれもこれも詰め込むやつおるやろ。

:sweat_smile::僕です。

:sunglasses::で、結局9割使わんと、重いカバン引きずって後悔する。コードも一緒や。「将来この機能要るかも」で先回りして作ったもの、大半は使われん

しかも厄介なのは、使われないだけじゃ済まないこと。

  • 作るのに時間を食う
  • コードが無駄に複雑になる
  • いざ本当の要件が来たとき、先回りで作ったものが邪魔になる

:sweat_smile::足りなかったら現地調達すればいい、みたいな話ですか?

:sunglasses::そうそう。必要になってから作ればええ。そのときのお前は、今より要件をちゃんとわかってるはずやからな。

これも「変更に強くする」に効きます。余計なものを作らない=変更のとき、考慮すべき荷物が少ない、ということ。

原則⑥:DRY

6つ目、DRY(Don't Repeat Yourself = 繰り返すな)。「同じことを2回書くな」。

ここで大事なのは、「同じこと」が指してるのは 見た目じゃなくて"知識" だということ。

:sunglasses::たとえばな、お前の住所。免許証にも、通販サイトにも、会社の書類にも同じ住所を書いてるとするやろ。引っ越したら、全部直さなあかん。一個でも忘れたら、古い住所が残って事故る。

:sweat_smile::あー、住所っていう一個の事実が、何箇所にも散らばってるのが問題なのか。

:sunglasses::そういうことや。「同じ事実・同じルール」が複数箇所にコピーされてる状態を避けろ、っちゅうのがDRYや。一箇所直せば済むようにしとけば、変更が楽になるし、直し忘れも起きん。

コードで言えば、消費税率みたいな「一個の事実」が、あちこちに * 1.1 とベタ書きされてる状態。税率が変わった瞬間、全部を探して直す羽目になります。これを定数や関数に一箇所だけ持たせておけば、変更はそこだけ。

:sweat_smile::なるほど。じゃあ似たようなコード見つけたら、どんどん一箇所にまとめてけばいいんですね!

:sunglasses::………。

:sweat_smile::え、なんですその間。

この「DRYだから、似てたらまとめればいい」という素直すぎる発想——実はここに落とし穴があるんですが、その話は次回に取っておきます。今は 「同じ知識を一箇所に」 がDRYだ、と押さえればOK。

原則⑦:可逆性

最後、7つ目は 可逆性。「後で戻せる方を選べ」。

設計判断って、たいてい 情報が足りない中 でやるものなんですよね。だから「絶対正しい選択」なんて選べない。それなら、せめて 「間違えても戻せる方」 を選んでおこう、という考え方です。

:sunglasses::契約書にいきなりボールペンで書くか? 普通、大事な書類ほど鉛筆で下書きするやろ。

:sweat_smile::消せるようにってことですね。

:sunglasses::そう。設計も一緒や。「これ、もし間違ってたら戻すのにどれだけかかる?」を毎回考える。戻すのが軽い方を選んどけば、判断ミスが致命傷にならん。

たとえば、

  • いきなり大きな共通化をするより、小さく分けておく方が、後で組み替えやすい
  • 「えいやで全部決める」より、決定を後回しにできる構造にしておく方が、情報が増えてから選べる

:sweat_smile::これ、さっきのYAGNIとかDIPとも繋がってる気がする。

:sunglasses::鋭いな。全部「将来の自分に選択肢を残す」って意味で地続きや。だから言うたやろ、7つはぜんぶ同じこと言うてるって。

まとめ:親玉から考える

今回のポイント。

  • 設計には、抽象度を問わず効く 大原則が7つ ある(関心の分離 / 低結合・高凝集 / 依存性逆転 / YAGNI / DRY / 可逆性 / 認知負荷低減)
  • でも7つをバラバラに暗記するのは悪手。全部「変更に強いソフトウェアを作る」という一つのゴールの手段にすぎない
  • ソフトウェアは必ず変わる。だから「変化に対応できる構造」が設計の本質ゴール
  • 新しい原則に出会ったら、「これは"変えやすくする"ためにどう効く?」 と親玉から考えれば自然に繋がる

7つを覚えるんじゃなくて、「変更に強くする」という1本の軸を持つ。これが大原則との付き合い方なんだと思います。

で、次回

7つの原則を一通り押さえて、いい感じに賢くなった気がした私。でも、さっきDRYのところで「まだ早い」とばかりに先輩に話を止められたのが、地味に引っかかってました。

:sweat_smile::先輩、さっきの話なんですけど。DRYって「同じ知識は一箇所に」ってことですよね。じゃあ、似たようなコード見つけたら、どんどんまとめてけばいいじゃないですか。さっきの間、なんかまずかったですか?

:sunglasses::……お前さ、その「似てたらまとめればいい」っての、一番事故るやつやで。

:sweat_smile::え。DRY守ってるだけなのに?

:sunglasses::(ニヤリ)ええ勘違いしとるわ。それ、次回みっちりやったろ。

原則は知ってる。でも、それを「どこまで」「いつ」適用するか——次回は、大原則を知っていてもなお難しい 「線引き」の問題 に切り込みます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?