CoC が苦手な奴、認知資源が乏しいだけ説

「設定より規約」「CoC (Convention over Configuration)」と呼ばれる設計思想がある。

フレームワークやライブラリ側で、ベストプラクティスなデフォルト設定を規約として決めておくから、利用する開発者はイチイチ考えなくていいよ、というワケだ。

コレは、ともすれば、「Opinionated (自己主張的)」なフレームワークやライブラリだとみなすこともできるだろう。

僕は CoC が割と好きだし、CoC か否かに関わらず、Opinionated なツールも好きである。だが、自分の回りでは「CoC が苦手」とか「柔軟性のないフレームワークは嫌い」といった人が多く、疑問を持っていた。

今回、どうして CoC が苦手な人がいるのか、どういうツールだったら好みなのか、それはどうして起こるのか、といったことを、いくつかの文献から考えてみた。

目次

CoC の特徴

CoC を採用していることで一番有名なのは Rails だろうか。「Rails Way」といわれるくらい、Rails の「レール」が敷かれていて、利用者はそれに従うことになる。

一番最初は、ディレクトリ構成などにも暗黙的にルールがあり、そのルールを知らないと、「どうしてこの記述だけでこのファイルとこのファイルが連携するんだ?」といったことが分からなかったりする。

一度ルールを覚えてしまえば、そのルールに従って思考停止で実装が進められるので、そこまで到達すれば生産性は高いと思う。僕はココでの生産性の高さ、成果物の一貫性をメリットに感じている。

2021-11-10 : ちなみに、「ルールをただただ覚えればいい」とは思っていないし、そのやり方じゃ当然 CoC が性に合わなくて当然だと思う。そのツールの内外ともども、世間的なプラクティスや流派を知っておいたうえで、設計思想から込みでルールを「理解」することが大前提

こういうところを「理屈を知らず覚えればいいだけ」みたいな暗記モノだと思ってる奴は、後述するように「自分が考えた最強の設計」で周りに迷惑をかけるし、世界レベルの設計思想を見聞きして理解したうえで、「それでも自分で考えることが大事」だと思ってるんなら、よっぽど自信家で自分のこと見えてないんだなーと思うよ。

ちなみに、CoC を採用している他の例としては以下あたり。

Maven や Gradle などのビルドツールが毎回辛いのは、そのビルドツールの「規約」があるからだろうなと。Node.js でも Grant・Gulp・Webpack とそれに近い経過を辿った時期があった。何か「一強」のツールが決まった分野って、皆が疲れた頃に妥協点が見い出せたモノなんだと思う。

規約よりも設定?

CoC が辛い?じゃあ、「規約」よりも「設定」が良いということか。そういったツールも多いので見てみよう。

Explicit is better than implicit.

「明示的であることは暗黙的であるより優れている」。自分は、この意見にも同意である。

僕は、イチイチ書かなくても同じ意味になるような「デフォルト値」であっても、デフォルト値として明示的に書くことは多い。将来のバージョンではデフォルト値が変わるかもしれない。他の開発者が「デフォルト値をわざわざ採用した」「検討した結果がたまたまデフォルト値と同じだった」ことに気付かず実装をむやみに変えてしまうかもしれない。そういったリスクを考慮すると、僕は冗長だろうが何だろうが、自分が意図したことは全部漏らさず書くことが良いと思っている。

CoC を嫌う人が、じゃあそういった「デフォルト値」などを明示的に実装しているか? というと、そういう人は僕の回りでは見かけない。むしろ明示すべき実装をサボったことで、デフォルト値を勘違いしていてバグを引き起こしたり、無駄なコードを書いていたりする低品質な場合の方が多い。

僕が最初に触れた Java のフレームワークは Struts 1 だった。Struts 1 は全ての情報が XML という「設定」ファイルに書かれていた。うんざりするほどの文量があり、ほとんどは定型的な記述だが、その内容を少しでも変えれば、設定の紐付けや構成を大きく変えられてしまう。「このプロジェクトだけは web.xml にこの記述があるので、この設定は無効化されていて…」といったことが多かった。

設定ファイルがあれば良いのか?CoC よりも生産性が高いのか?というと、そうでもない。むしろ、Struts 1 のような「設定地獄」にウンザリした結果、「規約」を作れば「設定」が減らせて、生産性が向上するよね、ということで CoC が生まれたはずだ。

ちなみに、Terraform や Kubernetes など、何かロジックに基づいて結果が動的に変わるモノではない、「リソース定義」系の分野では、「宣言的」に全てを記述することが好まれる。文量の多さは YAML という形式で誤魔化してはいるが、やはりほとんど全てを明示的に書くので、文量は増えがちだ。しかし、「何も書かなければ規約に基づいてこうなる」といったロジックはあまり多くはない印象。

Google のビルドツール「Bazel」も、他のビルドツールと比べて「形式的にでも書かないといけないモノ」は結構多い印象だ。

設定ファイルが存在する、明示的に書く、というやり方は、冗長さは感じるかもしれないが、「書いたとおりに動く」ことが期待できるし、全部読めば分かる状態にあるので、低スキルのメンバに暗黙知を求めずに済むことは良い点だと思う。全部そこに書いてあるのだから、それが読めさえすれば使えるでしょ?ということで、最低品質は保ちやすい仕組みだと思う。

僕も、チーム開発のしんどいところはスキル差だと思っていて、スキル差があって仕方ないのはその案件特有の業務知識だけにしたいと思っている。プログラミング言語やツールに関するスキル差を減らし、誰でも同じように開発できる状態が、品質を「保つ」ためには必要で、そのためには「読んでもらえば誤解なく分かってもらえる」方が良い。暗黙的な規約は勘違いが起こらないとも限らないので、「書いてあるとおりに従え」という方が、思考停止できる領域が多く、最低品質を底上げしやすいと思っている。勿論、設定地獄は辛いし、文字も読めない連中も多くて中々理想どおりにはいかないが、少なくとも CoC がベースのツールを採用する現場よりは、秩序が保たれやすいことを感じてきた。

CoC の悪い点

CoC の何が悪いのか。それは、守るべき規約が明言されていないところだろう。より正確には、どこに規約があるのか極めて分かりづらいことにあると思う。

また、Rails に関しては、Ruby の言語仕様も相まって、美しく書く「文化」でしか質を担保できない点が強いのは頷ける。型定義がないので、命名とテストの質が重要になる。そしてコレは極めて属人的な要素だ。

ルールは違えど、Java や Python のように、ある程度、構文で型やスコープなどを制御できれば、可読性や読解スピードは上がるのだと思う。「大文字から始まるからクラス」「アンダースコアがついてるからプライベート」みたいな感じだ。コレも「規約」ではあるのだが、目で見える形で規約がコードに現れているので、思い出しやすいのだ。Ruby on Rails だと、コードには一切書かれていないが、規約によってそのように動くことが多いので、思い出すきっかけがコード上になく、コードリーディング時に思考のコストがかかる気持ちは理解できる。

Ruby on Rails とセットで「テスト駆動開発 (TDD)」が語られがちだったのは、こうしたデメリットを補う方法がテストだったから、とも考えられるかもしれない。

ただ、個人的には、「じゃあ型定義が使えりゃいいのか」というと、別にそうでもないと思う。言語仕様的に型定義が出来ることと、誰かが書いたその型定義を読んで、読み手が構造や実装をすんなり理解できるかどうかは全く別だ。型・クラスの分割粒度だったり、命名の良さはやはり問われるワケで、この設計スキルとネーミングスキル (センスではない) がないのであれば、隠蔽の仕方が違うだけで結局分かりづらいのだ。

Opinionated / Un-opinionated

CoC よりも広義な意味合いで、設計思想として「Opinionated」なツール、「Un-opinionated」なツールがある。そのツールが打ち出す設計思想への強制力が強いか、もう少し柔軟性があるか、といった違いである。

例えば Angular は、SPA 3強の中で唯一「フルスタック」な「フレームワーク」であり、デザインパターンの推奨がそこそこ強く打ち出されている。Angular CLI を使わずに実装することはかなり難しい。

一方、React や Vue は、どちらかというと「UI ライブラリ」に過ぎない。そのため、SPA として一つのアプリを構築しようと思うと、他のライブラリの併用は必須となり、そこの設計は Angular と比べて柔軟性がある状態だ。つまり、Un-opinionated なライブラリであり、デザインパターンは自分で作る必要があるということだ。

Django は割とフルスタックで、Flask はそれと比べて小規模で柔軟性がある、というのも近い比較だろう。「フレームワーク」は「骨組み」という言葉のとおり、世の多くのフレームワークは、ある程度の「制約 ≒ Opinionated な設計思想」を打ち出すことで、生産性向上・成果物の一貫性を狙っている。一方で「ライブラリ」は、狭い領域での隠蔽、自動化を図ることで、その範囲内の生産性向上を狙っているものであり、システム全体のような大きな範囲での設計思想には踏み込まないがために、結果的に「システム設計」で柔軟性が生まれるワケだ。

他に、Next.js は「設定より規約」と「規約より設定」のハイブリッドともいわれていて、規約は用意しておきながらも、選択肢は多く用意してある。

選択肢が多く多様性は増すワケだが、それは「ある問題を解決する方法が多数存在し、一貫性が低下する」事態を招く。

規約を覚え続けるための「認知資源」

色々調べていたら、認知資源という言葉に行き着いた。

【認知的節約】
人は、必要以上に認知資源を用いない傾向があることを示す。

【認知資源】
注意を向けて考える、記憶する、といった認知活動に要する能力。認知心理学では、「認知資源には一定の限界があり、そのために認知的節約が起こる」とする。

「注意資源」などとも言われて、要するに、「ずっと意識していないといけないこと」「覚え続けておかないといけないこと」が多いと疲れるので、人はそうした要素を省きたがるということだ。

人は楽をしたがる生き物。人の「忘れる」という仕組みも、「認知的節約」を引き起こしている一因かもしれない。

「認知的節約」という仕組みが人間に備わっていなければ、それはそれで生きるのが大変だと思う。家を出る瞬間ひとつとっても、「財布は持ったか」「これから鍵を締めるが、ドアノブに故障はないか」「コンロの火は消したか」などなどの「注意」を、全て覚えていて、それを思い出していて、それから行動を決める、といった動きが必ず発生するような生き物だったとしたら、いつまでも家から出発できないだろう。時には忘れ物をする、うっかりやらかす、それくらい「抜けている」というか、「抜かす」ことで、家を出た後に行うべきタスクにより注意を向けられて、より創造的な暮らしが出来ているのだと思う。

意識していないとそうした注意は忘れがちで、そもそもその「意識する」こと自体が脳にとってはコストなので、自然と意識しないように楽を選ぶ習性が人にはあるということなのだろう。


しかし、だ。

「財布は持ったか」を忘れる程度なら、家に取りに帰ればいいので大きなトラブルにはならない。でも、「コンロの火は消したか」といった注意は、毎回常に忘れたらいけない注意だと思う。なぜなら火を消し忘れた時の被害が甚大過ぎて、取り返しのつかないことになるからだ。

自転車やクルマの運転が下手クソな人も同じ。事故を起こしたら最悪人が死ぬのに、注意が行き届かなくて、注意し続けることが出来なくて、ヒヤリハット、軽微な事故を繰り返す、ADHD 気味な人はまぁまぁいる。病気だろうがなんだろうが、忘れたらいけないモンは忘れちゃいかんし、毎回必ず注意すべきことはあるのだ。


じゃあ、どこまで忘れて良いことにするか。どこまでは覚え続けておいてもらうか。プログラミングの話に戻る。

…コレが、「CoC が苦手」という言葉の真意なのかもしれない。「私は他人が決めたルールを覚え続けることが出来ません」というワケだ。

この場合、問題の根幹は「覚え続けることが苦手」なので、規約が少ない代わりに設定ファイルが多くても、そういう人はやっぱり嫌がる。「どの設定ファイルがエントリポイントで、どの設定ファイルに連携して、何が決まっていって…」といった繋がりを、ある程度覚えていないといけないからだ。「CoC が苦手」と発言した人の口から、「Struts は設定ファイルが多くて嫌だよね」といった発言も、同時に聞いたりするのはこういうことだろう。

認知資源が乏しい人は何が出来るのか・何をしているのか

Convention 優位も Configuration 優位も、どっちも駄目な人は、じゃあどうやってプログラミングしているのか。

そういう人がどうしているか見てみると、車輪の再発明をよくやっている

どこぞで拾ってきた「楽できるツール」を闇雲に導入し、「俺が考えた最強の設計」をプロジェクトに取り入れていたりする。

「Rails は覚えることが多くて使いづらいよね」「Angular は書くことが多くて冗長だよね」そういって、オレオレ設計で React ベースのフロントエンドに、駆け出しのライブラリやフレームワークを好き勝手に導入してプロジェクト雛形を作り上げる。テメー個人が想像したことをどこにも文書化していないので、コイツの考える「規約」が誰も分からなくて、開発生産性は低く、保守性も極めて悪い。柔軟性ばかりで思想のないライブラリはすぐに開発停止していて、半年後に EOL を迎えた実行基盤に合わせてバージョンアップしようとしたら詰む。そんなことの繰り返しだ。

日本の隅っこで無名の開発者が一人で思い付いた程度の設計が、Google とか Facebook とか名だたる開発者達が長い年月をかけて、よってたかって作り上げたフレームワークやライブラリが提示する「規約」に勝っているはずがない。そうなのだが、彼らがやっているのはそういうことなのだ。何故か?

こういう奴は何をやっているのかというと、本人の認知資源を節約しているのだと推測する。

人様が考えたルールを覚え続ける能力はないので、代わりに過去の自分自身の思考パターンを流用することで、設計を覚え続けるコストを削減しようとしているのだ。「『俺が考えた最強の設計』のことは忘れてしまったが、俺だったらこう考えていたはずだろ?ほら合ってた」という楽な判断をしたくて、自分で設計しているワケだ。

そこの認知で「自分が楽をしたい」のが第一目的だから、設計した内容を文書化してチームメンバに展開するようなことは面倒臭がってやらない。自分が楽したいがために、回りに迷惑なコストを払わせているのだ。決して、高い品質で設計や実装をしようという思いから「オレオレ設計」を持ち込んでくるワケではない。ソイツは世界的なプラクティスを学ぶ能力がなく、自分が考えたことしか理解できないから、そういうことをするのだ。そうすることで自分の少ない認知資源を確保し、自分の生存戦略を保とうとしているワケだ。

認知資源の無駄遣いを減らせば、その分より創造的なことに頭が使える、という言い分は至極当然、よく分かる。しかし、自分よりも賢い人達が考え抜いた「規約」は、決して「覚えずに済ませていいこと」ではない。むしろ、それらを覚えて吸収することで、自分の一般的な技術スキルはさっさと世界レベルに合わせに行って、その上で案件固有の問題解決にリソースを割く方が、「オレオレ設計と案件対応」を毎回常に両方やるよりも結果的に楽できるではないか。

僕は「チーム全体の最低品質を守る」ために「明示的な記述」と「CoC」を併用する

以上のとおり、

というパターンが見受けられるのではないか、と僕は推測した。

一方、CoC や Opinionated なツールに抵抗がない自分は、多分そこらの人よりも「色んなことに同時に注意力が向く」し、「サボらずに注意し続ける」ことに対するコストが自分の中で少ないのだと思う。人よりも認知資源の余裕が大きい、といえるのかもしれない。自分でいうのもなんだが、自分にはその気があると思う。

僕は性格や基礎スキルで優位なんだと思う。恵まれているってことなんだと思った。

また、前職は数千人規模の大企業で、人数の多いチーム開発の経験がそこそこあるので、中小に転職した現在も、おおよそ次のような考えで過ごしていると思う。ちょっと極端な書き方をするけど:

現職は中小企業のマインドが強くて、慢性的に属人化しているし、一個人の能力以上にはスケールしない仕事の仕方をしているように見える。自走型の組織を目指している、というと聞こえは良いが、「各々自分で育ってください」と投げやりなだけで、教育制度も全然機能していないし、年長者に OJT のスキルもないままだ。結果的に、生産性はもっと上げられるだろうに上がっていないし、スキルの伸びも個人のやる気に依存していて、チームの統率が取れていないことが多々ある。

コレは、大企業特有の、保守的な考え方なんだろうな。でも、中小がコレから事業を大きくしていこうとしていくなら、一人のやる気に依存せず、一人の限界を超えた成果を出すための仕組みを作らないといけない。

それなのに、私個人は認知資源が乏しいのでオレオレ設計をします、っていうのは、被害を受けるメンバが多くてちっとも全体最適が図れないのだ。

別に全てにおいて CoC を採用する必要はなくて、適材適所でハイブリッドにやっていけばいいと思うんだけど、「CoC は嫌」っていう人間だけは排除した方が良い。

設定よりも規約を重視することで、

といった効果が狙える。

そして、こうした効果を狙い、チーム全体の効果を上げていくために、設定や宣言を明記することがより効果的になる。

CoC が嫌いだという奴は、メリデメを個人のスキルに依存して語っており、規約を覚えることも設定を明記することもどちらもサボるから迷惑なのだ。単に新しいことを覚えて徹底する自律心がないだけやんけ。