エンジニアとして良い仕事をするために必要なこと
ソフトウェア業界で日米を往復しながら仕事をしていると、世界中のさまざまなエンジニアに会う。私のように「プログラミングを心底楽しんでいる」人から、「新3K」(きつい・厳しい・帰れない)を身をもって体験している人までさまざまだが、共通して言えることは、エンジニアとしての基礎がしっかりできている人とできていない人では、その生産効率に大きな開きがあり、それが結果的には、会社での労働環境や待遇に、そして結果として自分自身にとっての「仕事の充実度」に、大きな影響を与えているということである。
いつも締め切りに追われている、毎回バグで苦しんでいる、徹夜の連続で体力に限界がきているなど、「仕事がきつい」理由はいろいろとあると思うが、会社や上司の悪口を言う前に、自分自身がプロフェッショナルなエンジニアとしてこの業界で勝負をするうえで必要な最低限の基礎ができているかを一度見直したほうがよいと思う。
足腰の筋肉が発達していないサッカー選手が、いくらボールの扱いが上手になったとしても一流の選手にはなれないのと同じように、基礎がしっかりしていないエンジニアがRailsだAjaxだAndroidだと目新しい技術に飛びついたところで、けっして良い仕事はできない。
意外とちゃんと理解されていないオブジェクト指向
ソフトウェアの基礎と言えば、「構造化プログラミング」「オブジェクト指向」や「MVC」(Model-View-Controller)などが代表的なものだ。現場のエンジニアに、「オブジェクト指向とは何か」を説明するように求めると、「情報隠蔽(いんぺい)と継承と多様性と動的束縛のことですね」などの教科書的な答えや、「JavaやC++などのオブジェクト指向言語でプログラムを書くことです」などの一見まっとうな答えが返ってくる。にもかかわらず、実際の現場で仕事をさせると、スパゲッティコードを量産するエンジニアがたくさんいる。なぜそんなことになってしまうのだろう。
さまざまなエンジニアと一緒に働きながら彼らの働き方やコードを見て私が到達した答えは、大半のエンジニアが表面的にしかものを見ておらず、本質の部分を理解せずに理解した気になってしまっているのに尽きるということだ。
数年前に、私の会社で組み込み機器向けのプロジェクトで、C++コンパイラが安定して動かないのでCでプログラムを書くように私が指示したケースがあったのだが、何人かのエンジニアが「今さらCなんかでプログラムを書けませんよ。クラスを使えなければスパゲッティコードになってしまいます」と猛反対をしてきた。しかたがないので、Cを使っても十分にオブジェクト指向のプログラミングができることを、実際にコードを書いて説明しなければならなかったのだが、彼らがまさに、典型的な「オブジェクト指向の本質を理解していない」エンジニアの例だ。
どうしてスパゲッティコードができてしまうのか?
「それなりに動く小規模なプログラムを書く」ことは、少しでも勉強した人なら誰でも簡単にできる。しかし、機能を加えていくに従い、「プログラム全体を見渡して必要な変更を加えること」が次第に難しくなり、作業効率が落ちていく。しまいには手もつけられないぐらい複雑に絡み合ったコードの固まり(=スパゲッティコード)ができてしまい、機能追加どころかバグの修正もままならなくなってしまう。エンジニアなら誰でも直面する問題だ。
ここで注目すべき点は2つである。1つ目は「コードの大きさに反比例して作業効率が落ちていく」こと。作業効率が一定であれば、一定量のコードを書くのに必要な時間は平均すれば一定のはず。しかし、コードに依存関係がある場合、どうしても100行のプログラムに10行のコードを追加するよりも、1,000行のプログラムに10行のコードを追加するほうがコストが高くなる。
このコード間の依存関係がある程度以上高くなると、2つ目の「手もつけられないぐらい複雑に絡み合ったコードの固まり」ができてしまう。そうなると数行のコードを追加・変更するだけでさまざまな副作用を考慮しなければいけなくなり、生産効率が極端に下がりはじめる。いったんこんなコードができてしまうと、「あるバグを直すと、その副作用で別のバグが発生する」という、もがけばもがくほど深みにはまっていく「底なし沼状態」に陥る。
諸悪の根源は「コード間の依存関係」
諸悪の根源は「コード間の依存関係」である。コード間の依存関係が高くなるほど、コードの変更に伴う「予想もしない副作用」が発生する可能性も高くなり、作業効率が下がり、しまいにはメンテナンスが不可能なスパゲッティコードができてしまう。
オブジェクト指向にしろ構造化プログラミングにしろ、この手のコンセプトの目指すところは「いかにコード間の依存関係を減らして、メンテナンスしやすいプログラムを書くか」である。そのためには、コードをある程度の固まり(モジュール)としてグループ化し、モジュール間のインタフェース(もしくはプロトコル)をできるだけ「粒度を粗く」(注1)かつ「明確に」定義し、モジュールをまたがったコードの依存関係を排除する。
たとえば、あるプログラムがモジュールAとBという2つのモジュールから成り立っていたとき、AとBのインタフェースさえ明確に定義してあれば、モジュールAの中のコードをいくら変更しても、インタフェースに影響を与えない限りモジュールBに副作用を与えることはない。それどころか、モジュールBを外部インタフェースだけ同じで中身がまったく異なるモジュールCで置き換えても、何の問題もなく動くはずである。
このしくみを利用すれば、モジュールごとの独立したテストや開発も可能になるし、ハードウェアや接続するサービスごとの違いを意識せずにプログラムを開発することも可能になる。
大切なのは開発言語やツールではない
気がついた人も多いかもしれないが、すでにここまでの段階で、オブジェクト指向の主要なコンセプトである粗結合・情報隠蔽・多様性・動的束縛の重要性をクラスやインスタンスという言葉を使わずに説明してきた[2]。
つまり、こういったコンセプトは、C++やJavaなどのオブジェクト指向言語を使わなければ利用できないような小手先のテクニックではなく、アセンブラやCを使って開発するときでさえ十分に活用できる、もっと本質的なものなのだ。
そこを理解せずに、「Javaを使ってクラスを作っていれば自然にオブジェクト指向のプログラミングができる」「GUIのプログラムはMVCのパターンに従って作ることが大切」ぐらいの表面的なところしか見ない軽い気持ちでプログラムを書くエンジニアが多いから、スパゲッティコードができてしまうのだ。
「粗結合」なプロジェクト管理のススメ
つまり、スパゲッティコードを避けるのに最も効果的な方法は、つねに「粗結合」を意識しながらコード間の依存関係を最小に保つように心がけることだが、これは複数の人が関わるプロジェクトの進め方そのものにも適用できるので覚えておくとよい。
私は数年前にビデオサービス用のセットトップボックスの開発に関わったが、ハードウェアのプロトタイプすらない段階から、わずか6ヵ月という短期間で、ハードウェア、デバイス側のソフトウェア、Webサービスのすべてをゼロから開発するという困難なプロジェクトであった。
そこで私が最初にしたのは、各モジュール間のインタフェースの定義。アプリケーションがハードウェアを制御するインタフェースと、そのアプリケーションとWebサービス間のインタフェースを明確にしておくことにより、ハード、アプリケーション、サービスの3つを並行して開発することを可能にしたかったのだ。
アプリケーションを担当した私が次に作ったのは、PC上で動くハードウェアのエミュレータと、Webサービスのエミュレータだ。この2つさえ作っておけば、ハードウェアがなくとも、Webサービスの開発が遅れていても、アプリケーションの開発を先に進めることができる。
そして、約2ヵ月間でエミュレータ上でアプリケーションが8割方動くところまで一気にもっていった。ハードウェアとWebサービスの準備が整うまで待っていたら、到底無理なスケジュールである。
その後少ししてハードウェアが届いたので、そこに必要なドライバを載せるなどして、わずか1日の作業で実機の上でアプリケーションを走らせることに成功した。ハードウェアの担当者は、「組み込み機器の開発でこんな奇跡みたいなことは見たことがない」と驚いていたが、エミュレータの上でアプリケーションをさんざんテストしてきた私からしてみれば当然の結果であった。
さらに2ヵ月ほどして、Webサービスの準備もできてきたが、こちらの接続試験は簡単にはいかなかった。そこで、定義したインタフェース通りにWebサービスが動いているかどうかを判定するプログラムを書いて試したところ、Webサービスの担当者がインタフェースの定義を誤解していたことが発見でき、問題をすみやかに解決できた。
こうやって書けば「当たり前のこと」をしただけなのだが、現実のプロジェクトの多くはなかなかこんな風にスムーズにはいかない。その原因はいろいろとあるが、私の経験から言えば、担当者が「なぜインタフェースの定義をきちんとしておくことが大切か」という「なぜ」の部分をしっかりと理解していないケースがとても多い。
「オブジェクト指向の本質とは何か」「いったい何のために必要なのか」をこれを機会に一度頭の中で整理していただくとよいと思う。「オブジェクト指向=情報隠蔽・継承・多様性・動的束縛」などの技術用語を試験前の学生のように丸暗記しても、実際の現場では何の役にも立たないのだから。