組込系の1つの現場

これまでほとんどビジネス系ソフトウェアにしか関わってこなかったが、最近、組込系の1つの現場でヒアリングする機会があった。差分開発の典型的な現場であった。ビジネス系との際立った(私が感銘を受けた)差異を挙げておきたい。

(1) 機能/非機能が通じない!

現場では「機能/非機能」が通じないのだ。"機能"が伝わらないのだから"非機能"が分かる訳がない。あるいは、非機能をくっつけてセットにしたものだから、逆に機能が分からなかったのかもしれない。「機能とは制御仕様のことか」と問われ、制御仕様書を拝見して、制御仕様には機能仕様と性能や信頼性といった非機能要件が含まれていることを語った。

「永続化」も通じなかった。不揮発性メモリへのデータ保持と換言した。「UI」も通じなかった。帰宅して妻に話したら「GUIって言えば通じたのでは」という。

個人的な知見の差でもあるが、組込系とビジネス系ではそれぞれ閉じた世界の中だけでやってきた風潮もあるとは思う。私自身が、組込系も、差分開発/派生開発も、そのドメインも知らなかったのは事実ではある。

(2) 事前条件/アサーションがない!

「事前条件/事後条件」が通じない。アサーションなど通じる訳がない。「事前条件は前提条件で置き換えてください」と言われる。不変条件はどうなる。持ち出すと面倒なので触れずに、制御仕様の前提条件ということで収めておいた。

組込系では、制御仕様で参照する大域的な外部変数については、その値は正しいという前提で構成されているのだ。つまり、その外部変数について事前条件を満たしているかどうかをアサーションで妥当性チェックするような必要はないのだ。事前条件という概念、もっと狭い定義としても値のレンジチェックなどを参照側で行うという概念が存在していなかったのだ。また後述するが、単体テストの実態がないことも事前条件チェックやアサーションが軽視される理由でもある。実装はC言語のようだが、assertライブラリを組み込むこともしないのか、あるいは、してはいけないのだろう。

外部変数に対して値を設定する側の制御仕様において、値の妥当性をチェックしてから格納することになっている。事後条件とは呼ばないが、それに相当するものは存在していることになる。

このメカニズムの意図は、格納された変数の値の妥当性を参照側の各所でチェックすると、コード量も増え、実行時のパフォーマンスへの影響が懸念されるためだ。そこまでシビアなものだと説明されると、それ以上の説得はできない。個人的には感銘した1点だ。納得した訳ではないが、現実として受け止めた。

ただ、悲しいかな、そのヒアリングを行った席のすぐ近くには2次サプライヤの方々の常駐席があり、そこにはアリスター・コーバーンの「ユースケースの書き方」の本が積まれてたりする。事前条件を知らない訳がない。ソフト開発は2次サプライヤに丸投げされているケースがほとんどのようで、知らないのはプロパーだけという可能性もありそうだった。

(3) デバッグしにくいswitch文ばかり!

制御仕様からコードを推察したところ、switch/case文が多用されている。最近switch文は敬遠されるところもあるというのに全盛だ。変数には必ず初期値を設定し、caseで引っかからなかった場合(otherwiseの場合)にその初期値を利用する記述で統一されている。私は、所見(初見)で、なんらかのそれらしい値が常に入っているために、おかしな値が入っていても気付かないし、otherwiseの場合をアサーションでチェックすることもできないのだろうと思った。

しかし、ここがもう1つ感銘を受けた点だ。実は、この変数の初期値には必ず安全側の値が設定されているのだそうだ。デバックできなかった場合、バグが盛り込まれてしまった場合でも、最悪の事態が起きないように、より安全側で動作するような値が必ず入っている。値がnullということはありえないのだそうだ。その分、デバッグは難しくなるが、フェールセーフになっているのだそうだ。そうは言われても納得した訳ではない。

(4) コードはifdefのお化け!

過去のコードを流用した差分開発であり、しかも製品のグレードによるバリエーションや海外仕向けによるバリエーションも多い。コードはifdefのお化けなのだ。問題となるのは、過去のコードでifdef=falseの部分だ。何世代も流用を繰り返すと、動くコードなのか、検証されたコードなのかは分からない。しかし、納期に追われると、なんらかの根拠らしきものを勝手に見出してtrueにして動けば儲けものという状況になるらしい。バグの多くは、このifdefを復活させたことで起きている。地雷を踏むと、慣用的に表現されるくらい頻度の高いことらしい。

差分開発では、ベースとなるコードが実際に稼動しているという実績がある。この実績という過信がテストを省いたり、検証されてもいないコードを復活させてしまう誘因になっている。

(5) 実は、単体テストはしていない!

動的なデバッグ単体テストはほとんどない。コードレビューとコンパイルの次は、すぐ結合テストとなる。単体テスト結合テストを区別していないという表現が正確なのかもしれない。ハードに組み込んでやった方が効率がいいからだそうだ。勿論、最初のプロトタイプでハードがない状態では単体らしきことは行うが、差分開発では過去のハードやテスト環境が整っているのでそれを利用するらしい。単体テストがないので、デバッグとかアサーションという概念がない。

(6) チェックリストだらけ!

CMMIでいうところの成熟度1のレベルなのだ。プロセスも明確に定義されず、むちゃくちゃな差分開発を繰り返している。それでも品質は保持しなければならない。そのため、チェックリストだけが随所に膨大に用意されている。ツギハギだらけで、粒度も順序も適当、各チェック項目をどんな手順と基準でチェックするかといった具体的な記述はない。体系化やパターン化などほど遠い。

実際、どうやってチェックしているかと問うと、経験豊かなエンジニアはチェックリストの項目はすべて理解しているので、コード開発中に適宜チェックできていると断言。実際にチェックリストにチェックを入れるのは、後でまとめて思い出しながら行っているとのこと。形式的なもので、実態はないと見た。

バグが出ると発注元からクレームが上がり、その再発防止策としてチェックリストに項目だけを追加してきた経緯があるようだ。お粗末過ぎる。

(7) タスクは名前しかない!

明示的なプロセスがない状態であり、唯一WBSがあるというので拝見したが、せいぜいV字モデルに割り当てられたタスク名の字面が決められているに過ぎなかった。先の制御仕様書の中にコードそのものが出てくるくらいだから、要件(要件開発)と設計(技術解)の分離とか段階的詳細化などといってもほど遠い。工程やタスクが明確でないので、当然、成果物も要件定義のものなのか、設計のものか、構築のものかさえはっきりしない。

(8) 構成管理しない!

複雑なバリエーションを扱う差分開発は、単純に考えても構成管理が重要だとわかるが、構成管理はしていない。過去の製品のコードがそれぞれ残っているだけだ。それを継承して差分開発する場合にはそのコードを2次サプライヤにごっそり渡せばよい。コードは管理しているので、構成管理していないとは言いすぎかもしれない。管理しているものは制御仕様書とコードのみと言ってよい。実際には差分開発なので、変化点と新規点を整理したドキュメントはなんらかの形式で存在はしている。それをあわせても3つの成果物だけだ。

実は、バグ管理もされていない。せいぜい管理されているバグは発注元からのクレームだけである。流出した不具合と呼んでいるが、これに対してだけは非常に厳しく原因分析して、再発防止策を練る。5段階のなぜなにを繰り返せ、現地・現人・現物に当たれという呪文のような文化だけは浸透している。その結果が、チェックリストに項目を追加することで終わってしまうことは残念ではある。

まとめ

1つの断片的な非常に偏った現場であったことを期待する。唯一体験した現場が特異な環境であったことを期待している。

ビジネス系の現場からみれば、めちゃくちゃな感じがするし、正直なところ、エンジニアリング的には遅れ過ぎているといわざるを得ない。私は、つい「10年以上遅れている」と失言してしまった。これに対し、担当者はこうも力説された。「我々からみるとビジネス系の開発はばかばかしい。あのような開発では儲からない。我々は、差分開発により最低限のコード開発で多くの製品を生み出し、利益を得ている。工数のかかるテストをしてはいけないんだ。テストをしなくていいように(V字モデルの品質の検証側を低コストにするという意味で)十分に品質を作り込んでいるのだ」と。

こんな状況でなぜ、それなりにうまく行っているのか。経験豊かなエンジニアや2次サプライヤの暗黙知だけでなんとかこなしているように見えた。