【コード哲学エッセイ】 抽象化は汎用化じゃない —— 変更しやすさの設計
コードを書いているとき、ここ、抽象化しておこうと思う瞬間があります。
クラス名に「Base」や「Common」とつけてみたり、とりあえず汎用的な関数にまとめたり。
かつての僕は、そのたびに少しだけ誇らしい気持ちになっていました。
「これでどこからでも使える」
「あとから拡張しやすいはずだ」と。
けれど、時間が経つと真っ先に壊れるのは、いつもその抽象クラスや共通メソッドでした。
汎用にしたはずが、 最初に割れる場所
フリーランスになってすぐの頃、とあるサービスの請求周りを任されたことがあります。
月額課金と従量課金、キャンペーン、法人向けの特別プラン。
請求ロジックに関わるケースが、あとからいくらでも増えていきそうな匂いがしていました。
「ここは、ちゃんと抽象化しておかなきゃ」
僕は、すべての請求を扱う万能なクラスを作りました。
BillingManager のような名前をつけて、プラン別の分岐や計算ロジックを、ひとつの抽象の下に押し込めていきました。
最初のうちは、それなりに誇らしい気持ちで動かしていました。
共通化できたし、メソッド名もそれっぽい。
汎用的な設計をしたと、どこか安心していたのです。
数ヶ月後、想定していなかったキャンペーンが追加されました。
初月無料だけど、特定の条件を満たすと二ヶ月目も半額になるような、少しひねりのある仕様。
そのとき真っ先に悲鳴を上げたのが、自分が誇らしく作ったはずの BillingManager でした。
条件分岐は増え、引数は膨らみ、このパラメータが立っているときだけ、この計算をスキップするといった例外が、抽象クラスのなかに積もっていきました。
汎用にするつもりで作った抽象ほど、変更のたびに最初に割れる。
そんな現実を、何度も味わいました。
抽象化の目的を、 勘違いしていた
当時の僕は、「抽象化 = 汎用的にしておくこと」だと信じていました。
どこからでも呼べるようにする。
どんなケースにも対応できるように、余白を持たせておく。
けれど、現場でコードと向き合ううちに、少しずつ視点が変わりました。
本当に欲しかったのは、なんでもできるクラスではなく、変更しやすい構造だった。
新しい要件が来たとき、その変更の影響が局所にとどまること。
バグが出たとき、どこを読めばよいかがすぐにわかること。
ある機能を差し替えたいとき、ここさえ変えればいいと指させること。
抽象化の価値は、どこからでも使えることではなく、どこを変えればよいかが明確になることにある。
そう気づいたとき、僕のなかで良い抽象化の定義が変わりました。
抽象化は、 変わるところを囲う道具
いまの僕は、抽象化をこう捉えています。
抽象化は、変わるところと変わらないところを分けて、未来の変更を安くするための道具。
たとえば、決済手段が増えていくサービスなら、決済方法ごとの違いが変わる部分です。
一方で、決済を実行して結果を記録するという流れそのものは、そう簡単には変わらない。
このとき、決済フローを表すインターフェースや抽象クラスを用意し、変わるところだけを実装ごとに切り出しておく。
増え続けるのは、あくまで変わる部分の実装だけ。
フロー全体の流れや、結果の扱い方といった変わらない部分は、抽象の外側にそっと置いておく。
未来の自分や、同じコードを触る誰かにとっての問いは、こうなります。
「今回の変更で、何が変わるのか?」
その答えが、このクラスやこのディレクトリに集約されている状態。
そこに、抽象化の本当の価値があるのだと思うようになりました。
何を共通化するかではなく、 どの変更を安くしたいか
では、実際に抽象化を考えるとき、どんな問いを自分に投げかければよいのでしょうか。
昔の僕は、こう考えていました。
📍 どこに重複があるか
📍 どこを共通化できるか
📍 どこからでも呼べるようにできないか
いまの僕は、こう問い直します。
📌 このプロダクトで、今後どんな変更が何度も発生しそうか
📌 その変更を安くしたい場所はどこか
📌 その変更を一か所に集めるには、どの境界で区切るのがよいか
何を共通化するかではなく、どの変更を安くしたいか。
抽象化の出発点を、そこに置き直すだけで、設計の方向が変わります。
同じ抽象クラスを作るにしても、増え続ける決済手段を安く差し替えたいのか、どの画面からも決済を呼び出したいのかで、切り口はまるで変わるはずです。
前者なら、決済の実装を差し替えやすくする抽象。
後者なら、決済という行為を表すファサードのような抽象。
どちらが正しい、という話ではありません。
どの変更を安くしたいかを言葉にしないまま抽象化すると、汎用っぽいけれど何に対しても中途半端な構造になりやすい。
その危うさだけは、いつも覚えておきたいのです。
僕が、 いま自分に問いかけていること
いま、抽象化を考えるとき、僕は必ずひとつだけ自分に問いかけます。
「この抽象は、未来のどんな変更を、どれだけ安くしてくれるだろう?」
答えがはっきりしないなら、その抽象はまだいらないのかもしれません。
一度、具体的な実装のままにしておく。
三回くらい同じような変更が来てから、ようやく「そろそろ囲ったほうがよさそうだ」と判断しても遅くはない。
抽象化は、早いほど偉いわけではない。
「使い回しが効きそうだから」という曖昧な理由だけで導入した抽象ほど、未来の自分を苦しめやすい。
抽象化は汎用化のためではなく、変更しやすさのための武器。
そのことを、今日も自分に言い聞かせながら、エディタの前に座っています。
あわせて読みたい
▼ 依存関係との付き合い方に悩んだときに
依存を減らすことを正義だと信じていた僕が、必要なつながりとの付き合い方を考え直した話です。
▼ 速さと設計のバランスに迷ったときに
目の前のスピードと、未来の自分を守る設計。
そのバランスをどう取るかを考えたエッセイです。
▼ テストを書く理由を見失いそうになったときに
テストを保険とだけ捉えていた僕が、未来の自分への祈りとして書く感覚を取り戻した話です。
ひとりごと
若い頃の僕は、抽象化できる自分に酔っていたところがありました。
けれど、汎用的なクラスほど、最初に壊れる現場を何度も見てきて、ようやく少しだけ視点が変わりました。
いまも設計で迷うことはあります。
それでも、「どの変更を安くしたいか」と自分に問いかけることで、一歩だけ落ち着いて考えられるようになりました。
このエッセイが、同じように現場でコードと格闘している誰かの、小さなヒントになればうれしいです。

いいなと思ったら応援しよう!
最後まで読んでいただき、ありがとうございます。もしこの記事が「役に立った」「心に響いた」と感じたら、珈琲一杯分サポートいただけると嬉しいです。あなたの温かい応援を力に、また次の創作活動に励みます。