概要
コードの目的は「動くこと」ではない。
それは 「読めること」「変えられること」「壊さずに進化できること」 である。
本稿では、メンテナンスに耐えうるJavaScript設計のために、以下の原則と実践を体系的に整理する:
- 意図を伝える命名戦略
- 関数・ファイルの分割と粒度
- コメントではなく構造で語る
- 責務分離とドメインへの還元
- 抽象化と具体性のバランス設計
1. 命名:コードの最小ドキュメント化
✅ 意図が“読める”名前をつける
// ❌ 曖昧すぎる
function handle() { ... }
// ✅ 具体性と文脈を備える
function handleLoginRequest() { ... }
- ✅ 状態 →
is
,has
,can
で始める - ✅ 動作 →
fetch
,create
,update
,delete
など動詞系で統一 - ✅ 配列 → 複数形に(例:
users
,items
)
2. 関数の粒度と分割の原則
// ❌ 1関数で複数の責務を持つ
function submitForm(data) {
validate(data);
saveToDB(data);
showNotification('保存しました');
}
// ✅ 単一責務ごとに分離
function submitForm(data) {
if (!isValid(data)) return;
persistUser(data);
notifySave();
}
- ✅ 関数は “1つの問いに答える”サイズ に分ける
- ✅ 長さではなく意味の分離が基準
3. コメント最小主義:コードで語れ
// ❌ コメントが必要なほど意味不明
// 1つ目の引数はログイン状態、2つ目は管理者権限
check(true, false);
// ✅ 関数と変数で意味を明示
checkPermission({ isLoggedIn: true, isAdmin: false });
- ✅ コメントは “なぜこのコードか”の補足 に限定する
- ❌ “何をしているか”をコメントで説明するのは敗北
4. 責務分離とレイヤー設計
// ❌ UIとロジックが混在
button.addEventListener('click', () => {
const id = input.value;
fetch(`/api/user/${id}`)
.then(res => res.json())
.then(data => render(data));
});
// ✅ ロジックは別関数に切り出し
button.addEventListener('click', () => {
const id = getUserIdFromInput();
loadUser(id).then(renderUser);
});
- ✅ UI → イベント → ロジック → ストア の流れを分離
- ✅ ファイル・関数の単位で “役割”を明示的に設計
5. 抽象化と具体性のバランス
❌ 抽象化しすぎて意味が見えない
execute(data);
✅ 具体的なユースケースに基づく命名
updateUserSettings(settings);
→ ✅ 汎用化は「3回以上使われたら」検討する
6. テスト性 = 設計品質の指標
- 関数がテストしにくい → 責務が混在 or 副作用が埋め込まれている
- モックが必要すぎる → 結合度が高すぎる
- 単体テストだけで信頼できる → 構造的に正しい
→ ✅ “テストしにくいコード”は、“設計が未熟なコード”の兆候
設計判断フロー
① 名前を見ただけで意図が伝わるか? → NGなら命名を見直す
② 関数が長い理由は意味が多いからか? → 責務を分離
③ コメントで何を補っているか? → コメント不要な構造に変換可能か?
④ UI・非同期・データ処理が混ざっていないか? → レイヤーで整理
⑤ 汎用化しすぎていないか? → ユースケースで具体化する
よくあるミスと対策
❌ ファイル名・関数名が意味不明(utils.js
, handle()
など)
→ ✅ “このコードが何を意図して存在しているか”を名前に落とし込む
❌ コメントでしか意図が伝わらない構造
→ ✅ 関数・変数・ファイルの粒度で「意味」を見せる設計に
❌ どこで状態が変わるか追いにくい
→ ✅ 副作用は“目立つ関数”に隔離し、変更点を明示する
結語
メンテナブルなコードとは、“一度だけ読むコード”ではない。
何度も読むことが前提であり、未来の自分や他者との対話のために設計された構造である。
- コードは言葉
- 設計は構文
- 構造は意図
すべての行は「何をしたいか」ではなく、
「なぜそう設計したのか」を語るべきである。