はじめに
本記事は前回の記事である「ソフトウェア設計についてtwada技術顧問と話してみた 〜 A Philosophy of Software Design をベースに 〜 - NTT Communications Engineers' Blog」の続編です。 前回の記事の内容がベースとなっていますので、「APoSD って何だっけ?」という場合はぜひ前回の記事をご覧になってから、以下にお進みください。
ということで、後編の対話パートにさっそく入っていきましょう!
Pull Complexity Downwards
iwashi: APoSD では、複雑性を下に追いやる(Pull Complexity Downwards)という話が出てきます。何らかの処理が複雑になる場合、それを隠蔽してインターフェースを極力シンプルに保つ、というのがAPoSDの主張です。
こちらに関しても、社内勉強会中で「議論があるよな」と twada さんがコメントされています。具体的に、どのような議論があるのでしょうか?
twada: 「シンプルな実装よりも、シンプルなインタフェースを持つべきだ」に関しては、設計流派としては色々あるところです。
古くは、Worse is Better の話につながります。この話には、2つの設計の流派が出てきます。それぞれ「MIT/Stanfordアプローチ」と、「New Jerseyアプローチ」と呼ばれています。
両派とも「設計は実装とインターフェースの両面において単純でなければならない」と述べています。ここまではまあ当然のことですが、実装の単純さとインタフェースの単純さが両立できないときなどに両派の違いが出てきます。書籍『The Art of UNIX Programming』には次のように説明されています。
- 簡潔性 (Simplicity)
- MIT/Stanford: インターフェースが単純になることの方が、実装が単純になることより重要である
- New Jersey: 実装が単純な方が、インターフェースが単純なことより重要である。単純さが設計において最も重視されるべき事柄である
APoSDでは、シンプルなインターフェイスで複雑な実装を隠蔽するのが良いと説いています。コードが多少複雑になろうと正しいことをせよ。コードの複雑さを抽象によって隠蔽し、認知負荷を下げよ。これがAPoSDでいう、Deep Moduleになります。
これは、MIT/Stanford アプローチと呼ばれている流派に近いように思えます。いま話しているうちに気がつきましたが、本書はStanford大学の教科書で、著者は同大学の教授でしたね。ですから、MIT/Stanfordアプローチに寄った意見になっているのも頷けます。
MIT/StanfordアプローチとNew Jerseyアプローチは、正当性や一貫性に関してもポリシーがやや異なります。書籍『The Art of UNIX Programming』にはこう記されています。
- 正当性 (Correctness)
- MIT/Stanford: 設計はすべての点において正しいものでなければならない。誤りは許されない
- New Jersey: 設計はすべての点において正しいものでなければならない。ただし、どちらかといえば、正しいことよりは単純なことの方が重要である
- 一貫性 (Consistency)
- MIT/Stanford: 設計は一貫性を欠いたものであってはならない。一貫性を保つためには完全性や単純さを多少犠牲にしてもよい。一貫性は正当性と同じぐらい重要である。
- New Jersey: 設計は一貫性を大きく欠いたものであってはならない。単純さを保つために、一貫性は犠牲にされることがある。しかし、あまり起こらない状況に対応しようとして実装を複雑にしたり一貫性を欠いたものにするよりは、それを捨てる方がよい
他にもいろいろあるのですが、詳しくは書籍『The Art of UNIX Programming』や、私の講演資料(過去を知り、未来に備える。技術選定の審美眼 2019 edition)を読んでみてください。
もちろん APoSD は Worse Is Better 以降の議論を踏まえた内容になっていまして、なにより複雑性を最大の敵としていますから、「コードがいくら複雑になろうとも、正しいことをせよ」と言っている本ではないということは強調しておきます。複雑性を廃して、どう破綻なくソフトウェアを設計するかの本です。その上で、競合する制約に出会ったときに、どちらを取るか、またはどうバランスをとるかというところにポリシーが現れるのですね。
iwashi: このあたりの具体的な事例って、どのようなものがあるのでしょうか?
twada: New Jersey派が結果的に生き残ったサンプルとしては、歴史的には、LinuxとMINIX、C言語とLisp、gitとbazaar-ngなどがあります。gitなどは典型例と言えそうですね。他には、MongoDB vs RethinkDBなどもそう考えられます。
OSSの世界においては、歴史的にはNew Jersey派が生き残ることも多かったと考えています。
iwashi: 何が理由で生き残ってきたのでしょうか?
twada: 目の前の問題を解決しつつも、実装がシンプルなために、後日他の人が開発に参加できるハードルが下がるから、ですね。
iwashi: まさに、OSSと相性がいいですね。
twada: そういうことなんです。
実装の単純さを選択するときに、漏れのある抽象化があったとしても目の前の問題を解決できます。かつ実装がシンプルだと第三者が開発に参加するハードルが下がります。
これは、結果的には進化的な強さにつながります。つまり、変化に耐えて、生き残るための十分な数の目を得られる。
技術の歴史において、「なんでこっちの技術が生き残っているんだ?」みたいな話が出てきたときに、設計の一貫性・正しさ・美しさだけが生き残りを決めるファクターではない、という実例ですね。
New Jersey派はPrgramaticと表現できそうです。どちらかというとUnixカルチャー。Eric Raymond氏もそうですね。
その対立軸としてあるのが、MIT/Stanford アプローチであり「Do the Right Thing(正しいことをせよ)」という流派です。StanrfordのJohn Ousterhout教授もDo The Right Thing派に近いように思えますが、実はUncle Bobも同じ傾向がありますよね。
John Ousterhout教授とUncle Bobは批判し合っていて水と油のように見えますが、やや理想主義に寄っている点では似ています。現実よりは理想を選びがちであると。
iwashi: 世の中にあるソフトウェアを振り返って、「これってどっちに寄ってるんだろうな?」って考えるのは面白いかもしれないですね。
twada: そうですね。
実装をどれぐらい抽象で隠蔽すべきかというよりは、その発言をしている人はどういう影響下にいるのかが見えてくると誤読を避けやすくなりますし、過大評価も避けられるでしょう。
iwashi: 技術の歴史や、根っこにある流れを知っているとわかりやすいですね。
twada: 技術の歴史でいえば、もちろんMIT/Stanford流にインターフェースが優れていて生き残っている例もあるでしょう。
APoSDのShallowモジュールは、Functionalityが浅いものを意味します。New Jersey派は、過度に隠蔽しないことを重視します。漏れのある抽象化で良いので、あまり隠蔽しない方向になります。一方で、MIT/Stanford側はきちんと抽象化して認知負荷を下げる方向ですよね。
iwashi: どちらが唯一正しいわけではない、ってことですよね?
twada: はい。なのでAPoSD本は、色々と本を読んでみて、自分の中で最適のバランスを探すための1つの本だ、と考えると良いです。
Uncle Bobの本を読んで「そこまではやらないけどこういう考え自体は大事だよね」と一歩引いて読むのと同じで、John Ousterhout教授はこんなことを書いているが、一歩引いて読むことができれば、設計の世界が広がる。極論があるから全体の設計空間は広がるわけです。
iwashi: そういう意味では、もし若者がAPoSDだけ読むと偏りすぎるかもしれないですね。
twada: 素晴らしい本ですが、ある意味1つの流派の総本山に連なる本でもあるので、それが正解かというとそうでもない。だから、「色々読んでみましょう」という話になります。
私自身、本読みとしては、意見の強い本は好きです。たとえば、Bertrand Meyerのオブジェクト指向入門や、アジャイル関係でいえばJim Coplienもそうですね。そういった戦うことを辞さないベテラン論客たちは視野を広げてくれます。
ある一方に心酔せず複数の意見を見て、自分の設計としてそのどこかにバランスを見出すのが大事です。特定の状況によって設計の流派を使い分けられます。
たとえば、OSSなら抽象を深くしすぎないで、第三者の参加がしやすいようにする方向性もあるでしょう。なぜなら、New Jersey派のほうが生き残りやすいだろうという算段があるためです。
それに対して、プロプライエタリな製品開発・クローズドな開発であれば、実装の単純さではなく設計の完全性や一貫性のほうが大事かもしれません。
どちらに設計の重きを置くか、というのは状況によって変わってくるわけですね。
ミッションクリティカルなシステムなのか、それともエンドユーザーが多くても一時的に落ちるのは許容されるのかでも、ソフトウェア設計で重視する内容は変わります。正確性が大事なのか、堅牢性が大事なのか。文脈によって大事にすべき品質特性は異なります。ソフトウェア設計に唯一の正解というものはないんです。
もちろん基本となるレベル、たとえば「可読性の高いコード」などはどこであっても正しいんです。そこから先で両立しにくい設計上の制約に面したときにどういう設計を選択していくかというのはソフトウェア設計の一番難しいところ。これは学んでいくしかないでしょう。
急がば回れで、失敗から繰り返して学ぶほうが多い分野です。おすすめなのは自分が設計したシステムを長い期間(たとえば2年以上)メンテナンスすることです。自分の設計判断がどういう結果になるのか、オーバーエンジニアリングやアンダーエンジニアリング、失敗や成功を繰り返すことで経験が増えるので、設計の打率が高まります。
iwashi: リーダブルコード のような書籍は汎用的ですよね。
twada: そうですね。一般的なプログラミング原則はどこでも適用可能です。たとえば、良い名前付けは、どのような状況下でも重要でしょう。
ただ、たとえば関数プログラミングとオブジェクト指向プログラミングはパラダイムが異なり、得意分野もやや異なります。「何が正しい」というよりは「向いている・向いてない」があります。ですから、自分の道具箱を増やすのが良いですね。
iwashi: 道具箱のツールを増やすために何をしたらいいでしょうか?
twada: 書籍『達人プログラマー』に「年に1つプログラミング言語を学ぼう」という教えがあります。ちょっとずつパラダイムの違う言語をやるのがお勧めですね。
おわりに
本記事では、APoSDを起点として、MIT/StanfordアプローチとNew Jerseyアプローチについての対話を紹介しました。
当社では、このようなソフトウェア設計に興味があるソフトウェアエンジニアを募集しております! NTTコミュニケーションズ株式会社 ソフトウェアエンジニア の求人一覧 より、具体的なポストを是非ご確認ください!