恐らくこれは開発現場であるあるな出来事だと思います。
今回のお題は「既存コードを参考にして新規コード書く」事についてです。
あなたが部下または外部の方に作業依頼をする時、どんな風にコードを書いたらいいかを事前に説明しますね。その時「この辺に書いてある既存のコードを参考に書いて下さい」と言ったりしますよね。
もしくは特に書き方の方針を伝えない場合は、依頼された側が「既存のこの辺のコードを参考に書きます、書きました」と言ったりします。
「参考」とは
この言葉のポイントは「参考」という部分にあります。実はこの「参考」には光と闇が潜んでいるのです。
何故ならこの「参考」は、大抵の場合は「ほぼコピペ」を意味しているからです。
☓ 「既存のコードを参考にする」
◯ 「既存のコードをコピペする」
「ほぼ」と言っているのは、数カ所違うだけのコピペの場合が多々あるためです。
ほぼコピペのメリット
- 周りのコードの書き方と足並みを揃える事ができる。
- バグが混入しづらくなる(かもしれない)。
- 新規にコードを書くより速く書ける(かもしれない)。
ほぼコピペのデメリット
- 元にしたコードを書いた人の技量が低い場合、低品質なコードが量産される事になる。
- 元にしたコードが書かれた時とほぼ同じコードが書かれてしまい、全く進歩が無く、効率化が測れない。
- 似たようなコードが量産されてしまい、後々メンテがしづらくなり、融通が全く効かないコードが出来上がる。
- 元にしたコードの問題(バグ・低性能・低効率)をそのまま別箇所に移植・量産される。
- 元にしたコードが何をしている・したいのか、を考えなくなる。ただコピペして動けばいいと考えるようになる。
DRY原則を簡単に破壊してしまうコピペは凶悪な存在です。破壊している事にすら気づいていない場合もあるので怖いです。
コピペの光と闇
コピペの光の部分
例えばメリットの「バグが混入しづらくなる」点は、開発のリーダー的存在が不在になってしまい、派遣社員一人で開発をしている場合はよく既存コードを参考に書かれます。下手に新規コードを書いてリスク(自分の知らない裏仕様等)を負うよりも、既存の仕組みを使ってリスクを回避する、という意味では正しいと思います。
また、納期が厳しい時に既存コードをほぼコピペして納期に間に合う確率を少しでも高めるという方法は間違っていないと思います。実際よくあるのではないでしょうか。
しかしこの光の部分を見ていられる期間は非常に短く、あっという間に手に負えない程の巨大な闇へと変貌してしまいます。
コピペの闇の部分
コピペは究極の思考停止であると思います。
コピペ元のコードが何をしているのか、何がしたくてそう書いたのか、を考えなくなります。最初は考えていても、コピペの数が増えるにつき思考は停止し、量産する事しか頭に無くなります。
そして量産の代償として、メンテ難易度の上昇、バグの誘発、メンテコストの上昇を招きます。その結果、チームの負の感情が渦巻くようになり、逃亡・放棄という最悪の結果を招きます。
自分で何かを生み出す機会も生まれない(機会があってもコピペ元を探そうとして実際コピペしてしまう)ため、自分で何かを生み出すスキルが身につきません。元になる何かが無いと何もできない人間ができあがります。
目先のリスクを回避するために、後々に大きな代償を払う事になるのがコピペです。
「代償を払う?その現場から逃げればいいじゃんwww」と考えても無駄です。勿論その現場に負の遺産を残して迷惑をかけますが、コピペ当事者にもしっかり負の遺産ができあがっています。前述した「思考停止」「元になるものが無いと何もできない」という遺産が積み上がり、以下のような負のループに嵌って抜け出せなくなってしまうでしょう。
- コピペする。
- メンテコストが増大する。
- メンテに時間がかかって残業が増える。
- 精神的に参ってくる。
- 給料・単価が挙がらない。
- 給料挙がらないし残業ばっかで辛い。もう限界。
- 現場から逃亡する。
- 新しい現場にjoinする。
- 振り出しに戻る。
私が負わされた闇
私はこの「ほぼコピペ」というものに大変な闇を負わされてしまいました。
簡単に経緯を書くと以下のような感じです。
- そこそこの規模のプロジェクトがあった。
- そのプロジェクトはメンバーに恵まれず、中核を担うプロパー社員は数人だった。私の担当はデータベースと全文検索エンジンの設計・実装のみだった。
- 当然納期も厳しかった。
- あるチームで品質の悪いコードが書かれていた。(レビューする時間も無く・・・)
- あちこちで悪いコードがあったが、ギリギリ納期に間に合い、利益も結構出た。
- 保守フェーズになった。
- 暫くは「ほぼコピペ」がまかり通っても問題にならなかった。
- 私は他のプロジェクトに移動し、暫くそのプロジェクトから離れた。そのプロジェクトは派遣社員が2〜3名で回すようになった。
- その体制が始まってから2年程度経過し、派遣社員のメンバーが入れ替わったりして人員不足により私が戻ってきた。
- そのサイトで中核となる機能でバグが見つかった。
- その中核機能は低スキルな人によって低品質なコードが書かれ、保守フェーズの「ほぼコピペ戦隊」の人たちによってその低品質コードが大量量産された事が判明した。
- バグをいざ直そうと思ったら修正箇所が40ケースくらいあった。今更リファクタするのはもう手遅れなレベルの作り込みで、無理ゲーだった。
- リファクタを諦め、全40ケース全てのユニットテストを他プロジェクトを沢山掛け持ちしながら1ヶ月かけてバリっと書き、「結果だけ」問題ない状態を作り上げた。
という感じです。ク◯みたいなコードが目茶苦茶な量を量産されていて、泡吹いて倒れるかと思いました。コピペ戦隊恐るべし・・・
どの辺が問題だったか
- 保守フェーズで派遣社員のみでチームが構成されていて、プロパーに開発に関する相談をするきっかけが無かったかもしれない。
- リスクを負いたくない・負えない(派遣社員だから?)という気持ちが強すぎるあまり、リファクタを極度に恐れ、ほぼコピペしかできない状況だった。
- リファクタするスキルが無かった。要件を満たす実装をする事だけで手一杯だった。(当然低品質)
保守フェーズの当事者達は「このコードは問題がある」事に気づいていた人もいました。(問題に気づかないレベルの人もいます)
しかし彼らは現状を改善する事はできませんでした。何故リファクタしなかったのかの理由を聞くと、以下の答えが返ってきました。
「時間が無かったので既存のコードを参考にコピペするしかありませんでした。」
「どうリファクタしていいか解りませんでした。」
「相談する相手もいなかったししょうがなかった。」
「他プロジェクトとの掛け持ちでリファクタどころではなかったです。」
要は「時間とスキルが足りないからコピペするしか無かった」という事です。しかしそれだけでは無いようでした。何故なら保守フェーズに入社2〜3年目のプロパー社員が実はいたからです。
彼もまた「既存のコードを参考に〜」と語っていたので、結局はリファクタをするきっかけを作り出す事はできませんでした。プロパー社員であるにも関わらず、です。
何故時間が足りなくなるのか
これは私の周辺の場合のケースですが、他プロジェクトの掛け持ちが多いためです。プロパー社員なら2〜5件、派遣社員なら1〜3件くらいが平均です。私が最も酷い時は7プロジェクトのかけもちで、1日1時間毎に違うプロジェクトをやっていた時期もありました。
掛け持ち以外では、単にスキルの問題で、速く上手く設計・実装できないから時間が足りないケースもありました。
何故スキルが足りなくなるのか
スキル不足系の人を私が見てきた感じでは、「コピペばかりで自分発の新規コードが極端に少ない」場合がほとんどであるように見えました。
そこでそういう傾向が見られる人に何故新規コード書かないの?と聞いてみたところ、以下の答えが返ってきました。
「既存コードを参考にした方が確実だから。」
「他プロジェクトとの掛け持ちで時間が無いから。」
「自分にはそういう権限は無い。」
「権限が無い」と言った方に「では権限を与えます。責任は私が持ちます。」と言ったところ、「実は◯◯の経験が無いので、新規のコードが書けません・・・」と返ってきました。「権限が無い」というのは実は自分を守るための口実だったようでした。
「既存コード参考にした方が確実」と言った方に、「では既存コードを無視していいので、あなたが考える最高のコードを書いて下さい。」と言ったところ、彼は黙りこんでしまいました。色々聞いてみると、実は「新規にコードを書く自信・スキルが無い」事が解りました。結局これも自分を守るための口実でした。
皆リスクを恐れるあまり、守りに入ってしまっていたわけです。フルアーマーでシールドガチガチの防御態勢になって硬直していたのですね。
防御態勢で硬直する事の代償
例えば元にしたコードが5年前に書かれたコードの場合、5年前の技術水準と全く同じレベルのコードを書いている事になりますね。IT業界で5年といったら相当な技術革新が進みます。様々なナレッジも蓄積され、効率化が進む筈です。しかし昔の水準のコードを量産します。
するとどうでしょう?あなたは現代の人なのに、数年前の古い水準のままのコードを書き、効率化も図っていません。1年経っても2年経っても変わりません。普通に考えると、「年齢を重ねた分だけ何かが向上していないといけない」ので、何も進歩が無い状態では評価が下がる、ボーナスが減る、給料が下がる、単価が下がるという結果が待っていると思われます。
それだけではなく、新規に何かを生み出すきっかけも極端に少なくなるので、「参考にするものが無いと何も始められなくなる」状態になってしまい、永遠にサポート約に甘んじるしかなくなってしまいます。その状態が許される期間はそう長くないので、それなりの年齢になった時、相応の結果がふりかかるでしょう。
「ほぼコピペ」は最初は数が少ないので問題が表面化し難いのですが、コピペ数が増えるにつれ、「あ、これメンテ無理だわ」「誰だよこのク◯コード書いたクズ野郎は!」という負の感情が大きくなります。
徐々に文句が増えていく現場
コピペ戦隊によって荒らし尽くされた現場に、新規参入した人は皆口をそろえて不満を吐き出します。
しかしその人達も「ほぼコピペ」を右に倣え?で始めてしまいます。不満を言っておきながら、自分でその不満なコードを量産するというダブルスタンダードを始めてしまう、おかしな現場が出来上がってしまいました。
CIすら導入せず、ひたすらお手製シェルスクリプトを手動実行してデプロイする毎日。それについて不満たらたらなのに一向に自動化しようともしない。ソースコードの問題の改善もしようともせず、ひたすら防御に徹して自分を守り、最後は壊れるという悪循環に陥っていました。
最後に待つものは
そして最後に待つものは、静かな終わり、です。
ほぼコピペによって量産されたコードのメンテが限界に達し、メンテコストの採算が合わなくなり、状況を改善しようという者も現れにくくなり、硬直せざるを得ない状況が続き、顧客から見放され、そのプロジェクトはゆっくりと終わりを迎えます。
但し無茶なりファクタは避ける
リファクタするにせよ、テストが必要になりますね。もしリファクタすると影響範囲が広いなら、テストが可能か、ユニットテストが存在しているか、等を考慮する必要があります。現実的にそれらが難しいなら、根本的なリファクタは諦め、ユニットテストを書くだけでも改善に繋がります。
どの程度の粒度でユニットテストを書くかは宗教論争になりそうなので明言しませんが、私は少なくとも中核機能は守られ、ユーティリティー系は引数の網羅パターンは欲しいなと思っています。
雑感
ちなみにこのお話は、「たった1行のコードの変更をするのに申請用紙に沢山のハンコが必要」だとか「sshも許されない現場」みたいな、極限の環境の話ではありません。全く逆で、「現状が改善するならバシバシやっていこう」「リーダーがOKといえば大抵の事は許される」「むしろもっと改善しろ」という、非常に柔軟で緩い環境で起きた話です。
色々な闇を見てきた私ですが、硬直したメンバーを導いていける人が一人でもいる場合、解決に向かっていく場合が多いように思えました。
率先してリファクタの設計・実装していくリーダーがいるのが最善ですが、設計・実装はしないけど「メンテコストが上がらないように極力重複コードを書かないようにしましょう」と指示するだけでも、状況がよくなっていきます。結局のところ、膠着したメンバーも何かきっかけがあれば変わる事ができるので、きっかけを与える事が重要だと思います。少なくとも私の周辺環境では、「悪くない環境であるにも関わらず、自発的に膠着しようとする」現象が起きているので、きっかけ作りは重要だと思います。
最悪なのは「導く人が一人もいない」状況です。要件と納期だけが延々と降ってくるだけで、リファクタの指針を決める人・実行に移す人がおらず、コピペ戦隊を生み出してしまうのは絶対に避けたいですね。状況を改善するきっかけが生み出せない、生み出したくても生み出せなくなり、皆諦めムードでお通夜状態になってしまったら、相当危険な状態であると言えるでしょう。
コピペだけでなく、「標準◯◯」とかいう設計というか、手法?も私はあまり好きではありません。最初は「おお!これ見れば大体問題無いな!」と感じるのですが、徐々に誰もその標準が正しいのかを考えなくなります。「標準◯◯」を延々とメンテするようなチームを存続する事も難しいので、コスト的にそれはメンテされない場合が多く、徐々に使い物にならなくなります。すると「標準◯◯」はフォークされ、徐々に各所で別の何かが生まれます。そのフォーク結果はフォーク元にPull Requestされる事もなく、結局は狭い範囲で共有されるだけのものに成り下がる場合がほとんどです。私は残念ながら「標準◯◯」に関するナレッジがPull Requestされてmasterにマージされて改善されていくフローが成功している環境を見た事が無いので、「標準◯◯」は信用していません。ほとんどの場合、単に思考停止に陥っているだけのように思えました。
こうした思考停止に陥る罠は沢山あるので、常に「今やってる事は本当に正しいのか?」を考えなくてはならないといけません。それに加え、納期と相談しつつ、目先のリスク回避のためにコピペを量産して後々の負の遺産に変貌させない努力もすべきだと思っています。