CoC が苦手な奴、認知資源が乏しいだけ説
「設定より規約」「CoC (Convention over Configuration)」と呼ばれる設計思想がある。
- 参考 : 設定より規約 - Wikipedia
フレームワークやライブラリ側で、ベストプラクティスなデフォルト設定を規約として決めておくから、利用する開発者はイチイチ考えなくていいよ、というワケだ。
コレは、ともすれば、「Opinionated (自己主張的)」なフレームワークやライブラリだとみなすこともできるだろう。
僕は CoC が割と好きだし、CoC か否かに関わらず、Opinionated なツールも好きである。だが、自分の回りでは「CoC が苦手」とか「柔軟性のないフレームワークは嫌い」といった人が多く、疑問を持っていた。
今回、どうして CoC が苦手な人がいるのか、どういうツールだったら好みなのか、それはどうして起こるのか、といったことを、いくつかの文献から考えてみた。
目次
- CoC の特徴
- 規約よりも設定?
- CoC の悪い点
- Opinionated / Un-opinionated
- 規約を覚え続けるための「認知資源」
- 認知資源が乏しい人は何が出来るのか・何をしているのか
- 僕は「チーム全体の最低品質を守る」ために「明示的な記述」と「CoC」を併用する
CoC の特徴
CoC を採用していることで一番有名なのは Rails だろうか。「Rails Way」といわれるくらい、Rails の「レール」が敷かれていて、利用者はそれに従うことになる。
一番最初は、ディレクトリ構成などにも暗黙的にルールがあり、そのルールを知らないと、「どうしてこの記述だけでこのファイルとこのファイルが連携するんだ?」といったことが分からなかったりする。
一度ルールを覚えてしまえば、そのルールに従って思考停止で実装が進められるので、そこまで到達すれば生産性は高いと思う。僕はココでの生産性の高さ、成果物の一貫性をメリットに感じている。
2021-11-10 : ちなみに、「ルールをただただ覚えればいい」とは思っていないし、そのやり方じゃ当然 CoC が性に合わなくて当然だと思う。そのツールの内外ともども、世間的なプラクティスや流派を知っておいたうえで、設計思想から込みでルールを「理解」することが大前提。
こういうところを「理屈を知らず覚えればいいだけ」みたいな暗記モノだと思ってる奴は、後述するように「自分が考えた最強の設計」で周りに迷惑をかけるし、世界レベルの設計思想を見聞きして理解したうえで、「それでも自分で考えることが大事」だと思ってるんなら、よっぽど自信家で自分のこと見えてないんだなーと思うよ。
- Masayoshi TakahashiさんはTwitterを使っています 「Railsの場合、レール≒規約に乗ることで設計をある程度さぼれるのは便利ではあるけど、設計手法を衰退させることにつながってしまった元凶みたいな側面もあるので難しい(けど設計好きな人は無駄に複雑な設計を導入しがちなのでアンチテーゼとしては重要だったという側面は評価せざるをえない)」 / Twitter
- Masayoshi TakahashiさんはTwitterを使っています 「PoEAAを今読むと、Active Recordは単なる過渡期の捨て駒でしかなくて、本命はあくまでData Mapperであり、まともで安価なData Mapperが出ればActive Recordは捨てられるはずみたいな雰囲気なのは割と驚くかもしれない」 / Twitter
- Masayoshi TakahashiさんはTwitterを使っています 「それは結局、永続化ストレージとしてのDBを自分が掌握してしまえば複雑なmapperはなくても戦える、というRailsの判断があったようにも思うので、その意味ではDB Migration機能がRailsの秘密だったのかもしれない」 / Twitter
- Masayoshi TakahashiさんはTwitterを使っています 「それでいくと、フロントエンドがアプリケーションそのものになるには永続化ストレージとそのロジックをどう握るかが問題になるはずで、西谷さんのAWS Lambda的なものをごりごり書く事も含めてフロントエンドと再定義すればあるうるけど、さすがに正直「サーバサイドにようこそ!」感が強い」 / Twitter
- レールに乗ると設計をサボれる → 設計手法が衰退した? ← 設計好きな人が作る複雑な設計へのアンチテーゼ
ちなみに、CoC を採用している他の例としては以下あたり。
- CakePHP、Laravel、Symfony (PHP)
- Spring Framework。Gradle、Maven (Java)
- ASP.NET
- Grok、Django (Python)
- Meteor、NestJS、Ember.js (Node.js)
Maven や Gradle などのビルドツールが毎回辛いのは、そのビルドツールの「規約」があるからだろうなと。Node.js でも Grant・Gulp・Webpack とそれに近い経過を辿った時期があった。何か「一強」のツールが決まった分野って、皆が疲れた頃に妥協点が見い出せたモノなんだと思う。
規約よりも設定?
CoC が辛い?じゃあ、「規約」よりも「設定」が良いということか。そういったツールも多いので見てみよう。
- Zen of Python
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 気味な人はまぁまぁいる。病気だろうがなんだろうが、忘れたらいけないモンは忘れちゃいかんし、毎回必ず注意すべきことはあるのだ。
じゃあ、どこまで忘れて良いことにするか。どこまでは覚え続けておいてもらうか。プログラミングの話に戻る。
- Rails のように多くの規約を覚えないといけないツールは、必然的に利用者の「認知資源」を使うことになる
→ 多くの人は、認知資源を節約したがる
→ 認知的節約を狙うため、「覚え続けておく必要があること」が多いと抵抗感が生まれる
…コレが、「CoC が苦手」という言葉の真意なのかもしれない。「私は他人が決めたルールを覚え続けることが出来ません」というワケだ。
この場合、問題の根幹は「覚え続けることが苦手」なので、規約が少ない代わりに設定ファイルが多くても、そういう人はやっぱり嫌がる。「どの設定ファイルがエントリポイントで、どの設定ファイルに連携して、何が決まっていって…」といった繋がりを、ある程度覚えていないといけないからだ。「CoC が苦手」と発言した人の口から、「Struts は設定ファイルが多くて嫌だよね」といった発言も、同時に聞いたりするのはこういうことだろう。
認知資源が乏しい人は何が出来るのか・何をしているのか
- 暗黙の規約を注意し続けられないから CoC は苦手
- 設定を覚え続けていられないから「設定地獄」も苦手
Convention 優位も Configuration 優位も、どっちも駄目な人は、じゃあどうやってプログラミングしているのか。
そういう人がどうしているか見てみると、車輪の再発明をよくやっている。
どこぞで拾ってきた「楽できるツール」を闇雲に導入し、「俺が考えた最強の設計」をプロジェクトに取り入れていたりする。
「Rails は覚えることが多くて使いづらいよね」「Angular は書くことが多くて冗長だよね」そういって、オレオレ設計で React ベースのフロントエンドに、駆け出しのライブラリやフレームワークを好き勝手に導入してプロジェクト雛形を作り上げる。テメー個人が想像したことをどこにも文書化していないので、コイツの考える「規約」が誰も分からなくて、開発生産性は低く、保守性も極めて悪い。柔軟性ばかりで思想のないライブラリはすぐに開発停止していて、半年後に EOL を迎えた実行基盤に合わせてバージョンアップしようとしたら詰む。そんなことの繰り返しだ。
日本の隅っこで無名の開発者が一人で思い付いた程度の設計が、Google とか Facebook とか名だたる開発者達が長い年月をかけて、よってたかって作り上げたフレームワークやライブラリが提示する「規約」に勝っているはずがない。そうなのだが、彼らがやっているのはそういうことなのだ。何故か?
こういう奴は何をやっているのかというと、本人の認知資源を節約しているのだと推測する。
人様が考えたルールを覚え続ける能力はないので、代わりに過去の自分自身の思考パターンを流用することで、設計を覚え続けるコストを削減しようとしているのだ。「『俺が考えた最強の設計』のことは忘れてしまったが、俺だったらこう考えていたはずだろ?ほら合ってた」という楽な判断をしたくて、自分で設計しているワケだ。
そこの認知で「自分が楽をしたい」のが第一目的だから、設計した内容を文書化してチームメンバに展開するようなことは面倒臭がってやらない。自分が楽したいがために、回りに迷惑なコストを払わせているのだ。決して、高い品質で設計や実装をしようという思いから「オレオレ設計」を持ち込んでくるワケではない。ソイツは世界的なプラクティスを学ぶ能力がなく、自分が考えたことしか理解できないから、そういうことをするのだ。そうすることで自分の少ない認知資源を確保し、自分の生存戦略を保とうとしているワケだ。
認知資源の無駄遣いを減らせば、その分より創造的なことに頭が使える、という言い分は至極当然、よく分かる。しかし、自分よりも賢い人達が考え抜いた「規約」は、決して「覚えずに済ませていいこと」ではない。むしろ、それらを覚えて吸収することで、自分の一般的な技術スキルはさっさと世界レベルに合わせに行って、その上で案件固有の問題解決にリソースを割く方が、「オレオレ設計と案件対応」を毎回常に両方やるよりも結果的に楽できるではないか。
僕は「チーム全体の最低品質を守る」ために「明示的な記述」と「CoC」を併用する
以上のとおり、
- 認知資源が乏しいエンジニアの生存戦略として、柔軟性を重視したライブラリを採用することでオレオレ設計をしたがる ⇔ 覚えることが多い Opinionated なツール、CoC を採用したフレームワークを避けたがる
というパターンが見受けられるのではないか、と僕は推測した。
一方、CoC や Opinionated なツールに抵抗がない自分は、多分そこらの人よりも「色んなことに同時に注意力が向く」し、「サボらずに注意し続ける」ことに対するコストが自分の中で少ないのだと思う。人よりも認知資源の余裕が大きい、といえるのかもしれない。自分でいうのもなんだが、自分にはその気があると思う。
- 決してスティーブ・ジョブズに憧れているワケではないが、洋服選びには認知資源を使っていない。「ファッションには興味がないし、毎日考えたくない」という思いから、どう組み合わせてもいいような洋服しか選んでいなかったりする
- 元来神経質であるために、義務感や疲労感を覚えることなく、「毎回ココを間違えないように注意する」みたいなことをやるのは得意。苦なく当たり前にできる。神経質な自分が安心できるので、なんなら率先して自主的にやっているぐらい
僕は性格や基礎スキルで優位なんだと思う。恵まれているってことなんだと思った。
また、前職は数千人規模の大企業で、人数の多いチーム開発の経験がそこそこあるので、中小に転職した現在も、おおよそ次のような考えで過ごしていると思う。ちょっと極端な書き方をするけど :
- メンバ間のスキル差を埋めるには、出来る人によるリードを活性化するのではなく、出来ない人を底上げするマネジメントの方が大事
- 自分一人が楽になるより、チーム全員が楽になる手段を探した方が、結果的に自分も楽になれる
- 人数に比例して「対話によるコミュニケーション」の時間が取りづらくなるので、話さなくても分かる仕組みを探すようになる
- その方法として、「規約を作り誰でも読めるように展開しておくこと」「明示的に実装し暗黙知を作らないこと」を徹底する
- チームの人数が多いのに作業が属人化していると、クリティカルパスの被害影響が大きくなるので、属人化しないようにする
- 誰でも同じ結果を出せるようにするには、「個人的な思考・思想」を混ぜないことが大事 → 思考停止していても出来るような仕組み作りが大事、ということになる
- 個人的な思想を避けるには、一般化された知識、「参考文献」が示せる知識を利用する。皆この本を読んでね、みたいな形で知識共有できる権威に頼る → OSS が示す Rail・Way に従うのもコレと同じ
現職は中小企業のマインドが強くて、慢性的に属人化しているし、一個人の能力以上にはスケールしない仕事の仕方をしているように見える。自走型の組織を目指している、というと聞こえは良いが、「各々自分で育ってください」と投げやりなだけで、教育制度も全然機能していないし、年長者に OJT のスキルもないままだ。結果的に、生産性はもっと上げられるだろうに上がっていないし、スキルの伸びも個人のやる気に依存していて、チームの統率が取れていないことが多々ある。
コレは、大企業特有の、保守的な考え方なんだろうな。でも、中小がコレから事業を大きくしていこうとしていくなら、一人のやる気に依存せず、一人の限界を超えた成果を出すための仕組みを作らないといけない。
それなのに、私個人は認知資源が乏しいのでオレオレ設計をします、っていうのは、被害を受けるメンバが多くてちっとも全体最適が図れないのだ。
別に全てにおいて CoC を採用する必要はなくて、適材適所でハイブリッドにやっていけばいいと思うんだけど、「CoC は嫌」っていう人間だけは排除した方が良い。
設定よりも規約を重視することで、
- 似たような設定の繰り返しを防げる
- 規約により一貫性をもたらせる
- 似て非なる (微妙な差異の) 設定を排除できる
- チーム内の対面コミュニケーションを減らせる
- 共通認識を作り、誤解を生みにくい
- 場当たり的な OJT を避け体系的に教えられるようになる
- 個々人が自分の判断で成果物を作らなくなる
- 規約でお決まりの場面は思考停止できるので「認知資源を節約」できる
- スキル差が合っても最低品質が担保しやすくなる
といった効果が狙える。
そして、こうした効果を狙い、チーム全体の効果を上げていくために、設定や宣言を明記することがより効果的になる。
- 「規約」をドキュメントオンリーで表現するのはコードとの距離が離れて現実的にはしんどい。コードから規約を拾い上げやすくしておく
- 暗黙の「値」(デフォルト値) を安易に使うことでバグを引き起こすリスクを避け、成果物に責任を持った実装力を身に付ける
CoC が嫌いだという奴は、メリデメを個人のスキルに依存して語っており、規約を覚えることも設定を明記することもどちらもサボるから迷惑なのだ。単に新しいことを覚えて徹底する自律心がないだけやんけ。