スポンサーサイト 

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ドキュメントの役割 

ソフトウェア開発において断定しにくい問題としてドキュメントをどうするかということがあります。ドキュメントに関してはおおよそソフトウェア開発を仕事としている人すべてが何らかの形で考えざるを得ない問題です。


日経ソフトウェアに「俊敏な開発のためのプログラミング 悪徳の栄え」というYugui(園田裕貴)さんが書かれているコラムがあります。普段、断定しにくいことに関して一旦断定することによって問題をはっきり見えるようにしようという趣旨のコラムだと個人的には解釈しています。第5回のコラムには以下のように書かれていました。



プログラムの仕様書はテスト・コードとして書くべきです。そうすればより精密に記述できますし、いつでも自動化されたテストによって検証可能になるからです。仕様書を自然言語で書いたとしても、それは「自動実行できないテスト・コード」かそれ以下のものでしかありません。逆にテスト・コードは、その期待に応えられる程度に厳密であるべきなのです。


通常、ソフトウェア開発時に発生する要件には機能要件と非機能要件があります。上記の文で言われているのは機能要件の範疇であることが前提となっています。ただし、任意のソフトウェア開発プロジェクトを対象とした場合、前提条件としてはまだ不十分です。機能要件の中にもテストコードとして表現できるものと出来ないものがあります。特にハードウェアに近いソフトウェア開発においてはその割合は増加します。「指定のポートから1kHzの方形波を出力すること」というごく簡単な機能要件ですらテストコードが書けない場合は普通に経験することができます。


また、以下のようなことも書かれていました。



ソフトウェア開発において真に必要とされる成果物はただ一つ。ソフトウェアそのものです。他の事に時間を費やすのは悪です。


ここでいう「ソフトウェアそのもの」というのが正しく動作するソフトウェアであるということは自明でしょう。そして正しく動作するソフトウェアというのはテストコード等によって検証されたソフトウェアということになります。厳密に表現されたテストコードが自然言語で書かれたドキュメントよりも有効であることは私も全く同意です。しかし、テストコードで書けない機能要件の割合が増えるほどソフトウェアが正しく動作することを検証するためには必要なドキュメントは増えてきます。


ソフトウェア開発におけるドキュメントはソフトウェアが正しく動作することを検証できるために書かれるものと、ソフトウェアがどのようにな構造になっているかを他の人間に理解してもらうためのものに少なくとも役割レベルで分けて扱われるべきです。テストコードもドキュメントも手間という面から見れば同様に必要悪です。関わるプロジェクトにおいてトータルの手間をどれだけ減らせるかという問題意識を持つことが重要だと思います。


 


スポンサーサイト

開発コストに対するの客観性 

Radium Software 「正しさのコストとリスク」より引用。

 

なにかを実現するための工学があったとして,そこから得られる利益が,それに要されるコストを下回っていたとしたら,それは工学として成立しない。

 

全く持ってその通りだと思います。工学そのものを学び研究する人も工学を元に実践する人も、今まで出来なかったことが出来るようになる、あるいは今までより効果的に出来るようになることによって何らかの利益がでることを前提としています。

 

商品の開発中であれば時間というコストがかかります。品質についても一定の時間内で最高を目指すという意味で時間のコストに含めてもよいでしょう。このコストを何とかしようとCMMIやRUP、アジャイルなどを導入するわけですが、必ずしもうまくいくわけではありません。

 

アカデミックな計量手法や有名な開発手法のような大きな話だけでなく、単にバージョン管理一つとっても、本当にそのコストメリットが意識されているでしょうか?バックアップ要素なのかパラレルなタスク運用における協調作業の形式化なのか、開発プロセスツールとの親和性なのか、他にも理由はあると思います。現在であればとりあえずSubversionを選択しておけばほぼまちがいないでしょうが、2年以上前には私の職場ではSubversionを知っている人はあまりいませんでした。そのときは仕様変更に伴うビルド環境構造の変更容易性を主に次のプロジェクトからはSubversionで行きましょうと説得しました。

 

バージョン管理だけでなく、バグトラッキングシステムやドキュメントライティングについても同じことが言えるでしょう。バグトラッキングシステムへの登録が単なる儀式人なっていないか、誰にも読まれないドキュメントを書いていないかなど、考えるポイントは身近にもあるはずです。

 

後追いの勝率が減ってきている現在、いろいろなものが新規導入されてきています。導入されるものがうまくいくかどうかは強制力ではなく、理解を得られる言葉だと思います。

名前付けと図解 

@ITで「ITエンジニアにも必要な国語力 第1回 名前にとことんこだわるべし」という記事を読みました。この記事では図解力を磨くためには読解力、つまり国語力が必要であり、国語力を鍛えるためにまず名前をつけることにこだわってみましょうというものです。私も普段、説明資料を作成するときに図解や名前には苦労している人間です。そこで名前と図解について私なりに考えてみました。

図解に使用される図には図を構成する要素とその関係が描かれています。そしてその要素にはそれぞれ名前そのものか短い形容がついた名前が付いています。図解しようとしている人は要素の名前を提示することで読み解こうとしている人に意味が通じると考えているわけです。それを前提としてそれらの要素には実はこのような関係があるのですよ、と図解することになります。

図解が必要となるケースとして、

  • 世の中には何々というものがあってそれはこれらの要素から成り立っていてその要素にはこのような関係がある
  • 名前は付いていないけれども世の中にはこういう要素があり、それらにはこのような関係がある

の2つがあると思います。どちらのケースにおいても図解が文章より有利である理由としては、Aに~するとBになり、さらにBに~するとCになりますというような要素の関係に直列的な順番が存在するような場合には文章でも十分に役割を果たすのでしょうが、条件分岐などが入ってきて、Aに~するとBになるが、Aに~するとB'になり、Bに~するとCになり、B'に~するとC'になりC'に~するとBになる、あたりになってくると図解した方が明らかに理解しやすくなります。たとえ図解されていない場合でも理解しようとする側で図を描くのではないでしょうか。

各要素にこのような関係があることに名前を付けることは、そのような関係を持つ要素の集まり自体を一つの要素としてさらに大きな集まりの関係を考える際にとても有用です。プログラマであればまさに関数に名前をつけることであり、ソフトウェア設計者であれば状態遷移図の状態に名前をつけることです。名前は他との識別の役割と同時に意味の代弁となります。スターゲイトでサマンサカーターがジャネットフレイザー軍医の弔辞を読んだシーンを思い出しました。

引き継いだソースコードをヤバイと思う瞬間 

「一生あなたがそのプログラムをメンテナンスするならいいけどね」

 

企業でプログラムを書いていれば一度は聞いたことがあるかと思います。 他人が書いたプログラムを引き継ぐということはごく普通にあります。RTOSなどが使われているそれなりの規模のプログラムを引き継ぐとき、 今までよくこれで問題がでないかったなぁと思うようなソースコードを渡されることがあります。 プロダクトによって見るべきポイントはいろいろあると思いますが、引継ぎ時に私が最低チェックを入れるポイントを挙げてみました。

 

まず、基本中の基本ですが組み込みソフトウェアの場合、汎用ポートを制御することが多く見られます。 システムが(起動時など特定の場合を除いて)通常稼動している際の処理で汎用ポートレジスタを特定のビットを1にしたり0にしたりするような場合に何のためらいもなく

*PORT_ADDRESS |= PORT_BIT;

のような記述をされているときです。他のビットを別のタスクや割り込みハンドラ内で制御している場合に非常に危険です。 仮に今扱っているハードウェアでは大丈夫でも、次の(試作も含めて)ハードウェアでピンの割り当てが変わらない保障はどこにもありません。 ちゃんと排他処理を行うべきです。

 

次にヤバイと思うパターンは割り込みハンドラ内でのグローバル変数へのライトアクセスです。以下はあまり現実的な例ではありませんが、 プログラムがふっとぶ可能性があります。

#define TABLE_MAX 10
int counter = 0;

void handler(void) { counter++ }

void func(int *table)
{
    if (counter < TABLE_MAX) {
        table[counter]++;
    }
    else {
        counter = 0;
    }
}

この例では排他処理やローカル変数を使うことで回避できますし、そのように記述されることも多いと思います。ただ、多くの場合、 OSなどのサービスを除くとメインループと割り込みハンドラという関係において割り込みハンドラ内でライトアクセスを行わなくてもプログラムは記述できることを意識することが重要です。 もちろんやむを得ない理由や最適化の結果としてライトアクセスを行うケースがあるのは認識していますが、 そのような場合は必ずソースコードにコメントをつけるべきです。 何のコメントもなくライトアクセスしているようなコードは危険性を疑ってみるべきです。

 

典型的なケースで一番困るのが状態管理用変数の継ぎ足しや流用でしょう。 開発が進むにしたがって当初考えていたよりも状態の定義が不十分であることが明確になってきます。 不十分であることがが発覚するたびに状態管理用変数の追加や流用が行われるわけです。 条件文のネストの中に状態管理用変数が再登場したり登場の順番が入れ替わっていたり、 3つ以上の変数を使用しているにもかかわらずなんのドキュメントも無い様な場合、やはりヤバイといえるでしょう。 高い確率でその変数はグローバル変数になっているはずです。

 

逆に安心感のあるコードのパターンとしては、 割り込みハンドラの中身を所有しているモジュールにおいてその割り込みの制御をモジュールの使用者にまかせずにモジュール内に隠蔽しているようなソースコードです。 特にそのモジュールがタスクも所有している場合、割り込み制御の隠蔽はそれなりに考えていなければできないものです。

 

油断しているとすぐにソースコードはヤバイものになります。 自分なりのべからず集を頭の中からアウトプットしておくことは自戒の意味においても大切ではないでしょうか。

 

ソフトウェア開発における再利用物 

最近、ソフトウェア開発における再利用性について同僚と話をすることがありました。その際、 どうにも現在の私の考えが自分でもうさんくさいと思えたのでもう一度自まとめなおしたほうがいいかなと思っています

 

再利用すべき対象だと考えているのは、ソースコードそのもの、モジュール間インターフェース、モジュール間関係の3つです。

 

ソースコードそのものはコンパイルされているかどうかに関わらず、 現実に利用される場合はいわゆるライブラリという形でサービス提供者から利用者に仕様と作法が提供されます。 モジュール間インターフェースは枠組み提供者から利用者とサービス提供者に仕様と作法が提供されます。 典型的な例としてはOSが提供するデバイスドライバの利用や作成のための仕様でしょう。モジュール間関係はいわゆるデザインパターンです。

 

ライブラリはそれが動作する環境に強く依存、あるいは環境自体を規定します。 インターフェースは例えばOSが提供しているインターフェースであればOSさえ動作すればハードウェアには依存しません。 デザインパターンはOSにさえ依存しません。 適用範囲という意味においてはライブラリ<インターフェース<デザインパターンという関係が考えられます。

 


これに対して実在する再利用物の数の関係はライブラリ数>インターフェース数>デザインパターン数になるでしょう。 抽象的な表現になりますが、 ソフトウェアという世界を満たすためには適用範囲の狭いライブラリは数多く必要となり適用範囲の広いデザインパターンは数が少なくてすむということです

 

多くの部署を持つ企業で全社的な再利用プロジェクトが進められることがありますが、 その目的が表す適用範囲の広さに関わらずライブラリを作ろうとします。 何もGoFパターンのようにソフトウェア全体に影響するパターンを作る必要はないでしょうが、その企業のプロダクトで一般的に必要な機能、 ユーザーインターフェースなり、ソフトウェアアップデートなり、 メカ制御など一段具体的な範囲に対してに対して有効なパターン開発に向かうべきではないでしょうか。 そして現場でそのパターンが提供する有効性を持ったライブラリを作っていくのがよいように思えます。

 

最近の私の嗜好:Makefileのターゲット 

eclipseなどのIDEも素晴らしいと思うのですが、makeもまだまだ健在です。 makeの中でも一番多く使われているのはおそらくgnu makeでしょう。ただやはりMakefileの記述は簡単なものではなく、 build環境が大きくなってくると特にgnu makeのように機能が多いmakeではMakefileの記述に嗜好が表れてきます。 以下は最近の私のTop DirectoryにおけるMakefileの私の嗜好です。

サブディレクトリを再帰的にmakeを起動していくやり方は多くの場面で見られます。大体こんな感じでしょうか。


SUB_DIRS = SubDir1 SubDir2 ReleaseDir

all:
    @for subdir in $(SUB_DIRS) ; do \
        (cd $$subdir && $(MAKE) $@) ;\
    done


私はこれに少し手を加えて


SUB_DIRS = SubDir1 SubDir2 ReleaseDir

%.subdirs:
    @for subdir in $(SUB_DIRS) ; do \
        (cd $$subdir && $(MAKE) $(basename $@)) ;\
    done


としています。こうしておくとtargetがclean、dependなどと増えていっても


clean: clean.subdirs
depend: depend.subdirs


と2行加えるだけで済みます。

その他、私の今までの体験では特定のディレクトリだけmakeをかけたいというケースがありました。 そのようなケースに対応するために以下のようなターゲットも用意するようになりました。


%.all:
    @cd $(basename $@) && $(MAKE) all


これですと、make SubDir1.allというようにmakeを実行すればSubDir1にだけmakeが実行されます。 SubDir1/SubDir1_1にmakeをかけたければ make SubDir1/SubDir1_1.allで行えます。 もちろんSubDir1/SubDir1_1にMakefileがなければいけませんが。

 


最近の私の嗜好:ラベルの付け方 

以前はCでラベルをつけるときに全部小文字+アンダースコアで書いていたのですが、最近はCamelCase+アンダースコアになっています。

 

例えばややこしい初期化が必要なデバイスを扱う際にdevice_init_phase_1stなんてラベルをつけると、 これは関数名なのかenumか何かで表された値なのかがわかりにくくなります。 そのため関数であればDevice_InitPhase1st()、 値であればDeviceInitPhase_1stのような書き方をするようになりました。 アンダースコアより前をnamespaceっぽく使おうというわけです。 namespaceを構成するときのポリシーというのをあまり見かけないのですが、何か基本があるのでしょうかね。

 

まあ、こんなことをやるのならC++をベターなCとして使いたいのですが、仕事となるとそうもいかないですからね。

 

 


バグの優先順位が致命的とかでいいのだろうか? 

多くのバグトラッキングシステムにはそれぞれのバグに対して 優先順位がつけられるようになっています。致命的とかshowstopperとか criticalなどのような順位を見たことがある人も多いと思います。 バグの優先順位をつけるときに迷われたことはありませんか?私はあります。

 

そもそも優先順位というのは何の優先順位なのでしょう。ハングアップして しまうものを1、ハングアップはしないものの、プロダクトとして致命的なものを 2というような順位をつけた場合、「みられまくっちゃ」と入力するとハングアップ する不具合と30秒で通話が途切れてしまう不具合があればどちらを 優先して対応すべきでしょうか?30秒で切れたらかけ直せばいいだけだから ハングアップを先に直そうという判断をするプロジェクトは無いと私は信じています。

 

プロジェクト終了後、今回はたくさんハングアップがでたな、と眺めるための ものだと割り切ってしまえばそれでおしまいですが、プロジェクトの運営に なんらかの影響を与えるものだとすればこのような優先順位のつけ方は あまり有効でないように思えます。

 

通常のプロジェクトではバグが発見された場合に最初に判断されるべきは 修正されるべきか、そうでないかです。別の言い方をすれば 修正する許可が得られる(Do)か保留される(Pendingかです。極論すれば優先順位は この2つでよいと言えるでしょう。これ以上の細分化はプロジェクトの 運営形態などを考慮して行われるべきです。

 

比較的一般的な細分化の例としては修正許可は得られていて 期日までに対応しなければならないものとそうでないもの という分け方があります。この場合だとHigh,Low,Pending という優先順位でしょうか。このような優先順位付けであれば プロジェクト運営においてバグ管理データベースから得られる情報 がどのように活用されるべきかは明確になります。重要なのは 優先順位がきちんとMECEになっていることだと思います。


Tracにclosedが無いことを前向きに考えてみる 

TracというIssueTrackingSystemがあります。 導入が容易でSubversionと連携しWikiも使えるということであちこちで使われているのを見ます。 しかし不思議なことに多くのバグトラッキングシステムがopen,assigned,resolved,closedというようなステータスを持っているにもかかわらずTracにはclosedがありません。 (本家のリポジトリのブランチには暫定的にはあるようですが)

 

Tracはデータベースにsqliteを採用していることからも少人数での開発に使われるであろうことが考えられます。 特に組み込み系の開発の場合大企業においても数人で開発しているところも珍しくありません。 closedというのはコードの修正が終わった後、確認作業を行い、正しい動作が確認された状態のことですが、 大企業などでは評価部隊はすでに独自の不具合データベースシステムなどが稼動していることが多く、 開発チームで独自に導入したシステムに対して協力的である可能性は高くないでしょう。また、 ハードウェアを含めて正しく動作することが求められるため、フィールドテストならこちらの部署で、温度系のテストであればこちらの部署で、 UI系のテストはこちらでと評価部隊が複数に分かれていることもあります。そのような場合、 確認作業を行った人がステータスをresolvedからclosedにするという運用ルールを決めたとしてもうまくいくとは思えません。

 

ならばTracでの管理はresolvedまでと割り切ってしまい、 どうしても確認作業が完了していることの管理を行いたいのであればその分だけTypeをToDoなりVerifyなどにして新規にTicketを発行すればよいと思います。 Tracで管理すべき事項はチームの責任範囲内のみと割り切るのもひとつの手ではないでしょうか。

 


逆システム学 

逆システム学―市場と生命のしくみを解き明かす
金子 勝 児玉 龍彦
4004308755

私はもともと完全なる均一の元に安定している状態よりも多様なものが存在し、 結果として安定している状態が望ましいのではないかという考え方はどこまで通用するのだろうと思っていました。

 

ソフトウェアというものは開発に携わっている人以外の人に使われることを前提に作られる場合、入力に対して出力が予測可能であり、 実際にそうなっていることが求められます。これは均一な状態で安定していることを求められていることになります。 出力の予想が困難であろうgoogleの検索結果にしてもこれは入力されるデータの捕捉が困難なのであり、 入力を固定した場合の出力は予測可能です。

 

このように均一性をもって安定状態を求められるものを作る業界にいながら、 多様性をもって結果としての安定状態の方が望ましいと考えてる私の中に矛盾が生じていたのですがこの逆システム学を読んでその間に一つの区切りを設けることができたと考えています。

 

周りの環境に合わせて適応しなければならない仕組みを要求されるものには単一の方法や考え方、 アルゴリズムのみでは恒久的には仕組みが維持できないということがこの本には書かれています。 逆に言えば周りの環境に適応する必要がなかったり、 一時的な効果があれば良いような物にはシンプルな仕組みが通用する余地があるということにもなります

 

MicrosoftOfficeは初期の段階ではとてもシンプルな機能しかありませんでした。 もしMicrosoftがその段階でオフィス用のアプリケーションに必要な仕組みはすべて実装されていると決め付けていればおそらく現在MicrosoftOfficeは生き残っていなかったでしょう。 しかしMicrosoftOfficeシリーズ自体は周りの環境が変化しても生き残っています。 バージョンアップという再インストール作業を行うことで、 単一の仕組みしかなかったものが周りの環境に合わせて別の仕組みが取り入れられることでいわゆる進化が行われたからです。

 

ソフトウェアを作り出す組織も周りの環境に合わせて進化しなければなりません。 しかもバージョンアップという名の大規模な人の入れ替えなしで行われるのが望ましいとすれば内在的に今までの仕組み以外のものを受け入れることが必要です。 わずかの環境の違いで有効な仕組みは変わってきます。 有効な仕組みを見つけるためには成功例をそのまま導入してもわずかの違いでうまくいかないことが多々あります。 均一な結果をもたらす仕組みのうち有効なものを有効な間だけ用い、 そしてそうでなくなった場合に備えてその代わりになる仕組みも用意し自在に入れ替えられるようにする必要があるのではないでしょうか。 RUPとアジャイルは水とマグネシウムのような関係ではなく、水と油だったとしても混ぜ合わせればドレッシングにもなると思います。

 


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。