適切に抽象化されたコードとはなにかって話

適切に抽象化されたコードを書く、というのはまさに言うは易し行うは難しだ。

最近私が心がけていることのうちのひとつに、「未来を予測しない」というのがあって、「ここはあとから変更とか追加があるかもしれない」と思って抽象化しておくみたいなことはやめようと思ってる。ひとことで言うと、よくYAGNIとか言われるアレです。

そもそも、なんのために抽象化するのかというということを考えると、ひとつは「一塊の手続きに名前を付けて隠蔽して外から中を意識しないで済むようにする」ってことと、その副産物として「交換可能性が高まる」っていうのがある。

抽象化して実装を隠蔽することで、

  • コードが理解しやすくなる
  • インターフェイスさえ合ってれば交換可能な部品として扱えるようになる

という利点があるから、わたしたちは抽象化を行うわけだ(だからこそ「名前重要」なわけですね)。

この後者の利点に注目すると、ついつい「あっあとからこういう変更があるかもしれないからここは抽象化しておいてあとで変更しやすくしよう」とか「あっあとからこういうのが追加されるかもしれないからここは追加しやすい形で抽象化しておこう」とか、そういう誘惑に駆られることがすごい多い。

じっさいこういうことをやっておいて後から役に立ったときって「うおー俺偉いすげー天才!」みたいな脳汁が出てめっちょ気持ちいいんだけど、実際にそうなることってそんなにない。普通の人間には未来は予測できないので、結局こういう「先を見越した抽象化」ってそこにかけた手間が無駄になる。無駄になるくらいならまだいいほうで、あとからむしろ負債としてのしかかってくることのほうが多いように感じる。

もし「そうじゃない、それはお前の頭が悪いんだ、俺の未来予想はいつも的確だ」という方がいるならば、プログラマーなんてやめて占い師か博打打ちになったほうがいいと思う。多分すごい儲かるから。そもそもユーザーがどう動くかとか競合のサービスがどういう動きをするのかみたいなところが事前にわかるならその能力は完全に「超能力」だと思う。サービスやプロダクトがどういう風に成長していくかがわかるのは独りよがりなサービスを作ってる作り手くらいだろ。「それでいい」ってんならそれでいいですけど。

一方で、「先を見越しておいて正解」な部分ってもの実はあって、それは「変わることが確実にわかってる部分」。これはあらかじめ抽象化されてるとあとからすごい役立つ。というわけで適切な抽象化を行うために「変わることが確実にわかってる部分はどこなのか」という問題が立ち上がってくるわけですが、それってつまりビジネス上の要請がなんなのかって話で、すでに話はコードの世界から「現実の世界」へと変わって行ってる。

だから適切な抽象化ってのはじつは「コードだけ」の問題ではなくて、そこにいわゆる「業務知識」とかそういうものが関わってきてると思うんだけど、コードだけに興味がある人間はそういうことあんまり考えたがらない感じがする。それは果たして本当に「技術力」って言えるのかな、現実の問題を解決することに役立たない「技術力」なんてクソくらえだぜって思うしそういうやつが「俺技術あるーすげー」って言ってるの見ると「馬鹿じゃないの」みたいに思う。

話がそれた。

適切な抽象化の話なんだけど、「未来のあらゆる変更に対して開かれた設計」なんてことはできるはずがないので、「現在の要求(そこには「今後ここは変わります」というものも含まれる)」に対して最もシンプルな実装をしておくことが求められてると私は思う。そうすると結局「想定していなかった変更」ってのが必ず出てくるんだけど、これはどうやっても出てくる。そういう時に、シンプルな実装になっていれば変更も容易なので、やっぱり今いらない抽象化は悪だなーって思う。ただし、テストがない環境、てめーはだめだ。「想定していなかった変更」ってのは想定してなかったんだから必ず今の設計を壊すような部分が出てくる。その時の変更で他の部分にクリティカルな影響を与えるかもしれない不安が出てくるわけで、YAGNIで健康的にやっていくためにはきちんとテストが書かれていることが最重要なんじゃないのかなーって思う。

なんだか、結局

  • 未来の変化を恐れすぎた過度の抽象化は悪
  • シンプルに保っておいて、想定外の変更に対してはテストで身を守れ

という当たり前の話になった。

ただ、DRY原則とかはすごいよく言われるけど、YAGNIに関してはすごい不当に軽視されてるような気がして(とくに、一定以上の能力のあるひとが軽視しがちな感じする)、それが最近すごく気になる。