はじめに
プログラミングの世界には、長年の経験から生まれた「原則」や「考え方」が数多く存在します。これらは先人たちが失敗と成功を繰り返す中で見出した、いわば 開発の知恵 です。
この記事では、プログラミングを学び始めた方や実務に入ったばかりの方を対象に、知っておくと確実に役立つ有名な原則を10個厳選して紹介します。
原則を知っておくメリットは大きく3つあります。
- コードの質が上がる: 何が「良いコード」なのかの判断基準が持てる
- コードレビューで会話ができる: 「これはDRYに反していませんか?」のように共通言語で議論できる
- 設計の判断に迷わなくなる: 「どう書くべきか」に対する指針が持てる
それでは、1つずつ見ていきましょう。
有名な書籍「リーダブルコード」を一部参考にしています。
より詳細を知りたい方はご一読をおすすめします。
1. DRY — Don't Repeat Yourself
同じコードを繰り返すな
DRYは、最も有名なプログラミング原則の1つです。同じ知識やロジックをコード中の複数箇所に書かないようにしよう、という考え方です。
なぜ繰り返しが問題なのか
同じ処理がコードのあちこちに散らばっていると、仕様変更のたびに すべての箇所を漏れなく修正 しなければなりません。修正漏れはバグの温床になります。
# ❌ DRYに反している例:税込み計算が複数箇所にある
order_total = price * 1.10
invoice_total = item_price * 1.10
receipt_total = product_price * 1.10
# ✅ DRYに従った例:関数にまとめる
def calc_tax_included(price, tax_rate=0.10):
return price * (1 + tax_rate)
order_total = calc_tax_included(price)
invoice_total = calc_tax_included(item_price)
receipt_total = calc_tax_included(product_price)
関数にまとめておけば、税率が変わっても修正は1か所で済みます。
注意点
「コードの見た目が似ている」だけでは、必ずしもDRYに反しているとは限りません。たまたま同じ処理に見えるが、実は異なるビジネスルールに基づいている場合、無理に共通化するとかえって複雑になります。「同じ知識が重複しているか」を基準に判断しましょう。
2. KISS — Keep It Simple, Stupid
シンプルに保て
KISSは、設計やコードをできる限りシンプルに保つべきだという原則です。複雑なコードは読みにくく、バグが入りやすく、保守が困難になります。
# ❌ 不必要に複雑
def is_adult(age):
if age >= 18:
return True
else:
return False
# ✅ シンプル
def is_adult(age):
return age >= 18
上の例は極端ですが、実際の開発でも「もっと短く、もっとスマートに書きたい」という欲求が過度な複雑さを招くことがあります。デザインパターンの多用、過剰な抽象化、ネストの深い三項演算子など、「書いた本人しかわからないコード」は避けるべきです。
KISSの本質は 「凝ったコードを書く力」よりも「誰でも読めるコードを書く力」の方が価値がある ということです。
3. YAGNI — You Ain't Gonna Need It
今必要ないものは作るな
YAGNIは、将来使うかもしれないからという理由で機能を先回りして実装することを戒める原則です。エクストリーム・プログラミング(XP)から生まれた考え方です。
# ❌ YAGNIに反している例:まだ誰も使わない機能を先に作る
class User:
def __init__(self, name, email):
self.name = name
self.email = email
self.phone = None # いつか使うかも
self.fax = None # いつか使うかも
self.secondary_email = None # いつか使うかも
def send_sms(self): # まだ要件にない
pass
def send_fax(self): # まだ要件にない
pass
# ✅ YAGNIに従った例:今の要件に必要なものだけ作る
class User:
def __init__(self, name, email):
self.name = name
self.email = email
「将来必要になるかもしれない」コードには以下のコストが発生します。
- 書く時間: 今の要件に不要な実装に時間を使う
- テストの時間: 使われない機能にもテストが必要になる
- 読む時間: 他の開発者がコードを理解する負荷が増える
- 的外れのリスク: 実際に必要になったとき、想定と違う仕様になることが多い
必要になったタイミングで実装すれば十分です。
4. 単一責任の原則(SRP)
1つのモジュールには1つの責任だけ
SRP(Single Responsibility Principle)は、SOLID原則の最初の1文字にあたる原則です。1つのクラスや関数には、1つの理由でしか変更が発生しないようにしよう、という考え方です。
# ❌ SRPに反している例:1つのクラスが複数の責任を持つ
class Report:
def calculate_totals(self, data):
# 集計ロジック
pass
def format_as_html(self, data):
# HTML整形ロジック
pass
def send_email(self, html):
# メール送信ロジック
pass
このクラスは「集計」「整形」「送信」の3つの責任を持っています。集計ロジックを変更したいだけなのに、メール送信のコードまで含むファイルを触ることになります。
# ✅ SRPに従った例:責任を分離する
class ReportCalculator:
def calculate_totals(self, data):
pass
class ReportFormatter:
def format_as_html(self, data):
pass
class ReportMailer:
def send_email(self, html):
pass
責任を分けておくと、変更の影響範囲が小さくなり、テストも書きやすくなります。
「この関数(クラス)は何をするものですか?」と聞かれたとき、「〇〇 と △△をします」のように「と」が入ったら、責任が複数ある可能性があります。
5. 関心の分離(SoC)
役割ごとにコードを分けよ
SoC(Separation of Concerns)は、プログラムを異なる関心事(=目的や役割)ごとに分離しようという原則です。SRPがクラスや関数のレベルの話であるのに対して、SoCはもう少し広い視点でシステム全体の構造に適用されます。
身近な例として、Webフロントエンドでは以下のように関心事を分けます。
| 関心事 | 担当 |
|---|---|
| 構造(何を表示するか) | HTML |
| 見た目(どう見せるか) | CSS |
| 振る舞い(どう動くか) | JavaScript |
バックエンドのフレームワークで広く採用されている MVC(Model-View-Controller) パターンも、SoCの代表的な実践例です。
Model — データとビジネスロジック
View — 画面表示
Controller — ユーザー入力の受付と処理の振り分け
dbtのプロジェクトでも、staging(データの整形)と marts(ビジネスロジック)をディレクトリで分けるのは、SoCの実践と言えます。
関心事が適切に分離されていると、ある部分を変更しても他の部分に影響が波及しにくくなります。
6. 早すぎる最適化は諸悪の根源
まず動くものを作れ、最適化はその後
この格言は、計算機科学者ドナルド・クヌースの有名な言葉に由来します。コードを書く段階で「ここは遅くなりそうだから…」と推測でパフォーマンスチューニングを始めると、以下の問題が起きがちです。
- 推測が外れる: 実際のボトルネックは別の場所にあることが多い
- コードが複雑になる: 最適化されたコードは往々にして読みにくい
- 開発が遅れる: 本来の機能実装に使うべき時間を消費する
# ❌ 早すぎる最適化:まだプロファイリングもしていないのにキャッシュを自前実装
class UserService:
def __init__(self):
self._cache = {}
self._cache_ttl = {}
def get_user(self, user_id):
if user_id in self._cache:
if time.time() - self._cache_ttl[user_id] < 300:
return self._cache[user_id]
user = self._fetch_from_db(user_id)
self._cache[user_id] = user
self._cache_ttl[user_id] = time.time()
return user
# ✅ まず動くものを作る
class UserService:
def get_user(self, user_id):
return self._fetch_from_db(user_id)
# パフォーマンスに問題が出たら、計測したうえで最適化する
正しいアプローチは以下の順番です。
- まず正しく動くコードを書く
- 実際にパフォーマンスの問題が発生する
- プロファイリングでボトルネックを特定する
- 特定された箇所だけを最適化する
7. ボーイスカウトルール
コードを触ったら、見つけたときよりきれいにして去れ
ボーイスカウトには「キャンプ場を、来たときよりもきれいにして帰ろう」という教えがあります。これをプログラミングに応用したのがこの原則です。
既存のコードを修正するとき、本来の変更に加えて、目に付いた小さな改善も一緒に行おう、という考え方です。
具体的には、以下のような改善が該当します。
- わかりにくい変数名を適切な名前にリネームする
- 不要になったコメントやコードを削除する
- 一貫性のないインデントやフォーマットを整える
- 小さなリファクタリング(長すぎる関数の分割など)
ボーイスカウトルールは「ついでにできる小さな改善」が対象です。本来のタスクと無関係な大規模リファクタリングを勝手に行うのは、レビューの負荷が増えたり、意図しないバグの原因になります。大きな改善は別タスクとして切り出しましょう。
1つ1つは小さな改善でも、チーム全員がこの習慣を持てば、コードベースは日々少しずつ良くなっていきます。逆に誰もやらなければ、コードは徐々に劣化していきます(これを 技術的負債 と呼びます)。
8. 防御的プログラミング
入力や外部を信用するな
防御的プログラミングは、予期しない入力や想定外の状況に対して、プログラムが安全に振る舞えるように備えておく考え方です。
ユーザーの入力、外部APIのレスポンス、ファイルの中身、他の関数の戻り値など、「自分のコードの外から来るもの」は何が入ってくるかわからないという前提で書きます。
# ❌ 防御していない例:入力をそのまま信用
def divide(a, b):
return a / b # b が 0 だったら? 数値じゃなかったら?
# ✅ 防御的に書いた例
def divide(a, b):
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("引数は数値である必要があります")
if b == 0:
raise ValueError("0で割ることはできません")
return a / b
防御的プログラミングで特に重要なポイントは以下の通りです。
- 入力値のバリデーション: 型、範囲、NULLチェックを入り口で行う
- エラーハンドリング: try-except(try-catch)で例外を適切に処理する
- 境界値の考慮: 空リスト、空文字、0、負の数、非常に大きな値などを想定する
- 外部依存への備え: APIのタイムアウト、ファイルの不在、ネットワークエラーに対処する
「正常系だけ動けばいい」という発想を捨てて、「何が起きても壊れない」コードを目指すのが防御的プログラミングです。
9. 名前重要(Naming is important)
良い命名は最高のドキュメント
変数名、関数名、クラス名、ファイル名。命名はプログラミングにおいて最も頻繁に行う設計判断の1つです。適切な名前が付いているコードはコメントがなくても読めますが、不適切な名前のコードはどれだけコメントがあっても理解しづらくなります。
# ❌ 何をしているのかわからない
def proc(d):
r = []
for x in d:
if x['a'] > 18:
r.append(x)
return r
# ✅ 名前だけで意図が伝わる
def filter_adults(users):
adults = []
for user in users:
if user['age'] > 18:
adults.append(user)
return adults
良い命名のための基本的な指針をいくつか紹介します。
省略しすぎない。 usr ではなく user、btn ではなく button。タイプ数を節約するより、読む人の理解を優先しましょう。ただし i(ループカウンタ)や e(例外)など、慣例として広く受け入れられている省略は問題ありません。
関数名は動詞から始める。 get_user()、calculate_total()、is_valid() のように、その関数が何をするかを動詞で表現します。
ブール値は質問形にする。 is_active、has_permission、can_edit のように、Yes/Noで答えられる名前にすると読みやすくなります。
一貫性を保つ。 同じ概念には同じ言葉を使います。ある場所では user、別の場所では customer、また別の場所では account と呼ぶと混乱の原因になります。
「名前が思いつかない」と悩むときは、そのコード自体の設計が曖昧な場合があります。良い名前が付けられないのは、責任や目的が明確になっていないサインかもしれません。
10. 車輪の再発明をしない
既存のライブラリ・ツールをまず探せ
車輪の再発明(Reinventing the Wheel)とは、既に広く使われている解決策があるのに、同じものを自分でゼロから作ってしまうことを指します。
# ❌ 車輪の再発明:日付のパースを自力で実装
def parse_date(date_str):
parts = date_str.split('-')
year = int(parts[0])
month = int(parts[1])
day = int(parts[2])
# うるう年の処理は...? タイムゾーンは...?
...
# ✅ 既存のライブラリを使う
from datetime import datetime
def parse_date(date_str):
return datetime.strptime(date_str, '%Y-%m-%d')
自作する場合と比べて、既存のライブラリには以下のメリットがあります。
- 多くの人にテストされている: エッジケースやバグが既に対処されている可能性が高い
- ドキュメントが整備されている: 使い方を調べやすい
- メンテナンスされている: セキュリティパッチやバグ修正が継続的に行われる
ただし、何でもライブラリに頼ればいいわけではありません。以下の場合は自作が妥当です。
- 学習目的で仕組みを理解したいとき
- 既存のライブラリが要件に合わないとき
- ライブラリの依存関係を増やしたくないとき(セキュリティやバンドルサイズの観点)
「まず探す → 見つからなければ作る」の順番を意識しましょう。
原則との付き合い方
原則は「絶対ルール」ではなく「指針」
ここまで10個の原則を紹介しましたが、最も大切なことを最後に伝えます。原則は絶対に守らなければならないルールではありません。
原則はあくまで「多くの場合において良い結果をもたらす指針」です。状況によっては、原則に従わない方が良い判断になることもあります。
例えば、以下のような場面ではDRYに反する選択が合理的です。
- 共通化すると依存関係が複雑になりすぎる場合
- 2つの処理がたまたま似ているだけで、変更の理由が異なる場合
- プロトタイプを素早く作りたい場合
トレードオフを考える視点
原則同士が矛盾することもあります。
| 場面 | 矛盾する原則 |
|---|---|
| コードを共通化したいが、共通化すると複雑になる | DRY vs KISS |
| 将来の拡張に備えたいが、今は必要ない | SRP vs YAGNI |
| パフォーマンスを上げたいが、コードが読みにくくなる | 最適化 vs KISS |
こうした場面では、「今のチーム・プロジェクトにとって何が最も重要か」を考えて判断します。正解は文脈によって変わります。
原則を知っていることの本当の価値は、「なぜその判断をしたのか」を言語化できるようになることです。「DRYは知っているが、この場面ではあえて重複を許容した。理由は〇〇だから」と説明できるようになれば、原則を使いこなしていると言えるでしょう。
まとめ
本記事で紹介した10個の原則を一覧にまとめます。
| # | 原則 | 一言でいうと |
|---|---|---|
| 1 | DRY | 同じコードを繰り返すな |
| 2 | KISS | シンプルに保て |
| 3 | YAGNI | 今必要ないものは作るな |
| 4 | 単一責任の原則(SRP) | 1つの責任だけ持たせよ |
| 5 | 関心の分離(SoC) | 役割ごとにコードを分けよ |
| 6 | 早すぎる最適化は諸悪の根源 | まず動かせ、最適化はその後 |
| 7 | ボーイスカウトルール | 来たときよりきれいにして去れ |
| 8 | 防御的プログラミング | 外部を信用するな |
| 9 | 名前重要 | 良い命名は最高のドキュメント |
| 10 | 車輪の再発明をしない | 既存のものをまず探せ |
すべてを一度に実践する必要はありません。まずは日々のコーディングの中で「このコード、DRYに反していないかな」「この関数名、もっとわかりやすくできないかな」と意識するところから始めてみてください。
原則は知識として知っているだけではなく、繰り返し実践する中で初めて自分のものになります。この記事が、その第一歩のきっかけになれば幸いです。
最後まで読んでいただきありがとうございました。この記事が参考になりましたら、ぜひLGTMをお願いします。