Murga

個人的に言いたいコト・主張・気持ち。

カーゴ・カルト・プログラミング

システム設計の謎を解く 強いSEになるための機能設計と入出力設計の極意

システム設計の謎を解く 強いSEになるための機能設計と入出力設計の極意

カーゴ・カルト・プログラミング。

実際の目的には役に立たないコードやプログラム構造を儀式的に含めておくプログラミングのスタイル

 

カーゴ・カルトという語句は、元々は第二次世界大戦後の南太平洋で見られた先住民の宗教に由来している。これらの人々は、戦時中素晴らしい積荷をもたらしてくれた神のような飛行機を呼び出そうと、一心不乱に精巧な飛行機の模型や滑走路を作り上げた

 

転じて、背景にある仕組みを理解せずに行動を過剰に(そしてその多くは無意味に)繰り返し実施すること

もっと平たくいうと、「○○するためのおまじない」などといって無秩序に放り込まれる (実際は無意味な) コピペコードだったり、誰かの勘違いから広まった不必要な処理を真似して増殖していったり。

/**
 * 引数の HogeDto の更新日時を書き換えて UPDATE 処理を行う
 * 
 * @param hogeDto 更新対象の DTO
 */
private updateHoge(HogeDto hogeDto) {
  // HogeDto の初期化
  HogeDto newHogeDto = new HogeDto();
  // 引数を取り込む
  newHogeDto = hogeDto;
  
  // 更新日時の変更
  newHogeDto.updated_at = new Date();
  // UPDATE する
  boolean updateResult = HogeDao.update(newHogeDto);
  
  return updateResult;
}

前職で見ていた泣きたくなるコードを真似してみた。こんな書き方をするなら、「HogeDto の初期化」で生成された new HogeDto() には何の意味があるのだ?


カーゴ・カルトなコードが生まれる現場では、カーゴ・カルトな設計書も生まれがちだ。大抵はプロジェクト全体に「無意味な慣習を守らせる宗教」が蔓延していることが、カーゴ・カルトの原因だからだ。

例えば、本当はある一部の条件下で、前後の文脈が揃って初めて必要になった対処法を、「全ての場面でやらないといけない初期処理」とみなして取り入れてしまったり。前述の new HogeDto() は、自分が実際にコードレビューで「初期化を必ずしてください」とツッコまれたものなのだが、恐らくレビュアーの知見の中では、それが必ず必要なものになってしまっているようである。以前に何か代入忘れなどでぬるぽでも引き起こしたのだろう。

しかしこうやって無意味な慣習が蔓延していると、「いやいや、次の行で同じ変数に代入してるし、最初の初期化いらんでしょ」という反論もする気がなくなる。もしくはその反論が理解されず「とにかくそうしろ!結果は正しく動いてるんだから!」と怒られたりする。こうなると「もうこの現場は適当でいいや」とモチベーションが下がる。

もしくは知識が少ないメンバであれば、その指摘をよく考えもせずただただ鵜呑みにして「どうやらそうするのが良いらしい、自分も守ろう、皆も守ろうね」と、新たな布教者になってしまうのだ。

厄介ごとを避けようという方針は困ったものだ。なぜなら、言語をマスターするにはその言語のすべてを知らなければならないし、恐れたり逃げたりすることは知識を得ることの妨げになるのだから

「知らないままでいいや」と思うことが間違いなのだ。どれだけ面倒でも情報量が多くても、全てを知らなくてはならないのだ。大体それで金もらってんだろ適当な働き方すんなボケ。


以下の記事では、「OJT とカーゴ・カルト」という視点でも検証している。

つまり、OJT により引き継がれる「現場の知識」とやらは、無意味なノウハウや間違ったやり方が多分に含まれているよ、ということ。教える側が既に体系的に学習した内容ではなく、現場の場当たり的な「実践知識」をベースにして話をするので、皆のインプットがそれだけだと、代を引き継ぐうちに知識はどんどん劣化していく。

この劣化のスピードは、少なくとも現場にいる当人にはなかなか認識しづらいゆっくりとしたもので、本人が常に危機意識を持っていないとなかなか変えることはできない。なにせ「その場では何とかなっている」からだ。そうこうしているうちに、効果のない呪文にまみれたコードの中で、さらに効果のない呪文を唱え続ける事態に遭う。

だからこそ、世界のオープンソースを見たりして勉強し続ける必要がある。

小さなステージで満足しないで、広い世界に目を向けていけ。

参考

大きな泥だんご

マイクロサービスアーキテクチャ

マイクロサービスアーキテクチャ

大きな泥だんご。英語で Big ball of mud。

理解可能なアーキテクチャが欠けている ソフトウェアシステム のこと

 

でたらめに構築され、乱雑で無秩序、ダクトテープで繋ぎ合わされたようなコードのジャングルのこと

 

無秩序な状態のことです。結局きちんと設計されたソフトウェアなんてほとんどない、特にそれは最近の Agile の流行の中で助長されているのでは?

なんでこんな作りになっちゃってんのか分かんないけど、表面上はとりあえず動いている、という、世のシステムの9割がたが該当する状態のこと

なんで泥だんごになるの

理解可能なアーキテクチャが欠けている、ということは、理解可能な構造にまで落とし込まずに実装した、ということ。

乱雑で無秩序ということは、既存の構造と乖離した構造を無理やり繋ぎ合わせた、ということ。

極端な話、既存コードの設計が多少イケてなくても、追加コードは既存の設計を真似て作って馴染ませてやった方が、全体としては読解しやすくなる。イケていなくても、それはそれで「規則性」ができるからだ。

無秩序というのはそのとおり「秩序がない」わけで、「こっちのコードはこうなっている」「でもあっちのコードはああなっている」というシステムは読みづらい。イケていない設計でも「全体としてこういうダサい設計をしてるんだな」という秩序が読み取れるほうがマシなのだ。

さて、なぜこんな実装になるのか。人間誰しも、自分が手掛けて作るなら、最初から汚くて最低なものを作ってやろう、とは思わないはず。最初はきっと志高く、ああしようこうしようときっと考えているはずなのだ。それなのになぜ泥だんごが出来上がるのか。このあたりが理由ではないだろうか。

  • プロジェクトマネジメントがなされておらず、プログラマに対する圧力のみで開発されているとき
    • 恐怖駆動開発。十分な設計、検討がなされないまま、「とにかく動くものを出せ」と要求されているとき。
    • メンバ間の情報連携が希薄で、似たような実装、もしくはコピペコードが乱立する。共通化がなされない。
  • 予め設計しない
    • 恐怖駆動開発の他、アジャイルを誤解している場合とか。
    • 特に「共通設計」、たとえば命名規則や URL 設計、DB 設計が欠けていると、「その画面を担当した人が自分ルールの命名規則で URL 設計を行う」といったシーンがあちこちで生まれ、統一感のないシステむができあがる。
    • マネージャが先々までのガントチャートを引かない・引けない現場では大抵こうなる。
  • コードに対する意識が低い
    • 「設計書には手続きが型で書いているのだから、実装で勝手にメソッド化なんてされたら困るよ」という理由で、文字列連結のメソッドをコピペして各所に埋め込み直したことがある。汚いコードが推奨されていたのだ。
    • もしくは実装者の手抜きで、どこかから拾ってきたコードを考えなしに使う「コピペ駆動開発」が横行していて、それを監視する人がいない。
    • こうした理由でコードに対する意識が低いと、「設計書だけはリッチかもしれないが、改修不可能」なシステムが出来上がる。実際に動いているのは設計書ではなくコードなのに、システム屋がコードを見ていないからだ。
  • 予めツギハギによる成長を企んでいるシステム
    • 「フェーズ1」「フェーズ2」といった単語が出てくるシステムはそういうこと。当初から増改築を繰り返そうといるのだが、大抵は増改築に対応したアーキテクチャ設計なんてできていない。
    • 大抵は、コードを見られないシステム屋が設計しているせいで、コード・フレンドリーな設計になっておらず、強引な結合やコピペ駆動開発をせざるを得ない状況に追い込まれる。
    • そもそもフェーズ分けをする時点で各フェーズに十分な検討・設計期間なんて設けられていない。そもそもの要求とスケジュールに無理があるから、契約以前のコンペの時点で、崖に向かって歩いているのだ。
  • 顧客要求がすぐ変わる
    • 実装中にその機能の削除が決定するなど。
    • 法令的にその対応が必須な場合もあるが、いずれにせよ、そのような要求をコロコロと取り入れる環境は、間違いなくまともなシステムなんて生まれない。「仕方ないけど上手くやれ」はできないのだ。「仕方ないのであれば、仕方ないシステムにしかならない」のだ。

どうしたら泥だんごにならないの

  • コード・ファーストな設計
    • 使用する言語・フレームワークの特性を活かした設計を行う。
    • オブジェクト指向が分からない人間に設計をさせない。
    • 各画面の詳細な仕様書はともかく、共通設計だけは時間をかけよう。
      共通設計 = 共通認識。メンバが何を指針として実装していけばいいかを決めるものだ。
  • 中長期的なスケジュールを立てる
    • 「機能追加が発生するとしたら、いつ頃・どんな内容か → それは現在開発中のシステムにどうドッキングさせたらいいか」といったことを事前に考える。
    • 拡張可能なシステムになるよう最初にアーキテクチャを練る。
    • 大抵は事前にいくら考えても思ったようにはいかないものだけど、何も考えていないよりはマシになる。
  • 負け戦が確定している案件に携わらない
    • プログラマが担当案件を選べたとしたら、「コード・スメル」ならぬ「プロジェクト・スメル」を感じ取った時点で、関わらない方がいい。
    • そんなことは到底できないだろうけど。

参考