Brian Kernighan の「良いセンス」は、可読なコードが時間を節約しバグを減らし、巧妙なトリックよりチームを速く動かすことを示しています。

Brian Kernighan の名前は、多くの開発者が無意識に使う場所に現れます:クラシックな Unix ツール、C エコシステム、そしてプログラムを明晰に説明する術を教えた数十年の著作。The C Programming Language(Dennis Ritchie と共著)、The Unix Programming Environment、あるいは彼のエッセイや講演を思い出すかどうかにかかわらず、一貫する要点は「単純なアイデアをきちんと表現すること」です。
カーニハンの最良の助言は C の構文や Unix の慣習に依存していません。人間の読み方についての話です:私たちは構造をスキャンし、名前に頼り、意図を推測します。トリックで意味が隠されると混乱します。だから、TypeScript、Python、Go、Java、Rust を書く際にも「可読性のセンス(taste)」は重要なのです。
言語は変化します。ツールは向上します。チームは時間的プレッシャーのもとで機能を出荷し続け、ほとんどのコードは元の作者以外(多くは将来の自分)によって保守されます。明瞭さはそれらを成り立たせる乗数効果です。
これは「ヒーローコーディング」を称賛したり、古いルールを丸暗記しろという話ではありません。日常のコードを扱いやすくする習慣についての実践的ガイドです:
カーニハンの影響が重要なのは、チーム志向の単純な目標を指し示しているからです:コードが伝えるように書け。コードが明晰な説明のように読めれば、デコードに費やす時間が減り、改善に費やせる時間が増えます。
「良いセンス(good taste)」は個人のスタイルや凝ったパターン、最小行数に圧縮することではありません。意図を確実に伝える最も単純で明瞭な選択をする習慣です。
良いセンスのある解は、次の基本的な問いに答えます:このコードは何をしようとしているのか、そしてなぜこうしているのか?その答えに心の運動や隠れた仮定、トリックの解読が必要なら、チームに時間を余分に費やさせています。
大半のコードは書かれるより読まれることの方がずっと多い。「良いセンス」は読むことを主な活動と見なします:
だから可読性は美学(インデント、行幅、snake_case の好き嫌い)だけではありません。それらは有用ですが、「良いセンス」は主に推論を楽にすること:明確な名前、明白な制御フロー、予測可能な構造です。
一般的な誤りは簡潔さを優先してしまうことです。時には最も明確なコードは、手順を明示するため少し長くなります。
例えば、次の二つを比べてみてください:
validate → normalize → compute → return のように名前付き中間変数を使って工程を説明する数行後者は行数が増えるかもしれませんが、正当性を検証する認知負荷を減らします。バグの切り分けや変更の安全性も高まります。
良いセンスは、賢く見せるための改良をやめ、代わりに意図を明らかにする判断ができることです。仲間があなたに説明を求めずにコードを理解できれば、それは正しい選択です。
賢いコードはその場では勝利のように感じられます:行数が減る、巧妙なトリック、差分における「おおっ」。しかし実際のチームでは、その賢さが繰り返し請求される費用に変わります—オンボーディング時間、レビュー時間、コードに触るたびのためらいに支払われるのです。
オンボーディングが遅くなる。 新しい仲間は単に製品を学ぶだけでなく、あなたの私的なショートカットの方言も学ばねばなりません。関数を理解するのに巧妙な演算子や暗黙の慣習をデコードする必要があると、人はそのコードを触ることを避けるか、恐る恐る変更します。
レビューが長く、信頼できなくなる。 レビュアーはトリックが正しいことを証明するのにエネルギーを使い、ふつうは挙動が意図と合っているかを評価するべきです。巧妙なコードは頭の中でシミュレートしにくいので、単純なバージョンなら見つけられたエッジケースを見落とします。
賢さは次の場面で累積します:
よくある犯罪者:
17, 0.618, -1)が規則を記憶させる代わりに埋め込む&& / || のショートサーキット技)カーニハンの「センス」についての指摘はここに現れます:明瞭さは行数を増やすことではなく、意図を明白にすることです。今日20秒を節約しても、将来の読者に毎回20分のコストを課すなら、それは賢明ではなく高くつきます。
カーニハンの「センス」は小さな、繰り返しできる決定に現れます。大規模な書き換えは必要ありません—小さな改善がファイルをスキャンしたり、挙動を検索したり、時間的プレッシャーでバグを直すたびに積み重なります。
良い名前はコメントの必要性を減らし、ミスを隠しにくくします。
意図が明らかな名前を目指しましょう:チームでの話し方に合う名前を使うこと。
sum より invoiceTotalCents を優先する。名前を解読しなければならないなら、それは目的を果たしていません。
読み方の大半はスキャンです。一定した空白と構造が目に重要な箇所(関数の境界、条件文、ハッピーパス)を見つけさせます。
実践的な習慣:
ロジックが複雑になるとき、意思決定を明示することで可読性は通常向上します。
比べてみましょう:
// Harder to scan
if (user && user.active && !user.isBanned && (role === 'admin' || role === 'owner')) {
allow();
}
// Clearer
if (!user) return deny('missing user');
if (!user.active) return deny('inactive');
if (user.isBanned) return deny('banned');
if (role !== 'admin' && role !== 'owner') return deny('insufficient role');
allow();
後者は長くなりますが、チェックリストのように読めて、壊さずに拡張しやすくなります。
これらは「小さな」選択ですが、保守可能なコードの日々の技芸です:誠実であり続ける名前、読者を導くフォーマット、心の運動を強いられない制御フロー。
カーニハンの明瞭さは、仕事をどのように関数やモジュールに分けるかに最も現れます。読者は構造をざっと見て、各部分が何をするかを推測し、その推測が大体合っているべきです。
関数は「あるズームレベルでただ一つのことをする」ことを目指しましょう。検証、ビジネスルール、フォーマット、I/O を混ぜると、読者は複数のスレッドを同時に追わねばなりません。
簡単なテスト:関数内に「// now do X」のようなコメントを書いているなら、X はしばしば明確な名前の別関数に抽出する良い候補です。
長いパラメータリストは隠れた複雑性の税です:呼び出し側が小さな設定ファイルのようになります。
いくつかのパラメータが常に一緒に渡されるなら、意味のある塊にまとめましょう。オプションオブジェクトや小さなデータ構造は呼び出し側を自明にできます—ただしグループを整然と保ち、「misc」に何でも放り込まないこと。
また、原始値よりドメイン概念を渡す方が良いです。UserId は string より分かりやすく、DateRange は (start, end) よりルールを示唆します。
モジュールは約束です:「この概念のために必要なものはここにあり、残りは別の場所にある」。モジュールはその目的を頭に入れて持てる程度に小さくし、副作用を最小化する境界を設計してください。
助けになる実践:
共有状態が必要な場合は、それを正直に名付け、不変条件を文書化してください。明瞭さは複雑さを避けることではなく、読者が期待する場所に複雑さを置くことです。変更の際の境界維持については /blog/refactoring-as-a-habit を参照してください。
カーニハンの「センス」はコメントの付け方にも現れます:目的はすべての行に注釈を付けることではなく、将来の誤解を減らすことです。最良のコメントは、コード自体は正しいが驚くべき振る舞いをする箇所で誤った仮定を防ぐものです。
コードをそのまま言い換えるコメント(「i をインクリメント」)は余計です。役に立つコメントは、意図、トレードオフ、構文からは明らかでない制約を説明します。
# Bad: says what the code already says
retry_count += 1
# Good: explains why the retry is bounded
retry_count += 1 # Avoids throttling bans on repeated failures
「何を」コメントに誘われるなら、コード自体がクリアでない兆候です(より良い名前、小さな関数、単純な制御フローの必要性)。事実はコードに持たせ、推論や理由はコメントに持たせましょう。
陳腐化したコメントほど信頼を損なうものはありません。コメントが任意なら時間とともにずれます。誤っているならアクティブにバグを生む源になります。
実用的な習慣:コメントの更新を変更の一部として扱う。レビューで「このコメントはまだ振る舞いに合っているか?」を問うのは妥当です。合っていなければ更新するか削除する。「コメント無し」は「間違ったコメント」よりも良いです。
インラインコメントは局所的な驚き用です。広範な指針はドックストリング、README、開発者ノートなどに置きます—特に:
良いドックストリングは、関数の正しい使い方と期待されるエラーを伝え、実装を逐一語ることはしません。/docs や README の短いメモが「なぜこうしたか」の物語を保存し、リファクタリングに耐えさせます。
静かな勝利は:コメントは少なくするが、その一つひとつが価値を持つことです。
多くのコードはハッピーパスでは「大丈夫」に見えます。センスの真価は入力が欠ける、サービスがタイムアウトする、ユーザーが予期せぬ操作をする――そんなときに現れます。ストレス下では巧妙なコードが真実を隠しがちです。明瞭なコードは失敗を明示し、回復可能にします。
エラーメッセージはプロダクトとデバッグワークフローの一部です。次に読む人が疲れてオンコール中であることを想定して書いてください。
含めるべきもの:
ロギングがあれば構造化されたコンテキスト(requestId, userId, invoiceId など)を追加し、無関係なデータを掘らずに実行可能にしてください。
全てをワンライナーで処理しようという誘惑がありますが、良いセンスは重要なエッジケースを選び、それを見えるようにすることです。
例えば、「空の入力」や「見つからない」を明示的に分岐させる方が、途中でどこかで暗黙的に null が発生するチェーンより読みやすいことが多いです。重要な特例は名付けて前面に出しましょう。
戻りの形を混ぜる(時々オブジェクト、時々文字列、時々 false)と読者は心の決定木を持たねばなりません。次のパターンを好んでください:
明確な失敗処理は驚きを減らします。驚きがバグと夜間のページ通知を生むのです。
可読性はあなたがコードを書いたときに何を意味したかだけでなく、次にファイルを開く人が何を期待するかにも関わります。一貫性は「コードを読む」ことをパターン認識に変えます—驚きが減り、誤解が減り、毎スプリント繰り返される議論が減ります。
良いチームのスタイルガイドは短く、具体的で実用的です。すべての好みをエンコードしようとせず、繰り返し出る疑問—命名規約、ファイル構造、エラーハンドリングパターン、テストの「完了」基準—を決めます。
本当の価値は社会的なものです:書かれているとレビューは「私は X が好き」から「我々は X に合意した(理由はこちら)」に移ります。リポジトリに固定して近くに置くと良いでしょう(例:/docs/style-guide.md)。
フォーマッターやリンターを使って測定可能で退屈なルールを自動化しましょう:
これにより人は意味に集中できます:命名、API 形状、エッジケース、コードが意図に合っているかどうか。設計選択を説明するような手動ルール(例:「ネストを減らすため早期 return を優先」)は依然として重要で、ツールだけでは判断できません。
性能要件や組み込みの制約、トリッキーな並行処理、プラットフォーム固有の振る舞いなどで複雑さが正当化されることはあります。合意はこうあるべきです:例外は許されるが明示的であること。
シンプルな基準:トレードオフを短くコメントし、性能が理由ならマイクロベンチや計測を添え、複雑なコードは明確なインターフェースの背後に隔離して大半のコードベースを読みやすく保つ。
良いコードレビューは検査のように感じさせるのではなく、「良いセンス」についての短く集中した授業のように感じられるべきです。カーニハンの要点は、賢いコードが悪ということではなく、他人がそれを扱うときに賢さは高コストになる、ということです。レビューはそのトレードオフを可視化し、意図的に明瞭さを選ぶ場です。
まず問うべきは:「仲間が一度読んで理解できるか?」です。これは通常、マイクロ最適化に入る前に命名、構造、テスト、振る舞いを評価することを意味します。
コードが正しいが読みづらい場合、可読性を実際の欠陥として扱ってください。変数名の改名、長い関数の分割、制御フローの単純化、期待される振る舞いを示す小さなテストの追加を提案しましょう。「動くが理由が分からない」を防ぐレビューは将来の混乱を防ぎます。
有効な実践的な順序:
レビュアーのフィードバックが対立的になると進まなくなります。「なぜこんなことを?」ではなく、次のように尋ねてみましょう:
質問は協働を呼び、しばしば知らなかった制約を浮かび上がらせます。提案は方向性を示しつつ無能を示唆しません。このトーンがチームにセンスを広めます。
一貫した可読性を望むならレビュアーの気分に頼らないでください。レビュー・テンプレートと定義済みの「完了条件」にいくつかの「明瞭性チェック」を追加しましょう。短く具体的に:
これを続けるとレビューはスタイルの取り締まりから判断力を教える場に変わります—まさにカーニハンが提唱した日々の規律です。
LLM ツールは動くコードを素早く生成できますが、カーニハンが指した基準は「動く」ではなく「伝える(communicates)」であることを忘れないでください。チャットで機能を作り生成コードを反復するようなワークフローを採るなら、読みやすさを受け入れ基準として扱う価値があります。
Koder.ai のようなプラットフォーム(チャットプロンプトから React フロントエンド、Go バックエンド、Flutter モバイルアプリを生成し、ソースをエクスポートできる場)でも同じセンス駆動の習慣が適用されます:
速度は、人間がレビューし、保守し、拡張しやすい出力が前提のときにこそ最も価値が出ます。
明瞭さは一度達成して終わりではありません。要件が変わるとコードは読みやすさを失います。カーニハンの感覚はここに合います:壮大な書き換えや今日を驚かせる「賢い」ワンライナーより、着実で理解しやすい改善を好むのです。
最も安全なリファクタリングは地味です:振る舞いを同一に保つ小さな変更。ステップごとにテストを走らせてください。もしテストが無ければ、触る箇所の周りにフォーカスしたチェックを追加しましょう—一時的なガードレールと考えて、構造を改善しても恐れず進められるようにします。
実践的なリズム:
小さなコミットはレビューもしやすく、仲間は副作用ではなく意図を評価できます。
すべての「賢い」構成を一度に駆逐する必要はありません。機能追加やバグ修正でそのコードに触れるたびに、賢い近道を分かりやすい同等物に置き換えていきましょう:
これが実チームで明瞭さが勝つ方法です:作業中のホットスポットを一つずつ改善していく。
すべてのクリーンアップが緊急とは限りません。役立つルール:アクティブに変更されている、頻繁に誤解される、バグを起こしやすい部分は今リファクタリングする。安定して孤立しているなら後回しにする。
リファクタリング負債を可視化しましょう:短い TODO を残すか、「支払い」を説明するチケットを作って(例:「新しい支払い方法を追加しにくい;関数が5つの仕事をしている」)、曖昧なまま放置せず意図的に判断できるようにします。
「良いセンス」を一貫して現すには、実行しやすくすることです。ここに覚えやすく、行動に移しやすい軽量のチェックリストを示します。
Before: process(data) が検証、パース、保存、ロギングを一箇所でやっている。
After: validateInput, parseOrder, saveOrder, logResult に分割。メイン関数が読みやすい概要になる。
Before: if not valid then return false が5回繰り返される。
After: 先頭でガード節(または一つの検証関数)を作り、明確な問題リストを返す。
Before: x, tmp, flag2, doThing()。
After: retryCount, draftInvoice, isEligibleForRefund, sendReminderEmail()。
Before: ループに三つの特例が途中に隠れている。
After: 特例を先に処理(またはヘルパーに抽出)してから単純なループを実行する。
今週 1つだけ 改善項目を選んで採用してみましょう:「新しい略語は使わない」「ハッピーパスを優先する」「PRごとにヘルパーを一つ抽出する」「すべてのエラーメッセージに次のステップを含める」など。7日間追跡し、実際に読みやすくなったものを継続してください。
カーニハンの影響はC言語そのものよりも、コードを伝達手段と見る持続的な原則にあります。
言語やフレームワークは変わっても、チームはコードを素早くスキャンし、推論し、レビューし、デバッグできる必要があります。特に数か月後や時間的プレッシャー下ではなおさらです。
「良い趣味(good taste)」とは、意図を伝える最も単純で明快な選択を一貫して選ぶ習慣です。
実用的な試験はこうです:仲間が「これは何をしていて、なぜこうしているのか」をトリックを解読せずに答えられるか、という点です。
大半のコードは書かれるより読まれる頻度が高いからです。
読者を最適化すると、オンボーディング時間、レビューの摩擦、誤った変更リスクが減ります。特にメンテナーが文脈を失った“将来の自分”である場合に効果が大きいです。
「賢さの税」は次のように現れます:
また、デバッグやインシデント対応、引き継ぎが難しくなり、操作のたびにコストが積み上がります。数秒を稼いでも、将来の触るたびに数分失うならトータルで損です。
よく問題になるのは次のようなパターンです:
これらは中間状態を隠し、レビューで見落としやすいエッジケースを生みます。
短いコードが良いとは限りません。明確さのために少し長くなることは有益です。
例えば、命名された中間変数で「検証 → 正規化 → 計算 → 返却」と段階を分ければ、正当性の検証が容易になり、デバッグも単純になります。数行増えても認知負荷が下がるなら逆に効率的です。
最小限の労力でわかりやすさを改善する命名習慣:
invoiceTotalCentsはsumより良い)customerかclientのどちらか)名前を解読する必要があるなら、それは失敗です。名前は追加コメントの必要性を減らすべきです。
明確に、単純な分岐を好み、“ハッピーパス”を見やすく保つこと。
役立つ戦術:
「何を」ではなく「なぜ」をコメントすること。
良いコメントは意図、トレードオフ、非自明な不変条件を説明します。明白なコードの説明はノイズになるので避け、コメントの更新は変更の一部として扱ってください。間違ったコメントは無い方がましです。
ツールは測定可能で機械的なルール(フォーマット、不要な変数、明白な落とし穴)を担当し、人は意味に関する判断をします。
軽量なスタイルガイドは繰り返し出る議論を防ぎ、レビューを「好み」から「合意」へ変えます。性能上の例外があるときは、そのトレードオフを記録して複雑さを明確に隔離してください。