<- home

私が考える良いプログラムの書き方

2022/03/25

最近、プログラムは結局のところどんな風に書けばよいのかということをずっと考えていて、少しだけそれが見えてきたので、ちょっと書いてみようと思う。

サマリ:


straightforwardなコードを書く

複雑なコードよりもシンプルなコードが良いということに反対するプログラマはほぼいないと思うけど、「シンプルなコード」って結局どういうコード?というのはあまり説明されない。これは仕方がないことで、あるコードがシンプルか複雑かというのは割とその作りたいものによって変わってしまうからだ。基本的に、大きくて複雑なものを作ろうとするほどコードは少しずつ複雑になっていってしまうので、そういったコンテキストを考慮せずコードの断片だけ取り出しても、あるコードがシンプルかどうかの議論はしにくい。また、プログラマによってもシンプルという言葉の意味が違ったりするし、シンプルとイージーの違いが曖昧な人というのは驚くほどいる (シンプルとイージーの話はSimple Made Easyをきちんと読めば良いのではないかと思う) 。

なので、私は「シンプルなコード」よりも「straightforwardなコード」と言うことが多い。straightforwardというのは「率直な」とか、「ひねくれていない」というようなニュアンスの言葉だと思う。 straightforwardなコードっていうのは、目的を達成するための最短距離を行くようなコードのことだ。やたら柔軟であったり、過度に抽象化してあったり、きれいにレイヤリングされていたりといったことは、目的の達成のためには不必要であることも多くて、そういうのはstraightforwardでないなと直感的には思うことが多い。

straightfowardなコードをなぜ書きたいかと言うと、それが読むのも直すのも簡単だからだ。プログラムは少しずつ作っていくもので、すごい人が書いたすごいプログラムも、はじめはおもちゃみたいなものだったはずだ。いきなりすごいものを作ることはほとんどの人にとっては不可能で、まずは小さいものを作って少しずつそれを大きくしていく必要がある。そういう普通のプログラムの作り方と、straightforwardなコードは相性が良いのである。

難しいのは、ここでいう「目的」とはひとつではないということだ。自分だけが読み書きするワンショットなスクリプトとかは、柔軟性も抽象化も不要だけど、チームでそれを長期に渡って開発し続ける上では、保守性というものは間違いなく重要だ。straightforwardなコードというのは「(main関数にすべてを詰め込むような) 最低限のコードだけ書く」ということではなくて、どちらかというと「過剰なことをしない」という意味合いに近い。

自分が過剰に抽象化したり、レイヤリングしたり、柔軟性をもたせたりしてしまっていないか?といったものを発見することは実は割と簡単で、「なぜそれをしたのか?」と自分に問いかけてみるだけで良い。

例えば、コンパイラのようなプログラムを書いているとき、入力ストリームをトークンに分割して、それを「ノード」という形で抽象化することがある。この抽象化は妥当で、なぜならコンパイラというプログラムは最終的なアセンブリを機械的に出力するために抽象構文木を作りたいのだけど、抽象構文木の要素の型をなにかしら一つに定めないとそもそも抽象構文木が実装できないからだ。また、抽象構文木を辿るときに、ある部分木が具体的になんなのかといった情報は重要ではないので、むしろノードとして抽象化されている方が望ましいことになる。こんな感じで、なにかしらまともな理由が説明できるのであればそれは過剰とは言えないと思う。

「なぜやるのか?」を考えたときに、オブジェクト指向、DDD、なんとかアーキテクチャ、なんとか原則といったキーワードっぽい答えが出てきたとき、それはおそらく過剰なことをやってしまっている。また、将来のためとか拡張性のためといった言葉が出てきたときも大抵はそれらは不要だ。straightforwardなコードを書くということは過剰なことをしないということで、うまく理由が説明できないことはやめてしまって良いと思う。これによってコードは単純になるし、あとで直すのも簡単になる。

重要なのは、オブジェクト指向とかDDDがダメだというわけではないということだ。それらはそれなりには意味があるものだと思うけど、大事なのはあくまでちゃんと動く、単純でわかりやすいコードを書くことであって、オブジェクト指向がちゃんとできているかとか、レイヤリングに一貫性があるかとか、そういうことでは本来ないはずだ。

straightforwardなコードを書く上では、Worse is Betterも読んでおくと良いと思う。

速く書く

よっぽど何度も書いているようなプログラムは別として、私達は基本的に、どんなコードがstraightforwardなのか (過剰でないのか) は、実際にそれを書いてみるまでわからない。私は自分をプログラマとしては普通くらいのレベルだと思っているけど、これは実はかなりレベルの高いプログラマも同じなのではないかなと考えている。このコードは良いコードのはずだと思っていたら、9割方できたところでやっぱり不要だったということがあるし、それをおそれて単純に書くと、ここはもっと良く書けたなと思うこともある。 こういったことは私の中では全く予想不可能で、ある程度あたりをつけるといったことはもちろんやるんだけど、それが正しい保証というのはどこにもない。

こういうときどうすればいいかと言うと、とにかく書き始めてしまうのが良い。書くのが速くないプログラマはまず書き始めるまでにあれこれ考えてしまうし、いざ書き始めても考え込んでなかなか進められないということがある。書くのが速いプログラマは、頭の中にいろいろな考えはありつつも、とりあえずその時最も妥当に見えるコードを書いて、動くところまで持っていくことができる。書いてみた後にそれがじつは妥当ではなかった場合どうするかというと、それはただ単に直せば良いだけだ。とにかく動くものができあがればそれを少しずつ改善していくことができるし、もしできあがったものが本当によくなかったとしても、手が速ければ捨てて作り直すことができる。ソフトウェアのいいところはいくらでも後で書き直しができるところで、そのアドバンテージは存分に活かしたほうが良いと思う。

速く書くためにはできることがたくさんある。データ構造やアルゴリズム、それらの計算量なんかは単純に丸暗記しても良い。よく書くプログラミング言語の標準ライブラリとかも全部頭に入れておくと良いだろう (私はできていない) 。また、テストを書くときは細かいユニットテストよりもエンドツーエンドなテストを書いておくほうが良いし、そのテストは簡単かつ素早く実行可能にしておくのが良い (この辺は開発イテレーション偏重 - 兼雑記から大きく影響を受けている) 。gitのコミットやブランチといった機能を使えばすぐにコードはある地点に戻せるわけなので、そういった周辺ツールも活用するのが良いと思う。

速く書くことができれば、判断するために必要な時間が短くなるので、判断のコストがどんどん下がっていく。また、速く書いてそういった判断をすることで、プログラミングのスキルが高くなって、次にすべき判断の精度もプログラミングのスピードもさらに上がっていくことになる。


短くまとめるのであれば、次のようなことになる。

良いプログラムの書き方に正解みたいなものはおそらくなくて、ここに書いたものは私がこう思う、というものでしかない。それでも、今の時点で私は、こういったプログラミングのやり方がベストだと信じている。

Tweet