デスマーチの実例の一つとしてYRP 軍曹が携帯電話開発の現状を語るが有名です。
この状況を今の開発スタイルだったら、どれだけ脱却できるのかどうかを考えてみました。2004年頃にはまだなかったツールや普及していなかった考え方があることに気づきました。それらのツールや考え方を利用すると、ツールがだいぶ便利になってきているので、だいぶ改善されます。
しかし、マネージメント自体による部分が大きいことが分かりました。
事例 「まず、仕様書が無い。」
今でも: 十分に起こることです。
-
仕様書なしのコーディング
- 仕様書を書かずにいきなりコーディングを始めてしまう。どの時点でどのようにして設計方針を組織として承認したのかが不明確なままになっている。
-
サーバーが失われる
- ドキュメントがおかれていたサーバーが運用停止になって、ドキュメントにたどれなくなることこともあります。せっかく、ソースコード上にドキュメントの所在の場所を書いておいたのに、おき場所が変えられえてしまい見つけられなくなることもあります。
-
同じようなファイルが多数ある罠
- 今もあります。
- 対処方法:バージョン管理システムを使うこと。バージョン管理システムで差分表示をしやすいデータ形式を利用すること。
-
SharePointの罠
- SharePointというネットワーク上のドライブにあるファイルのリンクをメールやドキュメントに書いてあるが、SharePoint上の位置を変えられてしまった時点で、ファイルの名前さえわからないので、ファイルの検索することできない。
- 対処方法:必ずファイル名を書いておく。そうすればファイル名で検索できる。
-
メディアが読めない罠
- メディアがいつの間にか壊れていた(FD, CD-R、外付けHDDなど)。メディアを再生する機械がもうない(FD, MO, PD, tapeドライブ)。
- 対処方法:定期的にメディアを新しいメディアに置き換えること。
-
ソフトウェアの古いデータ形式が読めない罠
- 古い版のMS-Wordの場合。古い版の「一太郎」
-
最新の仕様書が、現状のシステムで動いているのよりも古いという罠
- 日常的に起こっていることです。
- 対処方法:ソースコードとドキュメントとをバージョン管理すること。ソースコードのドキュメンテーションコメントには、参照した仕様書のバージョンを書いておくこと。最新の仕様書に記載していない追加・変更の仕様であれば、その旨をドキュメンテーションコメントで書くこと。、
-
上流工程で出されるべき要件定義が存在しないという組織運営の欠陥がある場合という罠
- 開発しようとするものに対して、個別のモジュールの開発の仕様書があるべき以前に、ぞの製品自体についての開発フェーズに応じた「仮制定したシステム開発仕様書(案)などを元に、システム、サブシステム、コンポーネントの基本設計を行い、システム、サブシステム、コンポーネントの仕様書(初版)」などを行なわなければなりません。アジャイルな開発をする場合であっても、これらの要件定義がされなくてはなりません。上流工程で作成されるべき要件定義がなされないまま、末端の技術者をせかしている組織の場合だと、組織運営に問題を抱えています。不幸なことに、そのような末端から上流を変えることは不可能です。今あなたが、軍曹の状況 に放り込まれたとして、出来ることにはおのずと限界があるということです。
- 対処方法:そのようなプロジェクトには関わらない。別にソフトウェアを書いて仕事をするのはよそだってできる。
事例 「仕方ないので、スパゲッティ・コードを解読してノートに機能を図示しながら理解を進めていくが、変数名もval0, val1, val2のように取って付けたようなものが大半を占め、何をする機能なのかさっぱり分からなかった。」
2004年頃の状況: Doxygenは利用可能。
Wikipedia の英語版によれば
Doxygen の Initial release 26 October 1997; 19 years ago
なので、2004年時点で利用可能だったことが分かります。
今なら:Doxygenを使ってcall treeの図を生成する。
- call treeを見ることで、最上位の関数を早く見つけることができます。
- リファクタリングの重要性が以前よりは知られています。変数名や関数名の重要さについては、多くの本で指摘されています。
- ソースコードのドキュメンテーションコメントも古くなって嘘をつくようになります。コードの関数名・変数名が、それ自体が説明になっているような適切な名前をつけるようにすることです。「リーダブルコード」
今でも: 人によっては、このような変数をつい使ってしまう。
邪悪なスパゲッティコードは、今も存在しています。コードの読みやすさを考慮しないメンバーがいれば、今も邪悪なスパゲッティコードが生み出され続けます。
対処方法:これらの記事が役に立つことを期待します。
事例 「俺たちはこの時にスパゲッティを廃棄する決断をすべきだったのかもしれない。それを上位の担当者にきつく禁止されていたとはいえ、その決断を見送ったのが俺たちの地獄の始まりだった。」
今でも: このようなことは起こる。
今なら:外部仕様だけを守ろう
- 外部仕様と内部仕様とをまず区別する。
- 例:数値データをソートすることを考えてみます。数値が大きくなる順番に数値データをソートするということと、そのデータの入力と出力のしかたが外部仕様です。ソートに対してバブルソートとか、クイックソートとか使用するアルゴリズムは選べます。その部分が内部仕様です。
- 公開するのは外部仕様だけ
- 内部仕様は公開しないようにします。無名namespaceに押し込むとか、privateのメソッドやデータメンバーにします。そうすると、その内部仕様に対してアクセスされることがないことが確実になるので、内部仕様はこちらの自由となります。
- 次に外部仕様からテスト仕様を導出する。
- 外部仕様が十分に明確に記述されているならば、それからテスト仕様を導き出すことができます。もしテスト仕様をまったく思いつくことができないのだとしたら、その外部仕様、あるいはその機能の要件定義が十分ではないことを意味します。
- テスト仕様を満たしていることを単体テストする。
- 単体テストを書きます。
- その単体テストをパスするようなコードを別に作る。
- 外部仕様だけ同じ、別バージョンのモジュールを作成します。それで単体テストを満たしていることを確認します。
- 外部仕様はそのまま残していて、実装のコードを差し替える。
- 外部仕様は同じなので、#ifdef #endif やif(条件){前の版のモジュール}else{書き換えた版のモジュール}で使用する側のコードで切り替えながらテストします。
- テスト仕様を満たしていることを根拠に、古いスパゲティコードを捨てる。
- このようにして、書き換えた版でうまくいくことを確認してから、古いモジュールを削除します。
- このようなアプローチをすると「橋を燃やすな原則」を守りつつ、デグレを起こすことなく新しいコードに書き換えることができます。
事例 「噂を立ち聞きすると、山のようなバグレポートの事務的処理に追われているようだった。しかし、そのバグは俺たちの前任部隊の頃のもので、あれから仕様の大幅変更が入っているはずだった。しかし、当時のバグレポートの事務処理を終えてからでないと俺たちの版数からのバグには対応できないとの事だった。」
今でも: 意味のあることとないことの区別がつかない人がマネージャーだと起こる。
このような区別がつくように学んでいくタイプのマネジャーもいるし、学んでいってくれないマネジャーもいる。学んでいってくれないマネジャーの場合には、どうやって品質を作りこんでいくかにまで考えることが少なく、バグも対処療法だけになりがちだ。
-
変更になっていない部分のパスしたテストの一覧だけ確認する。
仕様の変更部分と変更になっていない部分を区別して、変更になっていない部分で、パスしているテスト項目だけ拾い上げる。どの部分だけは、もっともそうな状況なのかを理解するのに利用して、それ以上の部分には、労力をかけない。 -
他は無視する。
- テスト項目の一覧だけ確認して、結果は無視する。
-
追記:コンパイラエラーは最初の1つに集中しよう。
コンパイラのエラーメッセージの2番目以降は、最初のエラーによって引き起こされたエラーであることが多いものです。ですから最初のエラーを直したら、すぐにビルドをし直すことです。それと同じように、たくさんのバグレポートは、1つの大きなバグを修正したら、その他の状況はそれに影響を受けている可能性が無視できません。
バグレポートを上げてきたテストを自動化するようにしておきます。そうすれば、先にあがっていたバグレポートは無駄になりません。
本質的なバグの部分を直していき、自動化した単体テストを実行することです。
教条主義に落ちいっては、問題を効果的に解決することができません。
事例 「変数に至っては、グローバル変数のあまりの多さに、その規模を掴むので精一杯だった。」
今なら: C++でnamespace を使って変数のスコープを狭める。
この場合もそのように、変えていいことをしかるべきプロセスを経て、決めてもらえるかどうかどいう課題が残る。
今なら(追記): クラスの抽出をする。
そのグローバル変数を利用する複数の関数があるはずだ。その中で、意味のまとまりを抽出しよう。あるデータ構造を内部的に保持している何かがあり、それが何かをする仕組みがあるはずだ。
そのようなパターンはデザインパターンのどれかである可能性が高い。
事例 「スパゲッティの中には多くのグローバル変数が操作されている。他のチームも度重なる仕様変更の末に、当初は整然としていたソースが俺たちの請けた物と同じようにスパゲッティ化しているという話だった。」
今なら:
- C++でnamespace を使って変数のスコープを狭める。
- 「野放しのグローバル変数」からクラスを見つけ出す
事例 「駄目だ。今あるものを最小限の変更で動くようにするのだ。」
今でも: このように主張する人はいます。
その関数のインタフェースだけ十分明確になっていれば、
テストを行いながら置き換えるのが昔よりはやりやすくなってきています。
このように「最小限の変更」に執着するひとは、設計を理解していない人と、
その関数の利用されている上層との関係を理解して適切に言っている人との
両方がいます。
影響の範囲を減らすには無名namespaceを使って書くようにして、
自分が独立に責任をもって担当できる範囲を確保します。
それでも、そのような書き換えをして大丈夫かは、上位層がどのように実装されているかによりますから
コード全体が見えない末端の現場では、そのような判断をさせてもらえるかが微妙なところです。
対処方法:
明確な仕様の確認できた部分から、APIのインターフェースを極力変えずに、小刻みにリファクタリングを行なう。
(1回1回の変更は最小限の変更です。)
モジュールのコードのうち、外部に公開する必要のあるものを限定します。
ソースコードを分析し、期待したようには動いていない理由を分析していきます。
期待したように動いている部分とそうでない部分とを明らかにしていきます。
そうでない部分の仕様をできるだけ明確にして、その部分へのテストやassert()を追加していき、動作しない理由を分析していきます。
問題の修正点はそれだけでコミットします。
事例 「基本クラス内で使用する殆どのメンバ関数の中で、それを参照するように仕様が変わっていた。参照だけでなく、更新もするのだ。」
今なら: 引数にconst 属性をつける。ポインタは使わずに参照を使う。
const属性をつけることで、意図しない書き換えを防止することができる。そうすることでトラブルを予防できることを組織の同僚や上司に知らせていきましょう。組織のコーディングスタイルとして採用されれば、その分だけ余計な心配をしなくて済むようになります。
事例 「土日はすべてスケジュール回復対策会議のようなものに駆り出されて、膠着状態の会議に何時間もの間、発言権も無くただ同席させられた。」
2004年頃: git, Redmine ともにまだ初版がリリースされていませんでした。
Wikipedia git 初版 2005年12月21日(11年前)
Wikipedia Redmine初版 2006年6月25日
今なら: 会議よりRedmineやslackやgitを使う。
Redmineでチケットで管理して、会議の必要性を減らす。不具合についての十分に明確な情報をRedmine上で共有するから、そのバグが起きた状況、バグが入る前後の変化点の状況、実行結果のレポートが知らされることで、何をどう解決すればよいのかが見えてくる。具体的であって、しかるべき深さで物事を考える仕組みがあれば、考えるのが1人でも解決にたどり着く。会議を招集しても、具体性のないバグの報告で、物事が表層的であれば、10人いても正解にたどりつくことはできない。
今でも: 会議をすることが仕事だと思っている人・組織は多く、今日も結論の出ない会議を繰り返す。
対処方法: Redmineやgitのチケットで、自分の担当の部分でよい事例を示していきましょう。声が大きいだけの人、具体性に欠ける話しかしない人はそれらのチケットに書き込むことはまずありません。少しずつ世の中は変わっていくはずです。
根本原因: 実務の出来ない人、その分野で勘が働かない人を、マネジャーにしていることが多い。いわばカンナの使い方を知らない人を大工の棟梁の立場で仕事をさせている状況が、ソフトウェアの開発の現場には多いということが、ソフトウェア開発の現場で起きている問題の原因の多くを占めるように思います。
事例 「何が基準、いや、何が本当の仕様なのか?それすらも確認する手立てが無くなった。担当チーム毎に主張する「仕様」が異なり、それを調整する部門も機能していない。現場は混乱、というより混乱の内容すら正確に把握できている者がいなかった。少なくとも、俺たちがコミュニケーションを許されている範囲では。」
2004年頃:まだmarkdownは普及していない。
今なら: 仕様書もバージョン管理システム上においておき、ネットワークを通じて最新版をダウンロードできるようにする。
しかも、仕様書をmarkdown言語で書くようにして、変更点を見つけやすくする。
今でも: 要件定義が軽視されている状況では、チームの開発での承認された要件定義ができないままになってしまう。実現が怪しく、テスト方法も想像がつかないような曖昧な要件定義を、それでよしとしまっている。実現を要求される側は、実装可能でテスト可能な要件定義にしようとする。しかし、それじゃ十分じゃないと、両者の見解は食い違ったまま、時間は過ぎていく。
対処方法:適切な要件定義がなされていない。それはデスマーチの事例の大半に共通の特徴であるようです。
- その分野での用語の定義を明確にする。
- コンピュータの普通の意味の用語と矛盾する変な用語の使い方を要件定義の中でしないこと。
- 範囲という表現を使ったときに上限は含まれるのか、含まれないのか? 標準的なやり方と変数名の使い方にしたがうこと。
- 要件定義の中で決め切れていない部分はTBD(To be determined)として明記すること。
- 誤解しやすい表現を排除すること
- 表現のあいまいさを排除すること
- 同じ意味を指し示すためには同じ表現を使うこと
対処方法:基本設計仕様書が十分に実装可能なレベルになっていることを要求する
- 基本設計の時点で、ものごとの依存性を明確に切り分けておくことが必須です。
- それができていないかぎり、末端の実装でどうにかできる問題ではありません。
事例 「俺たちが派遣されてきた当初のソースを、全てコメントアウトされた形でソース内に復元させろ、とのお達しだった。」
今なら: git, SVNなどのバージョン管理システムを利用する.
このような無茶な状況は、Linux, Windows、MacOS上で開発している分には発生しないと思われる。
対処方法:SVNはローカルなWindowsでもLinux上でも利用可能です。インストーラーをダウンロードして、メディアからインストールすることで、ネットワークが無縁なマシンでもバージョン管理をしましょう。コメントアウトされたコードは、残っているだけで理解を妨げます。
ただし、COBOLの世界は除く。
事例 「机にある仕様書の版数は、俺たちのよりもメジャーで1つ先を進んでいるみたいだし、それは見なかった事にした。」
今なら: 仕様書もバージョン管理システム上においておき、ネットワークを通じて最新版をダウンロードできるようにする。
しかも、仕様書をmarkdown言語で書くようにして、変更点を見つけやすくする。
事例 「テスト要員を一気に5倍に増やして、人海戦術によるデバッグが賢明だとトップが判断した。」
2004年頃:まだテスト駆動開発という考えは普及しきっていない。
ケント・ベック 『テスト駆動開発入門』 長瀬嘉秀監訳、(株)テクノロジックアート訳、ピアソン・エデュケーション、2003年
今なら:テスト駆動開発
- GoogleTestなどの自動化テストを実行する。
適切な仕様書があって、テストすべき項目が明確化されていることが大前提となります。
今でも: 問題の本質を突き詰めて、トラブルを予防するマネジメントを心がけないと、このような対策がとられます。
その結果は、やはりデスマーチです。
対処方法:
- 「テスト駆動開発による組み込みプログラミング ――C言語とオブジェクト指向で学ぶアジャイルな設計」をチームのメンバーに読んでもらうことです。
- 単一責務の原理(Single Responsibility Principle)を守るとバグに苦しむことが減る
- テストを有効に行なうには、コードの開発時点ごとに意味のあるテストの内容が変わるということを理解しよう。
- 例:じゃんけんをするプログラム
- グー、チョキ、パーのどれかを表示する。
- 回数を繰り返すと、グー、チョキ、パーの全てが表示されること
- グー、チョッキ、パーのどれが出るかを簡単には予測がつかないこと
- 相手が出した今までの傾向から、自分が勝てる(あるいは逆に勝ちをゆずる)手を選択していること。
- 例:じゃんけんをするプログラム
このようなテストが考えられますが、下側のテストがだんだん難しいテストになっていき、プログラムの開発の初期には、テストすることに意味をなさないテストになっています。開発の初期から意味のあるテストと開発の後半にならないと意味のなさないテストとを区別して、テストによって何をどう開発にフィードバックするのかを考えて、テスト計画を立案しなければなりません。
- 数名のテスト要員を半年以上作業させておきながら、意味のあるフィードバックを開発の現場に返せていないテスト立案・実行になってしまっているとしたら。 罪悪感を感じてしまいます。
事例 「俺たちは現場に半分以上の時間入り浸るので、移動する時に頼まれ事を引き受けたりするのだが、それは必ず口頭であった。何故なら、問題発生時に責任の所在を示す証拠を残してはならないからである。」
今でも: Redmineを大多数のメンバーが使っていても、使わない人はいますね。
##最後に
デスマーチの状況を脱却するには、マネジメントを変えてもらう必要があります。マネージャーに気づいてもらうためには、「こういう事例がありますよ。」、「こうするといいらしいですよ。」と言って、マネージャーにアドバイスをできるといいなと思います。
そのような目的に、この記事が役に立つことがあれば、とてもうれしい。
ガラケーはもう新規種のなくなって相当たちます。日本市場でシェアの大半を占めていた2つのメーカーはともに携帯電話から撤退しています。自分たちの開発の仕方の課題を自力で解決できなかったツケがそのように形になったのではないかと思います。
2010 年の記事 [携帯電話ソフトウェア開発の取組み] (http://www.fujitsu.com/downloads/JP/archive/imgjp/jmag/vol61-2/paper12.pdf)
富士通のこの資料では図1ソフトウェア規模で最新のFOMA携帯電話 が「証券業システム」、「都銀基幹系システム」よりもソフトウェア規模が大きいとして書かれている。
2009 年の記事 携帯電話などのソフトウェア開発にかかるコストが飛躍的に増大
2009 年の記事 メーカー支援のため、100億円規模の開発費負担を発表──NTTドコモ 山田社長
2007 年の記事 携帯電話の組込みソフトウェアは、この2~3年の間に5倍の規模になり、しかもその規模は500万行に達しています
2005 年の記事 ソフト基盤の統合図るドコモ 携帯電話の開発費抑制狙う
「共通化できるソフトウエアを端末メーカーがそれぞれ開発していたら,端末の納入価格が許容できなくなってしまう」
一昔前の携帯電話開発がブラック職場でデスマーチ率が高かった理由
当時は共通のAPIを作って売るとかそういう発想はなく、とにかく各社横並びで独自のOSやファームウェアを開発してたっぽい。
スマホ隆盛、出遅れ日本勢-ガラケーの失敗糧にできるか 総務省乙www
これらの記事を読むと私にはこう思えます。
- どうやったら品質の確保しやすい開発ができるか、日本企業は考え出すことができなかった(あるいは、実現できなかった。)。
- 組み込みJavaを使う開発で、ミドルウェアを標準化させることで開発を楽にすることを思いつかなかった。
- 携帯電話サービスのプロバイダの枠を超えていること、言語の枠を超えていること、携帯電話端末メーカーの枠を超えていること。
- これらを満たすものものとして、GoogleのAndroidがその役割を担うことになった。
- テスト駆動開発に独力でたどり着くことがなかった。
- Git, SVN, Redmineなどのツール、あるいは同等のものを作り出すことがなかった。
これらが、過去の問題ではなく、今我々の開発スタイルが本当にいいものなのか、工夫が不足しているのではないかという疑念を持ちます。