プログラミングと「職場の安全」は似ている

2020年2月14日

私はある職場で「リスクアセスメント」というものを習ったことがあります。建設現場や工場など、作業者の身に危険が及ぶような職場で、安全を保つためのメソッドとして広く採り入れられているものです。

そこではまず、職場に潜む危険源を皆で列挙し合ってから、次のような順番で対策を打つことを考えます。

  1. 本質的対策 …… 危険源そのものを除去する
  2. 工学的対策 …… 危険源に近づけないような工夫を施す
  3. 管理的対策 …… 安全のための表示・ルール・マニュアルを設ける

上に行くほど、より安全な対策です。私はこの考え方が、「よいプログラミング」の概念とまったく同じであることに気づきました。そして、もともと漠然としていたアイデアがこのように体系立てられたことで、自身のプログラミングの品質は確実に高まりました。今回はその考え方についてご紹介します。

本質的対策

本質的対策とは、一言で言えば「危険源そのものを除去する」という対策です。

職場であれば、危険な作業や危険な機械のことを考えます。これらをなくしても問題がないのならなくす、あるいは別の危険でない手段に置き換える。これが本質的対策です。

プログラミングの世界では、これは「コードを書かない」ことに相当します。「それでは仕事にならないだろう」と反射的に思われた方は、少し立ち止まって考えてみてください。コードを書くことを生業にしながらも、コードを書く行為がバグを生むのだという認識に立ち、コードを書かないで済む方法を常に考える。これこそが大事なのです。

短く書く

まず基本的な話として、まったく同じ仕事をする100行のコードと10行のコードがあれば、10行の方がより優れているということがいえます。潜伏するバグの数はおおむねソースコードの行数に比例すると考えられているからです。この「短く書く」という行為は、ここでいう本質的対策に他なりません。

使えるものは使う

それから、既存のコードや標準ライブラリでできることを、それと気づかず自前で作ってしまうのもよくある過ちです。ソフトウェア業界ではこれを「車輪の再発明」と呼ぶことがあります。「使えるものは使う」というのも、本質的対策の一つです。繰り返しますが、コードを書く行為がバグを生むのですから。

余計なコードを書かない

「将来を見越した設計にしておこう」と考え、少し余分にコードを書いたことはありませんか? 私はあります。経験の浅い時分に陥りがちな思考ですが、これも避けるべきです。余計なコードはバグの温床です。コードは必要になったときに書けばいいのです。これは YAGNI の原則として知られています。

ノイズを除去する

誰も使っていない関数や形骸化したコメントを削除するというのも、本質的対策の一環といえるでしょう。「削除する」という行為は多くの人にとって得も言われぬ恐怖を抱かせるようで、これを率先して行う人をほとんど見たことがありません。ですが、プログラムを書くおそらく誰もが、意味のないコードやコメントに騙されて憤慨した覚えがあるでしょう。だとすれば、後に続く人のためにも、そのような危険源は取り除いてあげるのが人の道というものです。

自動化する

コードを書く仕事をしていると、たまに自分が機械になったような気分になることがあります。たとえば Excel に書かれた数表をそのままソースコードの形に置き換えるような時です。一度なら構いませんが、幾度となく繰り返すことになるようであれば、自動化を検討しましょう。この場合なら、コンバータを作り、ソースコード生成を自動化するのです。最初はコンバータ自体のバグに悩むかもしれませんが、こなれてくれば、驚くほどバグが出なくなります。

「コードを書かない」ことにつながる行為だけでも、ざっと挙げてこんなにもあります。これらはいうなれば、プログラマーにとって基本行動といえるものです。

工学的対策

本質的対策が不可能であれば、次に工学的対策を考えます。工学的対策は、「危険源に近づけないような工夫を施す」ことです。典型的には、近づくと危険な機械を柵で囲って簡単に近づけないようにする対策などがそうです。

これをプログラミングの世界に置き換えると、「設計の意図に反するコードはコンパイラが拒絶するような工夫を施す」ことに相当します。これだけだと抽象的すぎますから、具体例を挙げていきます。

const を活用する

プログラミングにおける工学的対策の最たるものは const です。これをうまく活用すれば、ケアレスミスによる不意な値の書き換わりをコンパイラが防いでくれるからです。実のところ const は使っても使わなくてもプログラムの挙動自体に影響を与えないことがほとんどですが、だからこそ、このキーワードの使い方はプログラマーの成熟度を如実に表します。const は掘り下げると奥が深くなるため、別記事を参照してください。

型システムを活用する

たとえばプログラムの中で、ある ASIC の状態を AsicState、ある FPGA の状態を FpgaState という enum で表現したとします。ここで、引数に AsicState を取る関数に FpgaState を放り込むと、当然コンパイルエラーになります。これは「状態」という混同しやすいものの取り違えを、コンパイラが防いでくれる一例です。

コンストラクタの制約を活用する

たとえば initialize のような名前の関数を作り、それを起動時に呼び出せば、コンストラクタを呼ぶのとさほど変わらないという認識をお持ちではありませんか? もしそうなら、大いなる誤解です。コンストラクタは、「必ず一度」「定義されたように」呼ばなければコンパイラが許さないという点で特別です。そして、静的に(つまりコンパイル時に)決定できるファクターは、たとえ引数が10個になろうと20個になろうとコンストラクタに押し付けるべきです。コンストラクタの活用を徹底することは、考えられている以上にプログラムを引き締める効果があります。

これらの工夫に共通するのは「コンパイラに適切なサインを送る」という点にありますが、このことは実はそっくりそのまま他のプログラマーへのサインにもなるという点にお気づきでしょうか。プログラムに制約を課せば課すほど、読み手にとっての解釈の幅が狭まるからです。つまり工学的対策は安全というだけでなく、ソースコードを読みやすくする効果もあるのです。

管理的対策

上の二つの対策でカバーしきれない領域に対して、管理的対策が適用できないかを考えます。順番はあくまで最後です。管理的対策は、「安全のための表示・ルール・マニュアルを設ける」ことです。

ここで、ソースコードコメントやコーディング規約がプログラミングのそれに相当すると単純に考えるのは早計です。これらを設けるにしても、あくまで上位の対策でカバーしきれない領域を補強するのだという意識を持たなければ、的外れになってしまいます。

よいコメントを残す

よいコメントとは何か。言い切るなら、プログラムの中で「これは説明されなければわからないだろうな」と思えるような暗黙の決め事を説明するコメントです。たとえば「この関数は失敗時に NULL を返します」というコメント。基本的には使用可能なポインタを返すが NULL だけは例外値である、という暗黙の決め事を説明しています。他には、「この enum の並びに依存した配列があります」というコメント。ここで知らずに enum の定義だけ書き換えるとバグが噴き出しますが、かといってコンパイルエラーで防ぐのも難しい問題です。

アプリケーションハンガリアンを活用する

アプリケーションハンガリアンの詳細については別記事にしましたので、こちらを参照してください。

ジョエル・スポルスキーの書いた記事「間違ったコードは間違って見えるようにする」の中で、この考え方の本質を言い表している箇所を引用します。

汚いコードに対する嗅覚を生かせるような仕組みを、最初から作り込むようになる。これこそ本当のアートだ。エラーが画面から飛び出して見えるような規則を考案することによって、頑強なコードを作り出すのだ。

私はこのたった一つの記事に、プログラミングスタイルを変えるほどの多大な影響を受けました。プログラムを仕事にする人には、ぜひご一読いただきたい記事です。