AHA プログラミング💡

この投稿は Kent C. Dodds 氏が書かれた AHA Programming💡 を翻訳したものです。

DRY(Don’t Repeat Yourself の頭字語)は、古くからあるソフトウェアの原則の一つで、Wikipedia には次のように要約されている。

システム内のあらゆる情報は例外なく、一つひとつが明確に、厳然と具現化されなければならない。

これはおおむねグッド・プラクティスだ。ほとんどの場合で、私はこれを支持する(おそらくこの定義の主張するほど強くは固執していないが)。これまでの経験からすると、重複コード(いわゆるコピペ、つまり DRY の対極)の一番の問題は、バグを見つけて修正したあと、同様のバグが他の箇所にも散らばっていると気づいたときに発生する。そうなると、そのバグに当てはまる箇所すべてを修復せざるを得なくなるのだ。

過去にコードベースを引き継いだときなんて重複コードだらけだったせいで、一つのバグ対応のために8箇所も修正が必要だったことさえある! 😱 本当にうんざりさせられる話だった! コードを一つの関数に抽象化し、どこからでも必要に応じて呼び出せるようにするだけで、どれほど救われただろう。

WET

もう一つ、Write Everything Twice を略した WET プログラミングと呼ばれるコンセプトもある。DRY 同様、融通が利かず、かなり規則に厳格だ。Conlin Durbin によれば、定義は次のとおりだ。

自分に問いかけてみるといい。「これは今まで書いたことがあるか?」と、2回まで。しかし3回目はまかりならない。

先に挙げたコードベースには、度を超えた抽象化がいくつも存在し、重複よりもはるかに有害だった。それは AnglularJS のコードで、 複数ある AngularJS コントローラー内では this が関数に渡されていて、メソッドやプロパティを this に対してモンキーパッチすることでインスタンスに特定の機能を持たせるという代物だった。継承もどきの一種といったところだろう。絶望的にややこしく、読み解くのが困難で、そのあたりのコードベースに手を入れるときは恐ろしくてたまらなかった。

そのコードが3箇所を超える数多くの箇所で再利用されていたのは確かだ。だが、その抽象化の出来は悪く、かえって重複してくれていた方がありがたいと思うくらいだった。

AHA💡

AHA(何かを発見したときみたいに「アハ!」と発音する)は、Cher Scarlett発言にちなんだ頭字語で、次の一句を略したものだ。

Avoid Hasty Abstractions (早計な抽象化は避けよ)

この原則について私が思っていたことを、Sandi Metz はものの見事に表現している

不適切な抽象化より重複の方が好ましい

これはまさしく本質を言い当てた金言であるから、ぜひ繰り返し読んでもらいたい。それから、このことについての Sandi のブログ記事 The Wrong Abstraction を読んでみよう。素晴らしい内容だ。

これらに通じる事柄としてもう一点、私からも付け加えたい大事な原則がある。

第一に最適化すべきは可変性である

肝心な点は、コードが将来的にはどうなるのか誰にも分からないところにある。何週間もかけて時間を費やせば、パフォーマンス向上のためにコードを最適化できるし、新たなる抽象化にふさわしい理想的な API をこしらえることだってできる。しかしそうしたところで、翌日には仮説の誤りに気が付き、しかたなく API を一から書き直したり、実装した機能が不要になったりする。そうなるかどうかは絶対に誰にも分からない。絶対そうだと断言できるのは、状況が変わらないなんて十中八九ないということだ。それにもし変更が生じなかったとしても、その重複コードに触れる必要はもうなくなるので、とりあえずはコードの見栄えを気にしなくたってよいのだ。

ここで誤解をしないでもらいたい。何も秩序を否定しようというわけではない。私が主張したいのは、将来のコードがどういった要件を持つのか誰にも分からないのは紛れもない事実であるから、それに対して注意を払うべきだということに尽きる。

だからユースケースを把握したという確信を得るに至るまでの間は、重複コードがあっても構わない。コードのどこに、的確な引数を持つ関数を作るための差異があるだろう? 実行中のコードの中に、すでに重複箇所が2、3あれば、コード内の共通箇所から抽象化を求める声が聞こえてくるはずだから、きっと抽象化に着手するにもってこいの気分になっている。

もし、抽象化するのを早まってしまった場合はどうだろうか。きっと抽象化された関数やコンポーネントのことを、ユースケースに完璧に合致していると思い込んでしまうだろう。だから新たにユースケースが出てきても、抽象化に無理に合わせようとしてコードを歪めてしまう。 これを何度も繰り返せば、if ステートメントやループ内で、抽象化は全アプリケーションの根幹を成すに至る 😂😭

2、3年前に、ある会社に雇われてコードレビューをしたことがあった。私は jsinspect というツールを使ってコピペコードを特定し、抽象化する余地のある部分を提示した。ざっと目を通した限り、重複コードは山ほどあり、抽象化がどんなふうに施されるべきかは一目瞭然だった。

AHA プログラミングについて押さえておくべきポイントは、抽象化に手を付けるタイミングに固執するのは禁物だということだ。その代わり、しかるべきタイミングだと確信が持てるようになってから抽象化に取りかかる。それまでの間は、重複コードがあっても恐れてはいけない。

結論

ソフトウェアの原則に対するアプローチとしては、私は実践性と節度を重視している。私が DRYWET よりも AHA を好む理由はそこにある。AHA は、抽象化に対して注意を促すことを意図しており、コードを関数なりモジュールなりに抽象化するタイミングには厳格なルールを定めているわけではないからだ。

この考えがあなたのお役に立てれば幸いだ。もし、ひどい抽象化の深みにはまってしまったことに気づいたとしても、元気を出そう! Sandi が、そんな状況から抜け出すための良き手引きを用意してくれている! 彼女のブログ記事を一読あれ。健闘を祈る!