第5章:コードのニオイ入門(やばさを嗅ぎ分ける)👃💥
この章でできるようになること 🎯✨
- 「これ…ヤバくなりそう🥺」なコードを早めに嗅ぎ分けできるようになる🐶👃
- ニオイを見つけたときに、**何が困るのか(未来の痛み)**を言葉にできる🗣️💡
- VS Code と静的解析(ESLintなど)とAIを使って、ニオイを見逃しにくくする習慣が作れる🔍🤖✨

1) そもそも「コードのニオイ」って何?🧸🌸
コードのニオイ(Code Smell)は、**表面に出ている「怪しいサイン」**のことだよ👀⚠️ それ自体がバグとは限らないけど、もっと深い問題が隠れてるかもって教えてくれる合図なの✨ (martinfowler.com)
そして大事なのがこれ👇
- ニオイ=「有罪判決」じゃない🙅♀️
- ニオイ=「点検しよっか」のサイン✅
SonarQubeの用語でも、Code Smellは「保守性を下げて、コードを分かりにくくする問題」って位置づけだよ📉🧹 (Sonar ドキュメンテーション)
2) ニオイを放置すると、未来でこうなる😇💣
ニオイを放置すると、だいたい次のどれかが起きるよ〜😵💫
- 仕様追加のたびに、同じ場所を怖くて触れない🥶
- 直したら別のところが壊れて、デバッグ地獄🕳️🐛
- 「誰が読んでも分からん」になって、自分ですら辛い😭
- テストを書きたくても書けず、確認が手作業になる🧪➡️😱
ニオイは「今の痛み」より、変更が来たときの痛みを増幅する装置だと思ってOK💥🔁
3) まずは王道ニオイ四天王 😈👑
ここからは、超頻出のニオイを「症状 → 未来の痛み」で覚えよ〜📝✨
A. 長すぎ関数 🐍📏
症状:1つの関数がやること多すぎ(計算・保存・通知・ログ…全部) 未来の痛み:ちょい修正で全体が壊れそう、テストしづらい😇💥
B. 巨大クラス(神クラス)🗿🏛️
症状:OrderServiceが何でも屋(料金・在庫・DB・通知) 未来の痛み:変更理由が増えすぎて、毎回怖い😱
C. if地獄 / switch地獄 🌋🔁
症状:条件分岐が増殖して、読みながら迷子🧭 未来の痛み:追加するたびに分岐が壊れて事故る🚑
D. コピペ地獄 📋📋📋
症状:似たコードがあちこちに… 未来の痛み:修正漏れが必ず出る(Shotgun Surgery系)🧨
4) TypeScriptで起きがちな「TS特有ニオイ」🧠🌀
TSは型がある分、型まわりのニオイが出やすいよ〜!
anyで逃げる(型安全の放棄)🫠- 型アサーション
as XXXの乱用(“ほんとに?”問題)🤥 stringで状態や種類を表現(例:"cash" | "card"をただの string にする)=プリミティブ執着🪨null/undefinedチェックが散らばる(nullable地獄)☁️- 返り値が「成功したの?失敗したの?」曖昧(例外/Result型の整理不足)😵
※ちなみに、現時点のTypeScriptのnpm最新版は 5.9.3 だよ📦✨ (npm) (この章の内容はバージョンに依存しないけど、「型で守る」流れはこの先ずっと大事!)
5) 実験:わざと「ニオイコード」を書いてみよう 🧪👃💥
Campus Café(ミニ題材)で、注文確定を一気にやる関数をわざと作るよ☕️📦 目的は「直す」じゃなくて、ニオイを発見する練習ね🔍✨
// わざとニオイだらけ!教材用 🧪
// やってること:合計計算 → 割引 → 支払い方法で分岐 → 保存 → 通知 → ログ
export function checkout(order: any, payment: any, coupon: any) {
let total = 0;
// 商品合計(nullチェックも雑)
for (const item of order.items) {
total += item.price * item.qty;
}
// クーポン(if地獄の芽)
if (coupon) {
if (coupon.type === "student") {
total = total * 0.9;
} else if (coupon.type === "rain") {
total = total - 100;
} else if (coupon.type === "set") {
if (order.items.length >= 3) total = total - 200;
}
}
// 支払い(またif)
if (payment.method === "cash") {
if (payment.cash < total) {
return { ok: false, message: "お金たりない" };
}
// お釣り計算
const change = payment.cash - total;
console.log("change:", change);
} else if (payment.method === "card") {
// なんか外部呼ぶ想定(詳細が混ざる)
console.log("call card api...");
} else if (payment.method === "paypay") {
console.log("call paypay api...");
}
// 保存(詳細が混ざる)
console.log("save to db:", order.id, total);
// 通知(詳細が混ざる)
if (order.notifyEmail) {
console.log("send email:", order.email);
}
return { ok: true, total };
}
ミニ課題①:ニオイに付箋を貼る気持ちでチェック🧷👀
上のコードを見て、ニオイっぽい場所にコメントを付けるつもりで数えてみてね😊 目標:8個以上見つけられたら勝ち🎉✨
ヒント👇
- 「責務が混ざってない?」🧩
- 「条件分岐、増えそうじゃない?」🔁
- 「型が弱すぎない?」🫠
- 「外部詳細(DB/API/通知)がロジックに混ざってない?」📡
6) ニオイ発見の“型”を作ろう:ニオイ質問リスト 📝👃
コードを読むとき、これを自分に質問すると嗅ぎ分けが上手くなるよ🐶✨
✅ ニオイ質問 10連発
- この関数、やってること3つ以上ない?🍱
- 変更理由が複数ありそう?(仕様追加の想像)🔮
if / switchが増えたら破綻しそう?🌋- 同じロジックが他にもありそう?📋
- 変数名が「a」「tmp」「data」っぽくて意味不明?😵
anyやasが安心感ゼロになってない?🫠- “文字列”で状態を表しすぎてない?🧵
null/undefined対応が散らばってない?☁️- 依存(DB/API)がロジックに直混ぜじゃない?🥣
- テスト書くとしたら、どこが邪魔?🧪🧱
7) VS Code と静的解析で「鼻を強化」しよ👃🦾✨
人間の嗅覚だけだと見落とすから、ツールの鼻も借りよ〜🤝
- TypeScriptの型エラー/警告:ニオイの予兆になる👀
- ESLint:怪しいパターン(複雑すぎ、未使用、危険な書き方)を拾いやすい🧹
- ちなみにESLintは v10 が2026年1月頃を目標に進んでて、大きめ変更も入る予定だよ📦⚠️ (ESLint) → だからこそ「ツールの指摘=絶対」じゃなく、理由を理解する姿勢が大事😊✨
8) AI(Copilot/Codex系)でニオイを見つけるコツ 🤖🔍✨
AIは「嗅ぎ分け補助」にはめっちゃ使えるよ!ただし、鵜呑み禁止ね🙅♀️🍭
使えるプロンプト例 🧠💬
次のTypeScriptコードを読んで、コードのニオイ(責務混在、条件分岐増殖、型の弱さ、重複、依存の混ざり)を
「ニオイ名」「どの行/箇所」「将来の痛み」「最小の改善案(今は直しすぎない)」の形式で箇条書きにして。
このコードをユニットテストしづらくしている要因を列挙して。
テストしやすくするための“境界(分離ポイント)候補”も提案して。
AIを使うときのルール3つ 🐣✅
- ① ニオイの理由を説明できない提案は採用しない🙅♀️
- ② 変更は一気にやらず、小さく(差分レビューしやすく)✂️
- ③ “綺麗すぎる抽象化”を急に入れない(それ別章でやる)🧊
9) ミニ課題②:ニオイを「未来の変更」で説明してみよう🔮📝
さっきの checkout に対して、次の変更が来たらどうなるか想像してみてね😊
- 割引が「学割・雨の日・誕生日・曜日・紹介」…増える🎟️
- 支払いが「現金・カード・PayPay・交通系・後払い」…増える💳
- 通知が「メール・アプリ通知・LINE」…増える🔔
書くこと(短くてOK)✍️
- 「どこが地獄になりそう?」😇
- 「なぜ地獄?(if増殖?責務混在?)」🌋
- 「境界として分けられそうなのはどこ?」✂️🧩
10) 章末チェック(できたらOK)✅🎉
- ニオイはバグじゃなく「点検サイン」って説明できる👃✨ (martinfowler.com)
- 長すぎ関数 / 巨大クラス / if地獄 / コピペ地獄 を見つけられる😈
- TS特有ニオイ(any乱用、型の弱さ、文字列状態など)を1つ以上言える🧠
- ニオイを「未来の変更」で語れる🔮🗣️
次章の予告 🧫✅
次は「テストしにくい=設計が苦しい」あるあるを体験するよ〜🥺 ニオイを“確信”に変える武器が、テストだよ🧪🧡