A Philosophy of Software Design vs Clean Codeの頂上決戦を読んだ
APOS vs Clean Code
A Philosophy of Software Designの著者であるJohn Ousterhout氏とClean Codeの著者であるRobert C. Martin(通称Bobおじさん)氏の対談のログを読んだ。
両者はバイネームでお互いの考えの一部を否定しあう関係性であったが、その二人がついに直接対決をするという何とも激アツな展開。プログラマー界ののエル・クラシコと言ってもよいだろう。
私はこの二つの書籍を読んでおり、どちらもほどほどに影響を受けながらコードを書いているで、非常に興味深い内容だった(A Philosophy of Software Designは日本語版がないので英語版を読む必要があり、理解が中途半端な部分はあるかもしれないが)。
この対談ログでは、主に関数の分割、コードコメント、TDDなど双方の意見に相違があるトピックについて議論が記録されており、それぞれのトピックにおいて最終的に双方が合意した内容と相違が見られた内容がサマライズされている。お互いの意見の相違を客観的に見た上でJohnさんがまとめたサマライズだけを見ても十分に勉強になる内容だと思う。
ここでは、自分のコード設計に関する考え方を整理するためにも読んだ上でどちらの考えに賛同できるかについて、自分なりの考え方をまとめてみる。
関数の分割について
関数の分割とは、すごく長い処理が書かれた関数があった時に、それをどの粒度まで分割するべきかについての議論である。ざっくり両社の意見の違いをまとめると、以下のようになる。
- Bobおじさん: 関数の分割は、関数が一つのことを行うようになるまで行うべき(One Thing Rule)
- Johnさん: 関数な分割、One Thing Ruleは過剰な分割は読む負荷をあげる
自分はJohnさんの「One Thing Rule(一つのことをやる)のOneに曖昧さがあり、過度な分割を助長する」という意見に概ね同意している。冗長な関数の命名をするくらいなら、Tidy Firstでも提案されてるようなステートメントを小分けにする(処理のかたまり毎に空行を入れる)とか、そこに対してコメントを入れたりする方が良い。
そのステートメントが、他のステートメントと異なる抽象度を持つ内容(特に具体的すぎる内容)である場合、関数として抽出する価値はあるだろう。ただ、関数のジャンプをして、今は全体の処理のどこの部分を読んでるかを脳内メモリに保存しながら関数の中身を読むのは結構ツラいと思う。
少なくとも「ステートメント内の処理がぱっと見だとわからないので関数として切り出して関数名で表現する」というのは関数の切り出しのガイドラインとしては過剰な気がする。それはJohnさんご指摘の通り、コメントを適切に使って解決するべきである。
ただ、Bobおじさんの当時の時代背景として(自分はその時はまだ生まれたてなので推測だが)、あまりにも長すぎるコードが多くて到底読めるような代物じゃなかったからこそ、あたまごなしに「とにかく関数を短くしろ」という指針を打ち出した、と考えると、かなり価値のあることだったのではないかと思う。
TDDについて
TDDについては自分は割と肯定的な意見を持っているのでBobおじさん寄りの意見だ。両者の意見をまとる。
- Bobおじさん: TDDはコードの品質を向上させるために有効である
- Johnさん: TDDは戦術的プログラミング(短期思考)を助長するので、先に全体を設計してコードを書いてから単体テストを書くべき
自分はざっくりTDDについて以下のような印象を持っている
- レイヤー化されたWeb APIの実装をする時にはTDDは有効である。ドメイン層のロジックを書く時にドメインモデルの振る舞いを先に考えて理想のインターフェースとそれに対するテストを書いてから実装をすると、良い設計につながる実感がある
- 一方で、新しいライブラリを作るなど、一般的なレイヤー分け等がすぐに見えてこない領域ではTDDの適用は難しい。そもそも全体の設計をどうするかに対する知識がないので、全体像の設計を考えて実装を少し進め、そこで得られたフィードバックをもらいながら設計も変えていき、概ね固まったらテストを書くという方が効率的だと感じる
自分は基本的にAPIのコードを書いてることの方が多いのでTDDは有効だと思う方が多かったが、たとえばAI Agentを実装したりライブラリの設計を考える時などは「最初にテストを書く」のはかなり難しいと感じたと感じた。まだ全体の処理や設計という抽象度の高い部分見えてない状態で、超具体のインターフェース(テストを書くということはつまりインターフェースを決めるということ)を決めるのはかなり難しいように感じた。(ただこれは問題を適切に分割をすれば解決する話なのかもしれない)
総じて、TDDにはプラスの側面の方が多いと思うが、全体の設計が見えてない時にはTDDは難しい、というのが自分の意見である。
熱いバトルを観戦してみて
二人の意見の相違に広がる設計の選択肢の中で、どれを選択するかは個人がコードを書くコンテキスト、ドメインに大きく依存する。これが医療ドメインのバックエンドのコードを書く時と、C向けアプリのフロントエンドを書く時では支持をする考え方が大きく変わってくるだろう(コードの特定という即物的な話だけでなく、一度書かれたコードが変わる頻度、リリースの頻度、チーム規模などを含め)。この二人の議論も、そのようなコンテキストの違いから生じているようにも思える。
いずれにせよ、このような設計の幅を広げてくれる議論は、そのような多様なバックグランドを持つエンジニアがどの考えを支持するかを考えるきっかけになる。この議論を踏まえて自分の考えが言語化されれば、コードレビューやペアプロの際の設計の議論にも大いに役立つし、仮に時間が経つにつれて自分の考えが変わったとしても、言語化による変化の気付きを得ることができる。
みなさんもJohn vs BobのBreaking Down並に熱いバトルを是非読んでみてください。
Discussion