見出し画像

プログラムに美しいもヘッタクレもある?


しばしご無沙汰しておりました。ゼバ会です。
お久しぶりでございます。

名前を覚えてくださった方には割と唐突な休載だったと思うんですが、プログラマーのサガで炎上案件に巻き込まれてしまいまして、ここ2ヶ月は火消しに勤しんでおりました。
いやマジ大変でしたわ。。。
まぁ、新しいネタが見つかったという意味では、それなりに実りはありましたけどね。

でも大変だったよ。。。。
大変だったよ!
チームで一番苦労したのは俺じゃないけど、それでもすごく大変だったよ!

。。。。はい。
気を取り直して本日は、「プログラムの美しさ」というテーマでお送りします。
プログラミングというものにおいて、出来上がったプログラムコードの美感はどういう観点で決まるのか、というお話。

ちなみに、、、
こと「美しさ」「美的観点」という話題になると、なにかにつけ「結局は人それぞれ」という結論にしたくなる方もいると思います。
ですが今回はそういう系統じゃなく、全てのプログラマーが覚えるべきコツの話です。

仮に、読者の皆さんの中に宝石を美しいと感じない人がいたとしても、その人にだって「世の中では宝石は美しいとされている」ことは理解できるはずです。
「宝石が美しいだなんて聞いたこともない。あんなの石ころだというのが世界の常識だ」なんて人がいたら、その人はよほど物知らずでしょう。

つまり、たとえプログラムコードの美しさの定義が人それぞれであったとしても、プログラムは美しくあるべきだし、かつ「世の中的に美しいといえるコード」は定義できるわけです。

今回私が遭遇した炎上案件も、原因の1つがこの「コードの美しさ」の部分にありましてですね。その経験も踏まえて、だったらどんなコードだったら炎上せずに済んだのか、というところもお話したいと思います。(もちろん炎上までしたからには原因はそれだけじゃないんだけど、、、)
さて、いったいそれはどんなコードでしょうか?

〇プログラムの美しさは「メンテナンスのしやすさ」で決まる

まぁ、簡単な話なので最初に答えから言ってしまうと、プログラムの美しさは、その後のメンテナンスのしやすさで決まります。

だってそうですよね?
「どういうふうに書けば後でメンテしやすいか」は人それぞれでも、「後で見やすいようにコードを整える」という目的は、プログラマーであればみんなの共通事項です。

長いエンジニア人生の中で皆さんにも1度くらい経験があると思うんですが、自分1人のために1度だけ実行してその場で破棄するプログラムは、きっといつもよりもグチャグチャでしょう。
ですがもし、当初すぐに破棄する予定だったそのプログラムを、その後何度も使わなきゃいけなくなったら?
あるいは、そのグチャグチャなプログラムを、別の担当者にも使わせないといけなくなったら?

そのような不遇なプログラムを、最初に作ったグチャグチャの状態のまま他人に見せる人は、ガチのド新人か、よほど意地悪な人だけでしょう。
通常は「ちょっとはキレイにしなきゃ」って思うものじゃないでしょうか?

なぜなら、
1.プログラムを長期的に運用するには
2.その中身がいつ読んでも理解できないといけない
からです。

そこには「プログラムとは、状況に合わせて日々作り変わるもの」という前提があります。
ゆえに、作り変える必要性のないプログラムには、コードをキレイにする必要性を感じないのです。

すなわち、美しさの定義の根拠となる「どう整えればメンテナンスしやすいか」という考え方が人それぞれであったとしても、「日々の作り変えのやりやすさこそがプログラムコードの美しさである」という結論はみな同じというわけです。

〇作り変えやすいプログラムの特徴

では、具体的にはどういうコードを書けば、そのコードが美しい(つまりメンテナンス性が高い)と言えるのでしょうか。
そのために注視すべきポイントはたった1つだけです。
中括弧は独立行に書くべきとか、1行当たりの文字数は120文字以下であるべきとか、そういう細かいコードルールの類は全て、たった1つのポイントを守るだけのために存在するにすぎません。
それはこれです。

「次の人が、ビジネスロジックを頭の中に思い浮かべやすいこと」

これだけです。
今プログラムを書いているあなた自身ではありません。
数日後・数ヶ月後に見返したときの見やすさでもありません。
あくまで、「次にこのコードを見る赤の他人」にとって見やすいことが重要なのです。

その際、もう1つポイントになるのは、思い浮かぶものがシステムロジックではなく「ビジネスロジック」である点です。
コード内に sendmail() と書いてあれば、システムとしてメールを発信しようとしている意図は分かります。
なのでそこに「ここでメールを送信する」とコメントを書く人はそうそういません。
ですが前後のコードがぐちゃぐちゃだと、なんのためにメールを送りたいのかは、必ずしもすぐ分かるとは限りません。

そういうときはコメントやドキュメントを残すことも重要ではありますが、それらは絶対100%の保証にはなりません。
中には、コメントが大量にあるばかりに、「コードがコメント通りかどうか」の確認が必要になってしまったケースだってあるでしょう。

つまりプログラムは、コメントやドキュメントがどんなに適切に作られていたとしても、最終的には「コードを見てビジネスロジックを理解する」という工程が必須なワケです。
ゆえに、そんな御大層なことをどうやって次の人にやってもらうか、が重要になってきます。

ドキュメントを見てない人にビジネスロジックが伝わりやすければ伝わりやす


しばしご無沙汰しておりました。ゼバ会です。

名前を覚えてくださった方には割と唐突な休載だったと思うんですが、プログラマーのサガで炎上案件に巻き込まれてしまいまして、ここ2ヶ月は火消しに勤しんでおりました。

いやマジ大変でしたわ。。。

まぁ、新しいネタが見つかったという意味では、それなりに実りはありましたけどね。


でも大変だったよ。。。。

大変だったよ!

チームで一番苦労したのは俺じゃないけど、それでもすごく大変だったよ!


。。。。はい。

気を取り直して本日は、「プログラムの美しさ」というテーマでお送りします。

プログラミングというものにおいて、出来上がったプログラムコードの美感はどういう観点で決まるのか、というお話。


ちなみに、、、

こと「美しさ」「美的観点」という話題になると、なにかにつけ「結局は人それぞれ」という結論にしたくなる方もいると思います。

ですが今回はそういう系統じゃなく、全てのプログラマーが覚えるべきコツの話です。


仮に、読者の皆さんの中に宝石を美しいと感じない人がいたとしても、その人にだって「世の中では宝石は美しいとされている」ことは理解できるはずです。

「宝石が美しいだなんて聞いたこともない。あんなの石ころだというのが世界の常識だ」なんて人がいたら、その人はよほど物知らずでしょう。


つまり、たとえプログラムコードの美しさの定義が人それぞれであったとしても、プログラムは美しくあるべきだし、かつ「世の中的に美しいといえるコード」は定義できるわけです。


今回私が遭遇した炎上案件も、原因の1つがこの「コードの美しさ」の部分にありましてですね。その経験も踏まえて、だったらどんなコードだったら炎上せずに済んだのか、というところもお話したいと思います。(もちろん炎上までしたからには原因はそれだけじゃないんだけど、、、)

さて、いったいそれはどんなコードでしょうか?


〇プログラムの美しさは「メンテナンスのしやすさ」で決まる

まぁ、簡単な話なので最初に答えから言ってしまうと、プログラムの美しさは、その後のメンテナンスのしやすさで決まります。


だってそうですよね?

「どういうふうに書けば後でメンテしやすいか」は人それぞれでも、「後で見やすいようにコードを整える」という目的は、プログラマーであればみんなの共通事項です。


長いエンジニア人生の中で皆さんにも1度くらい経験があると思うんですが、自分1人のために1度だけ実行してその場で破棄するプログラムは、きっといつもよりもグチャグチャでしょう。

ですがもし、当初すぐに破棄する予定だったそのプログラムを、その後何度も使わなきゃいけなくなったら?

あるいは、そのグチャグチャなプログラムを、別の担当者にも使わせないといけなくなったら?


そのような不遇なプログラムを、最初に作ったグチャグチャの状態のまま他人に見せる人は、ガチのド新人か、よほど意地悪な人だけでしょう。

通常は「ちょっとはキレイにしなきゃ」って思うものじゃないでしょうか?


なぜなら、

1.プログラムを長期的に運用するには

2.その中身がいつ読んでも理解できないといけない

からです。


そこには「プログラムとは、状況に合わせて日々作り変わるもの」という前提があります。

ゆえに、作り変える必要性のないプログラムには、コードをキレイにする必要性を感じないのです。


すなわち、美しさの定義の根拠となる「どう整えればメンテナンスしやすいか」という考え方が人それぞれであったとしても、「日々の作り変えのやりやすさこそがプログラムコードの美しさである」という結論はみな同じというわけです。


〇作り変えやすいプログラムの特徴

では、具体的にはどういうコードを書けば、そのコードが美しい(つまりメンテナンス性が高い)と言えるのでしょうか。

そのために注視すべきポイントはたった1つだけです。

中括弧は独立行に書くべきとか、1行当たりの文字数は120文字以下であるべきとか、そういう細かいコードルールの類は全て、たった1つのポイントを守るだけのために存在するにすぎません。

それはこれです。


「次の人が、ビジネスロジックを頭の中に思い浮かべやすいこと」

これだけです。

今プログラムを書いているあなた自身ではありません。

数日後・数ヶ月後に見返したときの見やすさでもありません。

あくまで、「次にこのコードを見る赤の他人」にとって見やすいことが重要なのです。


その際、もう1つポイントになるのは、思い浮かぶものがシステムロジックではなく「ビジネスロジック」である点です。

コード内に sendmail() と書いてあれば、システムとしてメールを発信しようとしている意図は分かります。

なのでそこに「ここでメールを送信する」とコメントを書く人はそうそういません。

ですが前後のコードがぐちゃぐちゃだと、なんのためにメールを送りたいのかは、必ずしもすぐ分かるとは限りません。


そういうときはコメントやドキュメントを残すことも重要ではありますが、それらは絶対100%の保証にはなりません。

中には、コメントが大量にあるばかりに、「コードがコメント通りかどうか」の確認が必要になってしまったケースだってあるでしょう。


つまりプログラムは、コメントやドキュメントがどんなに適切に作られていたとしても、最終的には「コードを見てビジネスロジックを理解する」という工程が必須なワケです。

ゆえに、そんな御大層なことをどうやって次の人にやってもらうか、が重要になってきます。
ドキュメントを全く読んでいない人に、ビジネスロジックが伝わりやすければ伝わりやすいほど、そのコードは「美しい」ということになります。

〇最適解は DIC 法


あくまで私個人の最適解であって、別に世界の賛同を得たわけでもなんでないのですが、プログラムの記法は以前にも紹介した「DIC法」がやはりどう考えてもベストです。
(ですので同じ方法を別の名前で呼んで常用している人もたくさんいるはず)

1. ビジネスロジックとシステムロジックを1つのメソッドに混在させない
2. 最上位のビジネスロジックに限り、以下のルールを適用する
  A. ビジネス上の手順1つを1行で記述する
  B. 処理1行につきコメント1行を書く
  C. 処理とコメントが矛盾する状態で保存ボタンを押してはならない
3. 2階層目以降のメソッドも、「ビジネスロジックであるかぎり」「可能な範囲で」このルールに従う
4. システムロジックについては、コードを読めば分かるものにいちいちコメントつけない
5. Cyclomatic complexity の最大値をチーム内で話し合って決め、みんなで守る

私自身も常にではありませんが、状況が許す限りプログラムは上記のルールに従うことにしています。
以前(たしか第3回だったかな?)でもサラッとは紹介したんですが、そのときはザックリだったし、細部も意味不明なまま放置だったので今回もう少しだけ説明します。

まずDIC法という名前は Document In Code の略です。
つまり、詳細設計書のメンテナンス不備を防ぐために、プログラムに詳細設計書を含めちゃおう、ってこと。
出発点はそういう意図でした。

それからこの方法を使うにあたって重要になってくるのが、自分が書いているメソッドが、システムロジックなのかビジネスロジックなのかをちゃんと区別しながら作ること。
方法論自体が、この区別ができる前提で作られています。

ビジネスロジックとは: 人間がやりたい作業を、人間都合で作った手順
システムロジックとは: コンピューターを操作するための(人間都合など関係ない)コード

この世の全てのプログラムは、「ビジネスロジックが、システムロジックを呼び出す」という階層構造で作られています。

public function noticeInformation( int accountId )
{
   // 1.1. アラートが発生していないかをチェックする
   Notice notices[] = checkNoticeExists();
   // 1.2. アラートがなければ何もしない
   if (notices.length == 0) return;
   // 2. アラートを通知すべき対象者をリストアップする
   Account accounts[] = listupNoticeTargets(notices);
   // 3. 順番にアラートを通知する
   throwNotices(accounts, notices);
}

たとえばこの例では、noticeInformation がビジネスロジックです。
ですので、もし人間が手作業で同じことをするとしても、同じように行われるであろう手順書、のような造りになっています。
それに対して checkNoticeExists listupNoticeTargets throwNotice の各内部は、(おそらくは)データベースからデータを引っ張ってくる、メール配信サービスをコールするなど、人間が手作業でやるときとは違う処理が書かれているはずです。

仮に上記の処理を人間が手でやるんだとしたら、こんな手順になるでしょう。

1. Web画面かなんかを見てアラートを確認
2. アラートがなければホッと一息(業務を終わる)
3. 紙の一覧表かなんかで通知の対象者をリストアップ
4. メールかなにかで注意を促す

ほら。書いてあるコメントと同じことしてるでしょ?
こういう処理をビジネスロジックと呼ぶワケ。

でもコンピューターはユーザーを紙の一覧表で管理したりしないし、おそらくはデータベーステーブルで管理しているはずです。
なので listupNoticeTargets メソッドの中身は、人間の手作業とは異なることをしていることになり、これはシステムロジックになります。

なお、もし上記以外にたとえば「上司に確認を取る」みたいな人間と同じ作業が入ってくるのであれば、そのときはそのロジックはビジネスロジックといえるもので、「ビジネスロジックがビジネスロジックを呼び出す」ような構造ってことになります。
その場合、2階層目のビジネスロジックは可能な範囲でなるだけ DIC法 に従う、くらいの空気感がちょうどいいと思います。

別に自慢じゃないんですけど、この方法で書いたプログラムは、かつての同僚とかからもメッチャ好評でした。
読みやすいって。

多分、多少見様見真似でもなんとかなる類のものかと思うんで、一度試してみてください。

〇クラスにするまでもない階層構造をレジオンで管理する

当然ですが私の DIC法 は、昔ながらのスパゲッティコードと比べれば、メソッドの数がどうしても多くなります。
なんせ、スパゲッティコードはメソッドの数が物理的に1個っきゃないですからね。
それと比べりゃ、どんなふうに書いたってメソッドの数は絶対多くなるだろうよ。

で、本来の構造化プログラミングからすれば、ビジネスロジックの階層が分かれるなら新しいクラスを切る、というのが本当は正しい方法なのかもしれません。
ですが、やたらめったら大量にクラスがあると、それはそれで見づらいのも確かです。
そんな「階層は分かれるけどクラスを分けるほどじゃない」ような構造は、以下のような工夫をすることで整理することができます。

// #region ユーザーアラート通知

/** メソッドの説明 */
public function noticeInformation( int accountId )
{
   // 処理
}

/** メソッドの説明 */
private function checkNoticeExists()
{
   // 処理
}

/** メソッドの説明 */
private function listupNoticeTargets(Notice notices[])
{
   // 処理
}

/** メソッドの説明 */
private function throwNotices(accounts, notices)
{
   // 処理
}

// #end region ユーザーアラート通知

メソッドの説明のさらに外側を #region ~ #endregion で囲んでいます。
コメントの書き方を工夫して

// ----------------〇 ユーザーアラート通知関連 START 〇----------------
// ----------------〇 ユーザーアラート通知関連  END  〇----------------

こんなふうに目立つようにしてもいいでしょう。
他には、「クラス内クラスを利用する」「無名関数を利用する」などの方法を使ってもいいかもしれません。
あるいは、メソッド1つ1つをさらに読みやすくするための工夫として、メソッドの内部構造を標準化してしまうなんてのもイイんじゃないでしょうか。

まぁ、具体的な方法論はどうでもいいんですよ。
大事なのは、

1.次にプログラムコードを見た人がどんな人でも
2.ビジネスロジックがすぐに理解できるように書く

ことです。
それこそ、次の担当者が稀代の変人とかだったりしてもね。
つまりプログラムの美しさは、次の人に対する思いやりの大きさで決まると捉えることもできるわけです。

〇次回予告

さて、最終的に今回は、全てのプログラマーが共通で目指すべきものの話になりました。
結論! 人類みんなが思いやりを持てば炎上案件なんて発生しねぇ!

全てのプログラムは、「あとで修正しやすいように」書かれるべき。
これが、プログラマーがプログラムをキレイに整える理由です。

さて、共通部分の話が終わったところで、次回は人によって違う部分の話をしましょう。
つまり、一般にコードルールとして採用されがちな各種項目に関して、「なぜそのようなルールが必要なのか」という理由を紐解いていきます。

たとえば、こういうの。
・if 文直後の中括弧を省略してはならない
・関数名の直後のカッコの後ろはスペースを1つ開けなければならない
・文末が空白で完了していてはならない

皆さんが今いるチームのコードルールにも、すんなり納得できたものや、どういう意味があるのかいまだに理解不能なものなど、色々あるでしょう。
「プログラムを整えるべき理由」を理解した今の皆さんなら、そういうのがより深いレベルで理解できるようになっているはずです。

次回はそこを細かく紐解きます。
みんな疑ってるけど実はメチャクチャ効果のあるルールや、世の中で重要だと信じられてるけど実は意味なかったもの、なんてのもあるかもしれません。

てなわけで、おったのしみに!!!

この記事が気に入ったらサポートをしてみませんか?