先週、シンプルな機能のPRをレビューした:チェックアウトに割引コードフィールドを追加。PRは47ファイル変更 — DiscountStrategyFactory、DiscountValidationService、3つのインターフェース。
2週間の作業。2日であるべきだった。
引き継いだすべてのコードベースには、誰も抱えていない問題を解決する抽象化レイヤーがある。
ORMをラップするリポジトリパターン。同じマシン上のサービスのためのメッセージキュー。3人チームのためのマイクロサービス。
一緒に働いた最高のエンジニアは洗練されたコードを書かない。動く最もシンプルなものを書いて、現実が迫ったときに複雑さを追加する。
プロバイダー用プラグインアーキテクチャを持つ決済システムで働いていた。
理論:
新しいプロバイダーを簡単に追加!
インターフェースを実装するだけ!
現実:
プロバイダーAにはリトライロジックが必要
プロバイダーBには異なるエラーコードが必要
プロバイダーCには非同期Webhookが必要
すべてのプロバイダーが特別ケース。
引き剥がした。プロバイダー固有のコード。理解しやすく、新しいものを追加する速度は同じ。
赤旗:
バカみたいにシンプルから始めろ。 恥ずかしいくらいシンプル。
痛みを待て。 3箇所でコードを複製しているとき、変更が無関係なものを壊すとき — それがリファクタリングするとき。その前ではない。
削除のために最適化しろ。 捨てやすいコードを書け。
ジュニア: シンプルなコードを書く(パターンを知らない)
ミドル: 複雑なコードを書く(パターンを知っていて、使いたい)
シニア: シンプルなコードを書く(パターンを知っていて、いつ使わないか知っている)
ミドルフェーズをスキップしろ。自分で作る代わりに、他の人のポストモーテムから学べ。
最高のコードは、実際の問題を解決する最もシンプルなコードだ。
— blanho
CRUDは履歴を上書きする。イベントソーシングはすべてを記憶する。それが重要な場面とオーバーキルな場面。
冗長パイプライン、インテリジェントなセグメント選択、カスタムストレージレイヤー — Netflix Live Originアーキテクチャの内側。
ロギング、認証、リトライ、レート制限 — 最初に設計しないが、後で全員が苦しむもの。
# 必要だったもの:
def apply_discount(order, code):
if code == "SAVE10":
order.total *= 0.9
return order
# 作られたもの:
class DiscountStrategyFactory:
def create_strategy(self, code: str) -> IDiscountStrategy:
return self._registry.get(code, NullDiscountStrategy())
class PercentageDiscountStrategy(IDiscountStrategy):
def __init__(self, percentage: float):
self._percentage = percentage
def apply(self, order: Order) -> Order:
order.total *= (1 - self._percentage)
return order
# + 3つのファイル、2つのインターフェース、1つのレジストリ# バージョン1: if文
def get_discount(code):
if code == "SAVE10":
return 0.10
return 0
# バージョン2: 辞書(5つ以上コードがあるとき)
DISCOUNTS = {"SAVE10": 0.10, "VIP20": 0.20}
def get_discount(code):
return DISCOUNTS.get(code, 0)
# バージョン3: データベース(管理UIが必要なとき)
def get_discount(code):
return Discount.objects.filter(code=code).first()