大人数でのプログラム開発に慣れてない人がやりがちなこと
ついこないだまで幼稚園児だったはずの息子が、気がつけばオープンソースプロジェクトの開発に携わる年齢になっていました。
最近は WSL 上の Node.js コードを、GitHub で管理する方法を学んでいるようです。
私がそういった部分の専門家なこともあり、当然ながらつまづいたところで質問を受けたりするんですが、、、
ちょっと気になったのは、その際にちらっと見たコードが、無駄に複雑だったこと。
息子が作った部分じゃなくて、元からの部分。
「あ、この人、集団行動が苦手だな?」というか、なんかそういう印象を受けたんです。
今回のコードがそうだったのはたまたまですが、個人作成のコードはしばしそうなってしまいがちなのも確か。
なぜ、自然に書いたコードが自然にそういう印象になるのか。
今回はそれについて考えてみたいと思います――。
いつもお読みいただきありがとうございます。
または初めての方も、この記事を見つけてくださって嬉しいです。
私はエンジニアの中島と申します。
AIにプログラミングさせることが主流になっても通用するエンジニアの育成について、日々悩んでおります今日この頃です。
この 絶対バグらないシステム作ろうぜの会 では、バグの出ないシステム・問題を起こさないチーム運営・AIには作れない設計論などのコラムを、なるだけ面白く・分かりやすくお伝えする主旨で記事を配信しております。
1. 大人数での開発に慣れてない人がやりがちなこと
CASE1. 思いついたことを、思いついた順に縦にメソッド化してしまう
人間がアイデアを思いつく順番というのは、あらかじめコントロールすることはできません。
たとえば社会の大局を見てシステムの大雑把な方向性について思案しているときに、突然「この画面のこの部分に、こんなワンポイント画像を入れたら面白そう」といった、あきらかにどうでもいいことが思い浮かんでしまうこともあります。
もしくは中には、全然関係ないことをしているときにこそ、よいアイデアが浮かぶケースだってあるでしょう。
個人開発が好きな人ほど、そうしたアイデアを、思いついた先から縦に順番に実装してしまいがちです。
当然ながらそのような実装の仕方は、仕事として行うときだけでなく、個人開発としてもNGです。
なぜならアプリというのは、対企業・対個人を問わず “誰かの要望を叶える” ためのものだからです。
お客さんの要望を叶えるからには、必然的にそれに適した手順というものがあるはず。
プログラムはその手順を追うように作っておかないと、作った直後はよくても、あとから見たときワケが分からなくなります。
このとき、この理論上の理想手順のことを、《サービスフロー》とか、または《ビジネスフロー》などと呼んだりします。
そしてそのフローを紙に書きおこしたものが概要設計です。
無論、プログラムというのは思いついたものを思いついた順に実装しても、特に問題なく動いてはしまいます。
それゆえ大人数での開発に慣れていない人は、次の人に分かりやすいコードを書く有用性がなかなか理解できません。
コードの難解さに次の人が困っているところを見たことがないために、自分が次の人を困らせていることを自覚できないのです。
そのような人が書いたコードは、次の人からすると『少しの努力で分かりやすくなるものを、わざわざ難解に書いている』ように見えてしまいます。
そしてそれが次の人には “意図的な意地悪” に見えるでしょう。
ですが実際には、何の気なしに書いたコードが意地悪だと思われるのは、最初に書いた側としても心外なはずです。
ですから処理を記述する順序は、たとえ実行するのに支障はなくても、人間に分かりやすい順でないといけないのです。
CASE2. コードを他者が読むことに対する考慮がない
他者が読んで分かりづらくなるコーディングの例としては、処理順序がバラバラなケース以外にもたくさんあります。
変数・関数の命名が杜撰すぎる
改行の位置に無頓着
コメントが少なすぎる
不要なコメントがやたら多い
省略記法の過度な多用
などなどなど、挙げていったらキリがありません。
大人数での開発を経験していない人は、サービスフロー・ビジネスフローを理解しながらプログラミングする必要がないだけでなく、『同じコードを長期間使い続ける』必要もありません。
一般的な個人開発の場合、コードの内容を正確に把握しておかなければいけない期間は “いいとこ半年” で、それ以上の期間に渡って特定のコードが常に修正され続けるケースはそうそうあるものではありません。
たとえそのアプリ自体が、長期に渡って使われ続けるものであったとしても、です。
ですからそのようなアプリしか作ったことがない人は、分かりやすい状態を長期に渡って維持し続ける必要性を感じることもないのです。
CASE3. 普通のエンジニアが知らないようなレアなテクニックを多用する
上述の通り、私の息子は現在オープンソースソフトウェアの開発に携わっているわけですが、彼が引っかかって解決に一晩かかった記述がこれです。
// @ts-ignore 古い項目名をここで置き換え
すでにご存知の方なら「あるある」ってなるでしょうが、私は知りませんでした。
この "@ts-ignore" って部分。
TypeScript で、“直後の行の型判定を無視せよ” という指示をトランスパイラーに対して出すコマンドです。
というより、TypeScript にそんなコマンドが存在すること自体を知りませんでした。
このような “コメント内にコマンドを書く” という手法そのものはいくつかの言語でしばし見かけるものの、さりとてそう滅多に使われるものではなく、知らない人が見る可能性がある場所にこんなにもさりげなく使っていいわけがありません。
しかも続くコメントにも注意とか何もないし。。。
特に TypeScript の場合、このような『トランスパイラーに対して指示を出すためのコマンド』それ自体を指す名称が特にないらしく、初心者が引っかかった際の検索は困難を極めます。
(私はオラクルに倣ってヒント文と呼んでいますが、知らない人からすれば純粋に謎の記述としか言いようがありません)
無論、プログラミングってものは、ときとしてやむをえず変な書き方をせざるをえない場所というのは現れるものです。
それはしょうがないんですけど、ただそれが本当にやむをえないことならなおのこと、“やむをえない書き方をした理由” をコメントに残すべきじゃないかなと思いました。
苦労したポイントでも、個人の感想でも何でもいいんだから。
とりわけ “自分に指示を出した上司への愚痴” なんかは、ビジネスアプリに残すべきじゃないとかそんなカタいこと言わず残しておくと、あとあとになって分かりやすさが激増するケースも多いです。
CASE4. 「プログラマーたるもの、自分と同じくらいのレベルに達しているべきである」という考え方を持っている
これは今回息子が携わったアプリではなく全然別の機会に触れたものですが、ドキュメントに物凄く恐ろしいことが書いてあったことがあります。
『プログラマーならこれくらい理解できて当然なので、細かい仕様はあえて残さない』とハッキリ書かれていたんです。
そのアプリが、古いCPUのニーモニックをシミュレーションするもの(いわゆるエミュレーター)で、それなりにレベルの高いものだったにも関わらず、です。
当然ながらそのアプリは、最初の開発者が手を引いたときに引き継ぐ人も現れず、最終版が出て割とすぐ、他の類似アプリに市場を食われてしまいました。
当たり前ですがプログラマーのレベルは千差万別なのであって、『これくらいのレベルには達してるべき』という社会通念は存在しません。
特定企業に『自分達のプロジェクトに参加できうるレベル』はあっても、それはあくまで特定のプロジェクトの話であって、決して社会全体のことではないのです。
技術継承が社会問題化している業界ならなおのこと、初心者の存在が許されないなんてことがあってはいけません。
CASE5. ネスト構造を必要以上に複雑にしたがる
ロジックやデータなどに親子関係があり、かつ親と子が同質の性質を持つことを “ネストしている” などと呼びます。
ループの中にループがある
親処理から呼ばれた子処理が、同じような子呼び出しを行っている
関数が自分自身をコールする(いわゆる再帰)
設定ファイル等が親ファイルを上書きする形で成りたっている(いわゆるオーバーライド)
たとえばこういうのがネストです。
で、私自身も過去に何度かお目にかかったことがあるんですが、このネスト構造というのをやたら複雑にしたがる人がいるようです。
息子が携わった今回のプロジェクトでも、そのような人が設定ファイルを作っているようでした。
設定を司るクラスが三重四重に extends されていて、かつ、すでに削除された過去の設定にアクセスするケースがある、なんて摩訶不思議な仕様もあるようでした。
通常多くの人には言うまでもないことですが、ネスト構造というのはそもそも複雑になってはいけないものです。
なぜなら 《ネスト》 という言葉自体が、『理想的には重なっていてほしくないものが重なっている』というニュアンスを含むものだからです。
一般的なビジネスアプリの場合は、そのような複雑なネスト構造が生まれてしまう理由は、作る前に情報を整理してないからです。
仕事である以上、それ以外の理由でネスト構造が複雑になることはほとんどありません。
が、個人開発の場合は、シンプルにもできると分かっていて、あえて複雑にすることもあるようです。
まぁ、プログラミングの初心者からしたら、複雑なものが作れるって純粋にカッコいいですからね。。。
でもその考えは初心者特有のものなので、あらためた方がいいのかなと思います。
CASE6. 同じコードの連続を共通メソッド化しない
新人君とかの面倒を見ないといけなくなったときなんかに、個人的に非常によく見かけるのがこちら。
似た処理が連続するようなロジックを書く際に、『同じ処理はメソッド化する』という感覚自体がないケースです。
だって、そんなことしなくたってプログラムは動きますからね。
それとこれはおそらく特殊事例だとは思いますが、私は若い頃、『処理を切り出してメソッド化する』こと自体に恐怖心を持っていました。
そんなことするくらいならスパゲッティソースのままの方が絶対にいい、と考えていたんです。
なぜなら、私が初心者だった頃は "Z80" というCPUが現役だったから。
バス幅は 8bit しかなく、動作周波数も 5MHz とかそんなもんで、だから関数呼び出しの階層が深いことがそれなりにボトルネックになったのです。
実をいうと、関数の呼び出しの階層は深くても処理速度に思ったほどの影響がないことに気づいたのは、プロのエンジニアになって十年以上たってからでした。
ミッションクリティカルなシステム向けと謳われていて高速と評判のフレームワークが、その内部で実は数十階層を超える物凄い深い階層の関数呼び出しを行っていることに気づいたときです。
当時は「そんなに呼び出しを重ねたら、重くてまともに動くはずがない」と思い込んでいたので、それなりに衝撃的だったことを覚えています。
エンジニアは、個人開発の経歴が長いほどそういう変な思い込みがある確率も必然的に高くなります。(あくまで確率論的には)
それゆえに入社前に全て学んでから社会に出ることは不可能だし、入社後の教育も大事なんだなと改めて思います。
2. 大人数で開発する際の最優先事項は“コスト”
システム開発を1人でやるときと大人数で、気をつけるポイントがなぜこんなに大きく違うのか。
それはかけられるコストが違うからです。
企業というのは従業員にお金を払うことで運営されていますので、個人開発とは違って本質的に莫大なお金がかかります。
従業員を雇うコスト
システム・インフラを維持するコスト
従業員の心身・健康を守るコスト
人間関係トラブルを防ぐコスト
余計な不具合修正を最小限にするコスト
企業自身が犯罪加害者になったときのダメージを最小にするコスト
今後のコストを最小にするためのコスト
コスト、コスト、コスト!!
このコストは必ずしもお金だけではありませんが、でも世の中なんでも何かするとなればコストはかかるのです。
そして企業とは、本質的に “コストが最小になるように” 活動しなければいけない宿命を負っています。
またここでいう “コスト最小” とは、第一線の現場で働く一般社員にとっては “苦労の総量を最小にする” と言ってるのと全く同じことです。
だから企業での開発は、個人開発と比べて圧倒的に面倒くさいんだと、私なんかは思うわけです。
さて、
てなわけで次回は、その “コスト” なるものを最小にするための――つまりあなたが周囲から「こやつ、デキる、、、!」と思われるために大事なことなどお話できたらと思います。
ではまた。