ドキュメントが書けていればプログラミングは省力化できる
プログラミングにおけるスキルというのはたしかにあって、直接的な成果物を作る工程なので一つも疎かには出来ない。でも、ドメイン駆動設計だとかクリーンアーキテクチャだとかいう考え方を勘違いしたまま固執していて、実装工程から頭を使えばいいんだ、設計段階のドキュメントは要らないんだ、みたいに思っているバカタレが身近には割と居る。
コイツらはそうした設計原則を腹に落とし込めていないから、最初からイビツなことをやっていて、ちょっとでも当初の想定と異なる要件が出てきた時に支離滅裂な実装をし始める。ソイツの中で「当初の想定」だと思い込んでいるモノは文書化されておらず、ソイツの頭の中で日々思い付きで変わっていっているので、「立ち返るべき原点」が常にブレていて存在しないのだ。
結果、本人にしか理解できない、「その人だけがクリーンアーキテクチャと呼ぶ何か」になっていて、他の人が手出しできない「難解で高尚な」スパゲッティが出来上がる。コレは皮肉であって一つも褒めてねえから勘違いすんなよ?「私には理解できないですよ~」ってのは相当な dis だからな。
僕が見てきたことでいうと、そういう大仰なアーキテクチャや考え方を持ち出す前に、もっとシンプルな考え方で対処すれば事足りる場合が多いと思う。
- 外部に見えるインターフェース (画面やレスポンスされる JSON) と、ビジネスロジックを扱うクラスファイルを明確に分離する
- SPA なら View Component (画面表示) と Service (API コールなどのビジネスロジック担当) の分離
- API なら Routing Controller (エンドポイント) と、DB Entity (ORM が DB アクセスする部分などのモデル定義) と、Service (DB アクセス等を制御するビジネスロジック) の分離
- View や Controller の立場から Service を呼び出すことはあっても、Service から View や Controller を呼び出すことはない。Service の関数は単独で呼び出して動作し、サーバやフロントエンドがなくとも実行可能 (ユニットテスト可能) な構造にする
- 単一責務の原則を守る
- 1つのクラスや1つの関数が複数のことをやらないように関数を小さく作る。これら関数は
private
にする - 最終的な「処理フロー」として束ねる関数だけが、
private
な関数を取りまとめてpublic
な関数として MVC を横断して呼び出せるようにしておく - 何でもかんでもスコープを広げて公開せず、小さく閉じておけば良い
- 1つのクラスや1つの関数が複数のことをやらないように関数を小さく作る。これら関数は
- 機能をまたいだ Service (ビジネスロジック) を出来るだけ設けない
shared
やcommon
といった単語が可能な限り出てこないようにする。多くは共通化すべきではない事柄を、「パッと見が似ているから」というだけで深く考えずに束ねている- なんなら本当に共通化できるとしても、あえてしないで残す方が良いことさえあると思う。一度共通部品化したモノを、後の改修で分離し直すようなリファクタは多くの人が苦手とし、「共通関数の中に条件分岐が登場する」という悪手を生みがち
- 本当は「共通」なのではなく「コア
core
」機能なのでは?それは機能をまたいで共通利用するのではなく、そのシステムの根幹にあるモノであり、扱うレイヤーが違う
- 命名が正しければ読み取れる、コメントをちゃんと書いていれば理解される
- ドキュメンテーションコメントに「入出力の型」「やること」「やらないこと」「例外」を書いておけばコードを読む必要がなくなるのに、コメントを書かないから実装を追わないといけなくなる
- 英語の品詞が無茶苦茶で、変数名がクソだから何をやっているのか理解できず、頑張って読み解こうとしないといけなくなる
- 実装の仕方自体が多少クソでも、コメントと命名がしっかりしていれば、やろうとしていることは読み取れる
「関心の分離」「単一責務の原則」「名前重要」「ドキュメントちゃんと書こう」っていう当たり前のことしか言ってない。でも、こういうことが出来ずに DDD とか CA とか抜かしてても何も実現されない。逆にコレさえできれば、DDD だの CA だの言わんでもそこそこの規模のシステムは作れる。お前らが大好きな「マイクロサービスアーキテクチャ」でやっていきたいんだろ?だったら一つのサービス (アプリケーション) の責任範囲を小さく作ればいいだろ?「単一責務原則」を語るレイヤーが一階層上がっただけやぞ。それも出来んから「既存のアプリに別機能を付け足しましょう」とか言い出すんだろ。それパフォーマンステストやったか?
何をもって1機能とするか。それにどういう名前を付けて、何と何を区別するか。
関心を分離するには「型定義」が最も分かり良い区分け方になるだろう。インターフェースをどう区切るか。
こういう設計って、いきなりコードを書き始めちゃうと拡張性に乏しくて、追加要件や機能改修が発生した時にすぐに破綻するんだよ。システム全体での設計思想からブレイクダウンせず、一つのファイルをエディタで開いて考え始めちゃうからダメなの。
システム全体の設計思想やルールごとというのは、コードに起こすことは不可能で、絶対にドキュメントを書かないと残せない。
そんなことは1・2年システム開発を経験していれば分かることなんだが、「日本語の作文が苦手で絶対にドキュメント書きたくないマン」はこの事実を認められず、できるだけドキュメントを書かずにコーディングする方法を模索し続けている。何年も上手くいかなくて毎回案件を炎上させてるんだしそろそろ気付こうよ。その期間に日本語勉強すればよかったんじゃない?コンビニバイトの外国人の方がまともな日本語喋ってるぞ。
「プログラムの行レベルの詳細設計書」には意味がないと思っている。「配列から特定の要素を抽出する」のに、for
と if
で書くのか、高階関数で書くのか、といった指示書だとしたら、基本的には要らない。
API の入出力の型を定める「インターフェース定義書」はあるべきだが、Controller クラスや Service クラスといった「クラス定義書」までは要らないだろう。それよりは「機能一覧」をベースに「API 一覧」や「画面一覧」を起こし、システムが持つ機能を一覧化し、命名規則を決めておくことで自動的にクラス間の繋がりが表明できるようにしておく方が分かり良いだろう。
こういうドキュメントを書くには頭を使う必要があるのは当然で、それこそが「設計」という仕事なのだ。勿論簡単ではない。先々の機能追加や、まだ検討されていないことも想定して拡張性や保守性を考えながら、今のアーキテクチャを構成していかなくてはならない。
こういう設計をサボってコードを書き始めるとどうなるかというと、一番実害のある問題は、「例外処理」の考慮がほとんど欠落しているためバグだらけのモノしか出来上がらない、ということだろう。画面ごとに表示方式が違うだとか、API ごとに命名が無茶苦茶で使いづらいとか、そんなのは可愛いレベル。そもそも正常系すらまともに動かないクソに陥るのが関の山なのだ。実装工程でその不出来を誤魔化していると、システムテストの段階で大量のバグに見舞われる。しかしそもそもが設計思想のないウンコードなので、改修も容易ではなく、納期に追われて初回リリースの時点でツギハギだらけのクソ団子を作り捨てるのだ。
各ドキュメントを作ろうとすることで、考慮・設計すべきことに目が向いて、仕様がまとまるのだ。
- 機能一覧を作った ← 業務が整理され、そのシステムが扱う範囲、正常系と想定される異常系が明らかになった
- 画面一覧を作った ← 命名、URI パス、機能一覧とのトレーサビリティが可視化された
- 画面遷移図を作った ← 機能に対し状態管理とユーザビリティを考慮した
- API 一覧を作った ← 外部公開されるインターフェースの定義ができ、データベースとのトレーサビリティが可視化される
もっと細かい個別の設計書も当然書かれるとして、一覧とフロー図を起こすだけでも全然違うことはお察しいただけるだろう。
一番最初に考えた構成が微妙だったとしても、どうしてそう考えたかという理由からドキュメントに残っているので (設計理由を書き残せよ?)、経緯や理由を追跡できるから、リファクタしようと思った時も修正対象が割り出しやすい。
こうしたドキュメントがちゃんと書けていれば、ディレクトリ構成、命名、責任範囲、型定義が明らかなのですぐに実装に取りかかれるし、どこからがスタブ・ドライバとして仮実装すれば良いかも分かる。ユニットテストも書きやすいし、盛り込むべきテストバリエーションも分かりやすい。何をどう作るか、実装工程で頭を使う割合を減らせて、かつ、ある種思考停止で作ったとしても、すぐに劣ったモノになるワケではないことは分かると思う。
いきなりコードを書き始めようとして、こういう設計まで考慮したコードを書ける人は、自分の観測範囲には居ない。こういう能書きを語ってる俺自身でも、設計書をまともに起こさずに実装することになった機能は、リリース後1年以内に状況が変わって、機能改修の際にまるっと捨てられたり、作り直されることが大半だった。俺がココに書いてることの意味が分からない程度の連中は、リリースまでこぎつけられないことだろう。
実装の段階においても、パフォーマンスを考慮した実装とか、設計のレイヤーでは表現しきれていない問題の解決は当然必要だが、設計思想がないまま作ってる連中はそうした考慮にプラスして、「さっき書いたコードとの辻褄を合わせようとする作業」が発生する。事前に設計するよりも、どうしても考慮している時間軸や領域が狭いので、書き進めているうちに「やっぱりこうしないと」とか「このパターンの考慮が漏れていた」とかいう話が出てきて、書いたコードへのつじつま合わせが必要になる。もしくは、矛盾したり抜け漏れが発生したりしていることにすら気付かず、バグまみれのコードをそのまま出してきて出来たつもりになってるかだ。
ドキュメンテーション下手クソな連中が言い訳に使う「アジャイル」だけど、そういうお前らが好きな「アジャイルソフトウェア開発宣言」には次のように書かれている。
包括的なドキュメントよりも動くソフトウェアを
左記のことがらに価値があることを認めながらも、私たちは右記のことがらにより価値をおく
日本語も英語も出来ないのにエンジニア職を名乗ろうとしてる素人ども、もう一度よーく読もうな。書く力もなければ読む力もないとか致命的だからな。
「『包括的な』ドキュメント」に「価値があることを認める」が、「どちらかというと動くソフトウェアの方を重視し」「『包括的な』ドキュメントはそこまでは重視しない」としか言っていないんだ。
包括的な、つまり「ぜーーんぶをドキュメントに書き起こすこと」すらも、「価値がある」と言ってるんだ。ニホンノエスイーが作るような行レベルの詳細設計書ですらも、「価値がない、作るべきではない」とは言及していない。ドキュメンテーションには、ソフトウェア開発をするにあたって価値があるんだ。
にわかアジャイル脳の連中も「ドキュメントを軽視してるワケじゃないけど、カッチリしたドキュメントを書く時間よりも、コード書いた方が良いよね」なんて言葉では言ってるけど、やってることは「私には言語能力がないので、できるだけ文章を書きたくないです」って言ってるのと同じだ。でも、アジャイル宣言ですら「ドキュメントに価値がある」と言ってるんだ。
もっというと、「よりよい開発方法を見つけだそうとして」こういう手法を考案した人達の名前もよく見てみような。JUnit を作ったケント・ベック、「依存性の注入」という言葉を編み出したマーティン・ファウラー、「SOLID 原則」という言葉をまとめたロバート・C・マーチンなど、錚々たるメンツである。プログラミングの世界を変えてきたプロ中のプロ達が、自分達の開発方法をより良いモノにするために考え抜いた手法が「アジャイル」なのであって、専門の大学を出てもいない素人が上っ面だけすくっても真似できるワケがないんだわ。
彼らは本を執筆したりもしている。言語化・ドキュメンテーションそのものは日常で既に当たり前にやっていることなのだ。ジョエル・スポルスキの「やさしい要求仕様」のサンプルを見ても分かるとおり、簡素なシステムを例にしたとしても、真っ当なエンジニアはあれぐらいの文量で詳細な設計を文書化するモノなのだ。それが前提にあって、「客への納品のために、さらに詳細すぎるドキュメントを書く」とか、「詳細設計書の承認がされないと実装工程に入れない」とか、そういう旧来のやり方だとかったるいし、そういうことよりも別のことをやった方がより品質が上げられるよね、っていう話でアジャイル宣言に至ったものだろう。
あなたの周りの要領の良い人と、要領の悪い人を見比べてみよう。
要領の良い人は、
すでにある物を自分の所へもってきて
ちょっとぐらいルールから外れても
適当に書き換えてまるで自分が作ったかのように成果を出す。要領の悪い人は、
ゼロから自分で考えて、教科書やルールからは絶対はみ出さず
努力に努力を重ねて、結果、
すでに誰かが作ったような物の劣化版を持ってくる。
どうしても自分の考えにこだわり。
基本的にパクリは悪だと思ってる。
設計は実装する前にやる必要がある。設計するためにはドキュメンテーションが必須。コレって要領の良い悪いじゃなくて、考える順番がそもそもおかしいよっていうだけなんだよな。
基礎もできない奴が発展系なんかできるワケない。身の程を知れ。設計は大事なことなんだから面倒臭がって逃げようとすんな。大事なことは大抵面倒臭いんだ。面倒なことだから仕事になってんだよ。逃げられんぞ。設計・文書化ができないならエンジニア辞めろよ邪魔なんだわ。
お前はただでさえバカなんだから、自分の考えにこだわるんじゃなくて、言われたこと・やるべきことをちゃんとやれ。勉強をしろ。