こんにちは、萩田です。
今回はプログラムのメンテナンス性についてのお話をしたいと思います。
弊社ではクラウドサービスを運営するにあたり、メンテナンス性を考慮したコーディングに気を付けています。
具体例を挙げながら、そのエッセンスをお伝えできればと思います。
質問:「味噌汁の作り方教えて?」
回答:「先に湯沸かしといて野菜切って湯湧いたら味噌入れて具入れて沸騰させないであと換気扇を最初に回してあと味噌がないからまず買ってきて」
突然でしたが、「味噌汁の作り方」についての回答はわかりやすいでしょうか?
なかなか理解しにくい回答だったと思います。
しかし、こういったコードを書いてはいませんか?
・・・というのが、本稿の問いかけなのです。
あなたの書いたソースコードは、『プロダクトライフサイクルにあった、メンテナンス性を担保する可読性』を確保できているでしょうか?
まずはじめに以下のコードをご覧ください。
const result = (a => b => c => a > 0 ? (b > 0 ? "A" : "B") : (b > 0 ? "C" : (c > 0 ? "D" : "E")))(a)(b)(c);こちらは意味がわかるでしょうか?
先ほどの味噌汁の例題と同じく、解読するのが大変だと思います。
これをわかりやすく書き換えると、以下のようになります。
function getResult(a, b, c) {
if (a > 0) {
if (b > 0) {
return "A";
} else {
return "B";
}
} else {
if (b > 0) {
return "C";
} else if (c > 0) {
return "D";
} else {
return "E";
}
}
}
const result = getResult(a, b, c);いかがでしょうか?
改善前のコードはワンライナーで無理やり短く書いたのですが、それですとメンテナンス性が落ちてしまいます。
弊社では後者のように、『メンテナンス性の高い、可読性の高い』コードを推奨しています。
あらためて、さきほどのコードの例を手紙に例えると、改善前のコードは、以下のようなやりとりをしているようなものです。
質問:「味噌汁の作り方教えて?」
回答:「先に湯沸かしといて野菜切って湯湧いたら味噌入れて具入れて沸騰させないであと換気扇を最初に回してあと味噌がないからまず買ってきて」
こう言っているのと同じことです!
つまり、プログラムのソースコードというのは、未来の自分に対して、あるいは後から読む人にとっての、手紙のようなものなのです。
前述の例ですとあまりに極端かもしれませんが、日常でも『無理やりワンライナーになっているコード』などを見かける場合があります。
私の見解では、『エンタープライズなシステムでは、簡潔にしすぎず、一定の冗長性=配線の遊びが必要』と考えています。
ここで、『自作フルタワーPCとメーカー製ミニタワーPCの比較』を例にしてみたいと思います。
PCの種類 | 配線 | プログラムの場合 | |
|---|---|---|---|
A | 自作フルタワーPC | 遊びがあり検証しやすい | 調査・デバッグしやすい |
B | メーカー製ミニタワーPC | 最短配線で動かせない | 調査・デバッグしにくい |
Aはフルタワーの筐体を元に自作PCを組んだケースでして、この場合、配線やパーツの隙間があり、問題が起きたときに部分交換や検証がしやすい。(これは例えば、工場の大規模な工業機械みたいなものかもしれません。機械のある歯車が故障した際、あまりにタイトな機構だとそれすら難しいのですが、『余白やメンテナンス性』を確保しておくことで、簡単に部品交換ができるようになります)
けれどBはメーカー製のコンパクトなPCです。配線などがタイトであるため、故障が発生したときに、ケースを開けて検証したり部分交換したりするのは難しい。
このように、ABの選択肢がある場合、SaaS運営においては通常、Aの方が適していると考えています。
例えば一例ですと、以下のようなアロー関数を利用したワンライナーがあったとします。
const calculate = (a, b) => ({ sum: a + b, difference: a - b, product: a * b, quotient: a / b });これはこれでメリットがあると思いますが、エンタープライズの現場では『遊びがなさすぎてイレギュラー時に対処しづらい』ということになります。
例えば、稼働中の本番環境において『変数bの中身がなにかおかしいぞ!』と兆候を掴めたとして、『じゃあbの中身をデバッグするぞ』となった場合、
const result = calculate_function(a, b);
function calculate_function(a, b) {
const result = {
sum: a + b,
difference: a - b,
product: a * b,
quotient: a / b
};
return result;
}こんな感じに書き直さないとロジックを理解できない場合があります。
(緊急時は認知バイアスの影響で、単純な論理的思考が阻害されてしまいます!)
このため、『重要な責任を担うエンタープライズシステム・医療系システムなどでは、あえて冗長な記述をする』
ということが正解となる場合もあると思います。
これは『戦場のノウハウ』であり、あまり一般的に言われないことかも知れませんが。
世の中には様々な背景や目的で書かれたソースコードがあります。
ざっと、以下のような種類があると言えるでしょう。
このように、シチュエーションによって様々な特徴のあるソースコードが存在します。
プロダクトの特性やライフサイクルに応じた、適切な書き方を採用すべきではないでしょうか?
ソースコードについて、どれくらいのメンテナンス性をもたせるべきか、という論点で整理してみます。
以下は、開発工数に対してどれくらいの保守工数がかかるか、というシミュレーション表です。
シナリオ | 開発工数(p1) | 年間保守工数(p2) | 初年度工数(p3 = p1 + p2) | 5年運用時工数(p3 + (p2 * 4)) |
|---|---|---|---|---|
A(雑に早く作る) | 10h | 30h | 40h | 【160h】 |
B(丁寧に作る) | 20h | 10h | 30h | 【70h】 |
結論としては、
『5年時総工数:A(雑)は160h。B(丁寧)は70h』
となり、Bの方が大幅に工数が低いということになります。
整理するとこういうことです↓
長期運用するプロダクトほど、メンテナンス性に投資した方が効率がよい、ということですね。(当たり前かもしれませんが)
『作るのは一瞬、保守は10年!』ということです。
同時にこの視座は、『フレームワーク選定時、初期コストとメンテナンス性、機能とコード量をどのバランスで考えるべきか』という論点もはらんでいます。
特に自社サービスを運営する場合、メンテナンス性・可読性・保守性は重要な要素となります。メンテナンス性は保守のスピードに直結します。
ちょっと想像してみてください。
あなたの担当した開発箇所でバグが発生しました。
電話がずっと鳴り響き、社内からも質問攻め。そんな中で1分でも早く問題箇所を特定して、修正しなければならない。場合によってはセキュリティに関わる問題かもしれない・・
こんなときに、落ち着いてデバッグするのは至難の技です。
ちょっとしたif文みたいなものも、複雑な条件が少し含まれるだけで、人間の判断速度が落ちていきます。
だからこそ、『可能な限りシンプルに、わかりやすく』コーディングすることが重要なのです。
自分でのセルフチェック、またはソースコードレビューの際に、以下のようなチェックを行うと有効だと思います。
こんなことに注意して、『素敵な手紙名人』になりたいものです。
今回は「プログラミングは手紙のように ~メンテナンス性の高いソースコードの工夫~」ということで、ソースコードのメンテナンス性について書かせていただきました。
いかがでしたでしょうか?
みなさんの参考になれば幸いです。