2008年5月14日水曜日

「同じ」と「違う」(3)~「同じ」から「似ている」へ

対象を事物に絞れば「同じ」モノは存在しない、と言いました。

違和感ありますよね。「同じ」という言葉は非常にリアリティを持っている。実は同じものが存在しないことを認めると、人間にとって世界の根本的な成り立ちが怪しくなるのです。

確かに、「同じ」ものが存在するような気がする。では何が「同じ」なのでしょうか。

養老猛司氏は、テープに録音された音声としてのテキストと文書に書かれたテキストが同じであるという例を上げ、「同じ」なのは情報である。と述べました。情報それ自体は媒体に関係なく「同じ」である、と。

氏の意見に同意してもよいのですが、私としてはもう少し踏みたい。
「情報」という言葉にすると「同じ」かどうかは怪しいと思うからです。理由は簡単。例えば(何でもいいのですが)夏目漱石の小説を読んだとします。私が読むのと他の人が読むのとでは当然感想が違います。確かに文字の並びは同じといってよいでしょう。しかし、同じ本を読んだとしても同じものを見ていることにはならないのです。あるいは聖書。キリスト教者が読むのとそうではない人が読むのとでは、全く受け止め方が違ってくるでしょう。

私は、同じなのは「記号の配列」であると思います。厳密に言えば。

でも情報だって同じだろう。翻訳されたら記号の配列は変るが、相対性理論は日本でもアメリカでも同じ相対性理論だ。その通りです。しかし、この議論の前に私は「同じ」という言葉をもう少し緩めて「似ている」という範囲に広げたいと思っています。

アメリカの相対性理論も日本の相対性理論も、究極的には同じだと思います。

しかし、やはり人によって理解のレベルも着眼点も解釈も異なるはずです。ですから現実的には「相対性理論は相対性理論である」というよりも、人の解釈が「似ている」から「限りなく同じ」のグラデーションのどこかに分布する、というのが妥当だと思います。私と現役の科学者が相対性理論について語り合ったって、どこにもたどり着かないでしょうから。

次に「似ている」について、もう少し踏み込んでみましょう。

(続く)

2008年5月13日火曜日

「同じ」と「違う」(2)~「同じ」は存在しない

自分は自分である
ゴキブリはゴキブリだ

以上は「AはAである」という構造を持っています。言い換えればAとAは同じだ、ということです。

では

自分は自分ではない
ゴキブリはゴキブリではない

はどうでしょう。AとAが違う、ということでこれはほとんど論理的には矛盾していますね。

以上より、A=Aは真だし、A≠Aは偽だということが一般に言えます。

(何をつまらんことを言っているんだ、という声が聞こえてきそうです。もう少々お付き合い下さい)

しかし、実はここに大きな問題があるのです。

具体的に考えて見ましょう。具体的に、というのは実際に存在する事物についてです。ここでは事物を空間的に位置を占め、重さを持つものとします。

すると、どうなるか。
目の前に缶コーヒーが二本あるとします。同じメーカーの、同じ種類の缶コーヒーです。同じ自動販売機で、午前と午後に一本ずつ購入したものです。
この二本の缶コーヒーは「同じ」なのでしょうか。

ブランドが同じ。種類が同じ。価格が同じ。同じに決まっている。

果たしてそうでしょうか。
「同じ」としている主語に注目して下さい。「同じ」としているのは缶コーヒーではありませんね。すべて缶コーヒーの言わば属性とも言うべきものです。缶コーヒーそれ自体ではない。もう少し言えばその属性も先ほど定義した「事物」ではない。

缶コーヒーそれぞれは、同じではないのです。なぜなら空間的に別の位置を占めているからです。座標が違うものは同一ではありえません。つまり、世の中に存在する事物に、同じ物は一つとして存在しないのです。

「同じ」は実体=事物的な存在ではありません。「同じ」とは価値評価に過ぎません。

言い換えれば「同じ」は人間の頭に存在する概念でしかありません。

ところがあたりを見れば「同じ」事物があふれているように見える。それは頭が周りの事物を「同じだ」「同じだ」と強制的にラベリングしているからなのです。

(続く)

東京三菱のシステム障害に思うこと

原因はカタカナで転送すべきだったデータを漢字で送っていたためだったそうです。

早速「テストが甘いかったんじゃないか」などという結果だけを見て偉そうなことを言うひとがちらほら出てきています。この手の原因と結果を転倒させる人は本当に始末に悪いですね。

確かにテストが甘かったのは事実でしょう。外部システムとの正常系テスト(記帳の回数オーバーのハンドリングはほとんど正常系です)をしていれば、事前に発見できたはずですから。しかし、部外者がそのレベルのことをしたり顔で言うのは、プロ野球の監督の采配に文句をつける酔っ払いオヤジとかわりません。

安易に時系列(原因⇒結果)を逆転させてはなりません。なぜなら、それは安直な分析につながるからです。今にしてみればあまりに自明だったことが、必ずしも現在自明であるとは限りません。むしろ原因と結果という図式を単純に信奉して「ああすればこうなる」式に(これも養老節ですな)手を打つ方が危険です。原因と結果という図式は、本来は不可能であるはずの未来の予測を、いとも容易に見せてしまうまやかしの図式でもあるからです。

送信データミスが原因で、結果として障害が発生した、という解釈は正しいです。それを逆転して「送信データミスがなければ障害は起こらなかったのに」と考えるのも、まあ妥当な感想でしょう。でも、そこから「テストが甘かった。私の成功体験では・・・」と断ずるのは早計です。

システム構築中は、取り返しのつかない意思決定の連続です。そして意思決定には常にリスクが伴う。人間ですからどこかで誤ることは十分ありえるからです。そのリスクをいかに取るかが大事なのです。

今回について言えば(結果を知っている部外者からは)明らかに必須と思えるテストの実施がなぜ漏れたのかが問題です。
外部システムとのインターフェースがある以上、結合テストはほとんど必然です。漏れようがない。それから今回の問題は正常系のテストでカバーできるはずです。異常系・障害系のテストなら時間との兼ね合いで割り切る判断もあるかもしれませんが、正常系のテストが行われなかった理由はほとんど謎です。

私には以下の要因が思い当たります(どれも当たり前のことですが・・・)。どちらも「ピーク6000人を投入した」「失敗が絶対に許されない」と言われるからこそ、気になる点です。

1.作業に優先順位が定められていたか
2.現場レベルのエンジニアに無用なプレッシャーが掛かっていなかったか

人が多くても、適切にリソースが配分されていなければ意味がありません。その人員は、何の仕事をしていたのでしょうか。システムに無理解な上層部(残念ながら昨今はリーダークラスにも多いですね)の思いつきに対応するために使われたのではないでしょうか?本当に現場に必要とされるタスクに、回されたのでしょうか。そもそも、何が重要か、適切に判断されていたのでしょうか?

それから「失敗は許されない」というスローガンは、前にも書きましたが実に百害あって一利なしなのです。このスローガンによって引き起こされる副作用には、恐らくは瑣末事大主義と優先順位付けの不能でしょう。失敗をできるだけ起こさないためには、リスクをきちんと評価して、重要な作業を優先しなければなりません。しかし「失敗は許されない」無思考に陥ると、作業の割り切りやリスクテイクが出来なくなるのです。

今回発生した障害の裏に何があったのかは知りませんが、同じエンジニアとしてはこれ以上何事も起こらないことを祈っています。

2008年5月12日月曜日

「同じ」と「違う」(1)

埴谷雄高氏の「死霊」に出てくる有名な言葉に「自同律の不快」というのがあります。私はこの言葉を、カントの「同一律」と物自体、ニーチェの力への意志、それから養老猛司の「同じ」ってやつだな、うんうん、と勝手に解釈し、それなりに納得したつもりでいました。

ある日、もう半年以上前でしょうか、日経新聞の夕刊で「死霊」に関する記事を読みました。そこでは「自同律の不快」のことを『「私は私である」と言い切るのは不快だという感覚』であると解説していました。
さすが日経、浅はかな解釈であることよ、と鼻で笑っていたのですが、Webで見る限りどうも一定のお墨付きを得た解釈のようです。むしろ埴谷雄高氏がこの言葉を使って自ら語ったことがあるような様子すら見受けられました。(気楽なエッセーなので言質を取ったりしち面倒くさいことはしませんが)

改めて読めば、確かに大きく的を外した解釈ではなさそうです。「私は私だ」という経験上自明である言明に不快感を示す、一体どういうことだ?と興味を引きますし、少なくともA=Aが不快だ、と言うよりは分かり易い。

しかし私は文字通り「A=A」が不快である、という意味で「自同律の不快」という言葉を受け止めたいと思います。

「私が私である」はもちろん(養老猛司風に言えば)「虫は虫だ」というのも自同律です。A=Aであるという形式を元に世界を構成する理性の乱暴さ。それに対抗する「自同律の不快」は普遍的な共感を得ることができると思うのです。

(続く)

Springフレームワークを使ってみた

Springフレームワークに含まれる、以下のチュートリアル文書に従ってSpringフレームワークを試してみました。
spring-framework-2.5.3/docs/MVC-step-by-step/html_single/index.html

まだ第一印象レベルですが、その感想です。

■XML地獄は健在
Strutsと同様、SpringでもXML設定ファイル地獄が存在します。
単純に比較するのも酷ですが、多機能なだけSpringの方がとっつきにくいという感想です。

XDocletを使えば少しはマシになるのでしょうか。

■DAOはイケてる?
DAOは結構イケてる気がします。
Torque(だったかな?)よりも簡単そうですね。(チュートリアルが優れているせいかも?)

■Spring's JDBC framework
神経を使う connect / close をしないで済むのが素晴らしい。
WebSphere(v5以降?)ではUserTransactionを使うのですが、JNDIを使わないだけシンプルで良いと思います。(WASで同じことをやる場合、JNDIの登録作業が発生して面倒くさいです。WASの実装に依存するところもブラックボックス的で気に入りませんし)

■総評
目的は違うのでしょうが、Strutsよりマシかどうかは分かりません。
Springで提供している機能がバッチリ要件にハマれば採用の価値はあると思います。
しかし、それにしても相応の学習負荷と開発ルールの策定負荷があります。

例によって喧伝されている技術ではありますが、難解さ/取っ付きの悪さが目立ちます。

Strutsと同様Springそれ自体は優れたアプリケーションなので、デザインを勉強するにはよいと思います。

以上。

2008年5月7日水曜日

「醜い親子」??

『トランヴェール - Train Vert』という無料雑誌があります。JR東日本の新幹線の座席ポケットに、通販雑誌と共に配られています。こぢんまりとまとまっていて広告臭も薄く、ぼーっと目を通すには悪くない雑誌です。

このGW、子供を連れて新幹線に乗る機会がありました。新幹線に乗ってしばらくして、子供たちがおもちゃやお菓子に目を奪われておとなしくなっている隙に、缶ビールの栓を空けてこの雑誌の5月号を手に取りました。何気なく読み始めたのですが、冒頭に掲載されていた内館牧子氏のエッセイにいきなりいやな気分にさせられました。

内容は、コンビニのサンドイッチとおにぎりを泣きながら拒否する未就学児と、それをなだめる親についてのエッセイです。
この未就学児は内館氏の足元に「ゴミ」などと言ってサンドイッチを投げつけ、親は彼(彼女)を叱りもせず海苔巻を与えて一緒に食べたそうです。エッセイは、日本の歴史上の少年兵士と目の前の親子を比較して親子を醜いと断罪するものでした。曰く昔の少年は屋外ではモノを食べてはならないと教えられてきた、飢えを我慢して戦いに臨んだ少年に比べ、この親子の醜さはなんだ、云々

言わんとすることは分かるのですが、一言でいえば「大人気ないなあ」という感想です。その場で「食べ物を粗末にさせてはならない」と親を注意するのならともかく、当の本人たちには何も言わずに言わば公共の場であるところの雑誌で糾弾するというのはどうも卑怯ではないかな、という気がしました。

子供を持つ親の立場からすれば、内館氏に断罪された彼らの気持ちも分からないではありません。
・不機嫌な子供は実に、実に、実に扱いづらい
・子供を怒る親を不愉快に思う人は多い(泣く子供よりも子供を真剣に怒る親の方が醜い)
 ⇒ 子供はさっさとなだめたい(内館氏のような人がいるから!)
(もう一つ言えば)
・コンビニのサンドイッチはかなりいまいちである

だから、私にとってすれば、彼らが公共の場で貶められるのは残念な気がしてなりません。一歩間違えば、つまり家族の気分や疲れ次第では、容易に起こり得る状況だからです。(ついでに言えば仮に私が断罪された親の立場だったとして、面と向かって文句を言われたら素直に謝りますし、別にしかられたことにクヨクヨしません。)

いやはや。

2008年4月28日月曜日

ドラえもん 緑の巨人なんとか

期待せずに見に行ったのですが、にもかかわらず、残念ながらかなりの駄作でした。

安直な「自然との共生万歳」的なサクサクストーリーであることは覚悟していました。しかし物語以前に展開やセリフ回し、人物描写がお話にならないくらい乱暴で脈絡がないです。ラピュタのパクリもイタい。シャレにもパロディにもなってないし、何で??と違和感のみが残る感じでした。
途中から「こりゃひどい」と思って目と耳をシャットダウンして耐えていましたが、辛かった・・・

子供も「いつまで経っても宇宙船から出られなくてつまらない」と言ったので、途中で出てきました。

映画版プリキュアの方がよほど面白かったです。

2008年4月24日木曜日

これは強力!XDoclet

非常に便利そうに見えるのですが、EJBやStrutsと共に使われるため、この人単体で何ができるのかが見えづらいXDocletです。
実際に試してみましたが、かなりマニアックですが、相当使えるツールとみました。
(そもそも目的が違うかもしれませんが、JJTreeとかjavaccとかで苦労したことがサクっとできる感じです)

かなりシンプルに試してみました。

【概要】
XDocletはAntと連携して動きます。
今回、Ant1.7、XDoclet1.23を使いました。

(1)ant buildファイルにXDocletタスクを定義します。
タスクにはejbとかStrugsとかSpring用にすでにカスタマイズされたものがあるようですが、今回はシンプルに実行するため「xdoclet.DocletTask」を使います。

antを実行すると、ソースディレクトリに含まれるJavaソースを解析してくれます。クラス名や、JavaDocコメント形式のコメントそれぞれにアクセスできるようになります。

(2)次に.xdtファイルを準備します。
特にXMLフォーマットにこだわる必要はなさそうです。CSVなどにも出力できますし、当然JavaソースをジェネるのもOK(なんと素晴らしい)
<XDtClass:forAllClasses>タグなどを使って、クラス名やコメントにアクセスします。

(3)Javaを準備します。
JavaDocコメント形式
 /**
  * @mytest.tag3
  * name="foo"
  */
で書いておくと、XDocletが解析してくれます(ただのコメントでは駄目です。)

(4)Antタスクを実行すると、xdtに解析結果を埋め込んだ形でアウトプットが作成されます。

EJBやStruts、SpringのXML定義ファイルや、Setter, Getterしか持たない単純なBeanクラスなどをジェネるのに最適!素晴らしいツールです。

【詳細】
ディレクトリ構造はこんな感じ。


まずはAnt build fileです。
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="./" default="build" name="xdoclet-test">
<!-- Properties -->
<property name="xdoclet.home" value="F:/libraries/xdoclet-1.23"/>
<property name="src.dir" value="src"/>
<property name="xdoclet.template.dir" value="xdt"/>
<property name="build.xml.dir" value="xml"/>
<property name="build.dir" value="bin"/>

<!-- Path definitions -->
<path id="xdoclet.classpath">
<fileset dir="${xdoclet.home}/lib">
<include name="*.jar"/>
</fileset>
</path>

<!-- Targets -->
<target name="compile">
<mkdir dir="${build.dir}"/>
<javac destdir="${build.dir}" source="1.5" target="1.5" debug="true"
deprecation="false" optimize="false" failonerror="true">
<src path="${src.dir}"/>
<classpath refid="xdoclet.classpath"/>
</javac>
</target>

<target name="build" depends="compile">
<taskdef classname="xdoclet.DocletTask" name="doclet"
classpathref="xdoclet.classpath" />
<doclet destdir="${src.dir}"
excludedtags="@version, @author, @todo" force="true"
verbose="false">
<fileset dir="${src.dir}">
<include name="**/*.java" />
</fileset>
<template destDir="${build.xml.dir}"
destinationFile="test.xml"
templateFile="${xdoclet.template.dir}/test_xml.xdt"
subTaskName="Generate XML file" />
<template destDir="${build.xml.dir}"
destinationFile="test.txt"
templateFile="${xdoclet.template.dir}/test_print.xdt"
subTaskName="Generate Class info" />

</doclet>
</target>
</project>

次に'*.xdt'です。二通り準備しました。
■test_print.xdt
<XDtClass:forAllClasses>==================================================
Class name :
<XDtClass:fullClassName/>
Class tag values :
<XDtClass:forAllClassTags tagName="mytest.tag1">
<XDtClass:classTagValue
tagName="mytest.tag1" paramName="value"/>
Field tag values :
<XDtField:forAllFields>
<XDtField:fieldTagValue
tagName="mytest.tag3" paramName="name"/>
</XDtField:forAllFields>
Method tag values :
<XDtMethod:forAllMethods>
<XDtMethod:ifHasMethodTag tagName="mytest.tag2">
<XDtMethod:methodTagValue
tagName="mytest.tag2" paramName="name"/>
</XDtMethod:ifHasMethodTag></XDtMethod:forAllMethods>
</XDtClass:forAllClassTags>
</XDtClass:forAllClasses>

■test_xml.xdt
<?XML version="1.0" encoding="UTF-8"?>
<XDtClass:forAllClasses>
<class name="<XDtClass:className/>">
<variables>
<XDtClass:forAllClassTags tagName="mytest.tag1">
<tag value="<XDtClass:classTagValue
tagName="mytest.tag1" paramName="value"/>"/>
</XDtClass:forAllClassTags>
</variables>
<fields>
<XDtField:forAllFields>
<tag value="<XDtField:fieldTagValue
tagName="mytest.tag3" paramName="name"/>"/>
</XDtField:forAllFields>
</fields>
<methods>
<XDtMethod:forAllMethods>
<XDtMethod:ifHasMethodTag tagName="mytest.tag2">
<tag value="<XDtMethod:methodTagValue
tagName="mytest.tag2" paramName="name"/>"/>
</XDtMethod:ifHasMethodTag>
</XDtMethod:forAllMethods>
</methods>
</class>
</XDtClass:forAllClasses>

次にJavaソース(1本/2本)
package testpack;

/**
* @mytest.tag1
* value="hoge1"
*/
public class XDocletTest0 {

/**
* @mytest.tag3
* name="foo"
*/
public static String STATIC_VAR = "hoge";
int i = 0;

/**
* @mytest.tag2
* name="piyo2"
*/
public static void method1() {
return;
}

private String method2(String arg) {
return arg;
}

}

(2本/2本)
package testpack;

/**
* @mytest.tag1
* value="hoge1-2"
*/
public class XDocletTest1 {

/**
* @mytest.tag3
* name="foo-2"
*/
public static String STATIC_VAR = "hoge";
int i = 0;

/**
* @mytest.tag2
* name="piyo2-2"
*/
public static void method1() {
return;
}

private String method2(String arg) {
return arg;
}

}


実行すると、以下の二つのファイルが作成されます。
う~ん。強力。(って不親切過ぎかな)
==================================================
Class name :
testpack.XDocletTest1
Class tag values :
hoge1-2
Field tag values :
foo-2
Method tag values :
piyo2-2

==================================================
Class name :
testpack.XDocletTest0
Class tag values :
hoge1
Field tag values :
foo
Method tag values :
piyo2

<?XML version="1.0" encoding="UTF-8"?>

<class name="XDocletTest1">
<variables>
<tag value="hoge1-2"/>
</variables>
<fields>
<tag value="foo-2"/>
<tag value=""/>
</fields>
<methods>
<tag value="piyo2-2"/>
</methods>
</class>

<class name="XDocletTest0">
<variables>
<tag value="hoge1"/>
</variables>
<fields>
<tag value="foo"/>
<tag value=""/>
</fields>
<methods>
<tag value="piyo2"/>
</methods>
</class>


余裕が出たら、もう少し細かく解説します。

参考)
春のXDoclet中心生活

2008年4月17日木曜日

プロジェクトにおける不安について

プロジェクトに参画した人は、普通さまざまな不安を抱きます。

「このコードをリリースして大丈夫だろうか」(開発者)
「無事リリースできるのだろうか」「明日はどんな障害が発生するだろうか」(マネージャー)
「障害が発生して業務を止めたりしないだろうか」(顧客)

人は不安からは逃げられません。プライベートであろうが仕事であろうが、あらゆることが不安の対象となります。

むしろ不安を抱かない方が却って問題です。何も不安に思わなければ、問題意識も生まれません。漠然とした不安をきっかけとして、具体的で有効なアクションを取れることもあるのです。

不安には冷静に対処する必要があります。なぜ自分が不安に思うのか。まずはその対象を明確にしなければなりません。また、不安は対象が明確になればほとんど消えてしまうものなのです。
いろいろ考えてみると不安の原因が単に自分の無知だった、ということも少なくありません。その場合も不安はさっぱりと消えてしまいます。
例えば、あるプログラムが本当に動くかどうか不安に思って調べたところ、開発環境でずっと問題なく動いていることを知ったために一切の不安がなくなった、などということは開発者ならば珍しくないでしょう。

もちろん、対処しようがない不安もあります。そういう場合は覚悟を決める他はありません。ですが、プロジェクトに参画することで生じる不安は、ほとんど全てが具体化すれば消えてしまうものです。ですから、具体化の努力を怠ってはなりません。

まずは漠然とした不安を感じたとして、その不安の原因をできるだけ具体化するのです。例えばある機能がちゃんと定義されているかどうか。具体的にはどのような定義が、どこに設定してあればよいのか。期待通りに定義されていることを確認すれば、不安は消えます。
あるいは無知から生じる不安については、自分が何を知らないから不安なのかをはっきりさせ、その具体的な事実なり知識なりを仕入れさえすれば、同じく不安は解消します。

それを怠り、いたずらに不安の対象を広げるのは好ましくありません。特に下手に不安を抽象化するとその対象は際限なく広がることになります。
例えば、先ほどの「設定漏れ」が不安の原因ではなく、「人によるチェックミス」が原因だと考えてしまったとしましょう。ところが人為的なミスはどこでも常に発生しうるものです。ですから、そのように不安の原因を定義してしまうと、不安はいつまで立っても解消しないことになります。人によるチェックミスは「あってはならないもの」ではなく「当然ありうべき」言わば制約なのです。不安の原因がチェックミスであると考えてしまうと「チェックミスが発生する(し得る)」限り不安は解消しません。すると「今後チェックミスは許さない」という無茶なスローガンが生じたり、「他にチェックミスはないのか」という愚問が生まれたりします。
もちろん今後同じようなミスが発生しないように、「設定漏れ」というリスクに対して「設定項目が正しいか」チェックする運用を作る、という対処は正しいと言えます。そうではなく、顧客やマネージャーの漠然とした不安を「全部チェックする」「あらゆる設定の妥当性を確認する」ことによって解消しようとすると、行きつく先はプロジェクトの破綻です。

顧客やマネージャの不安を上手にハンドリングできるSE、つまり「信頼感」を与えるSEこそが、優れたSEではないか、と私は思います。

DB2ストアドプロシージャ メモ

Stored Procedure のページが簡潔で良いです。
この通りにやれば出来ますが、一部ハマった点があったのでメモしておきます。

まず、接続は「デフォルトコネクション」を使います。
Connection con = DriverManager.getConnection("jdbc:default:connection");

最初、'jdbc:db2://hostname:port/dbname'でやったところ見事にハマりました。

まず「resultset closed invalid operation」とか何とか出て、気持ち悪いなと思ったもののconnectionをクローズせずにやったところ「java.sql.CallableStatement.executeQuery() was called but no result set was returned.」だのなんだの。

デフォルトコネクションを使ったところ、connectionをcloseしても問題なく使えました。

次に
db2 "call sqlj.refresh_classes ( void )"

です。これを実行すると、SQL0444N rc=4 が出ました。
SQL0444N  ルーチン "*_classes" (特定名 "SQL080417102058370")
が、アクセスできないライブラリーまたはパス "...refresh_classes"、関数
"sqlj.refresh_classes" のコードで実行されています。 理由コード: "4"
SQLSTATE=42724

これも大分ハマったのですが、
以下のように'void'を取ると問題なく実行できました。
db2 "call sqlj.refresh_classes()"

以上です。

2008年4月15日火曜日

コマンドラインからCVSを使う

コマンドラインからCVSを使ってみます。
Eclipse - CVS連携はよく出来ていますが、やはり内部の動きが見えないと不安ですね。(そんなことはないですか?)

情報提供しているページは山ほどありますが、自分のために整理してみました。

■ログイン■

$CVSROOTを確認します。サーバがセットアップされているのが前提となります。
# echo $CVSROOT
:pserver:user@hostname:/cvsroot
【CVSにログイン】
# cvs login
Logging in to :pserver:user@hostname:2401/cvsroot
CVS password: ********[Enter]

■プロジェクト追加■
# ls sample_project
a.c b.java
# cvs import -m "初期登録" sample_project vendor start
N sample_project/a.c
N sample_project/b.java

■バイナリファイルの指定■
# cvs co CVSROOT
# cd CVSROOT
# vi cvswrappers
【ファイルを編集し、以下の行を追加します】
*.gif -k 'b'
*.GIF -k 'b'
*.bmp -k 'b'
*.BMP -k 'b'
*.xls -k 'b'
*.XLS -k 'b'
*.Xls -k 'b'
*.pdf -k 'b'
*.PDF -k 'b'
*.class -k 'b'
# cvs update
cvs update: Updating .
M cvswrappers
# cvs commit
cvs commit: Examining .
【viが開くので"バイナリファイル指定を追加'というコメントを入力し':wq'でviを終了します。】
CVS: ----------------------------------------------------------------------
CVS: Enter Log. Lines beginning with `CVS:' are removed automatically
CVS:
CVS: Committing in .
CVS:
CVS: Modified Files:
CVS: cvswrappers
CVS: ----------------------------------------------------------------------
cvswrappersを編集
~
~
"/tmp/cvsNrkSUb" 9 行、314 文字
Checking in cvswrappers;
/cvsroot/CVSROOT/cvswrappers,v <-- cvswrappers
new revision: 1.3; previous revision: 1.2
done
cvs commit: Rebuilding administrative file database

■CVS関連作業の終了■

cvsからチェックアウトしたファイルをリリースします('-d'オプションでディレクトリも削除されます)。一旦作業を止めるイメージです。
# cd .. # <- 削除対象ディレクトリを指定するため上位に移動
# cvs release -d CVSROOT
You have [0] altered files in this repository.
Are you sure you want to release (and delete) directory `CVSROOT': yes
#

■タグをつける■

タグをつける前に、以下のを確認しましょう。
 バージョンを構成する全てのファイルがコミットされていること
 リリースしないファイルやディレクトリがないこと
  # ls *.log *tmp* *test* *.org *.orig *.bak *.back *.[0-9]* *hoge* *piyo* *foo* *bar* # などなど
 classなどをCVSで管理している場合は、タグをつける前にコンパイルをしましょう
# ls
sample_project
# cvs tag rel20080326 sample_project
cvs tag: Tagging sample_project
(略)

■rdiffの使い方■

タグ=バージョン名をつけると、タグを使ってもバージョン同士/バージョンと現状のdiff/rdiffを見ることができます。(もちろん-r [リビジョン]でもOKです)
# cvs commit -m'rdiffテスト' a.c
# cd ..
# cvs rdiff -s -r rel20080326 sample_project
cvs rdiff: Diffing sample_project
File sample_project/a.c changed from revision 1.2 to 1.3

■cvs statやcvs logコマンドでタグの状況を確認■
# cvs stat -v a.c
# cvs log a.c

■タグを削除する■
# cvs stat -v | grep -p Existing
【削除したいタグを確認する】
# cd ..
# cvs tag -d rel20080326 sample_project

■エクスポートする■

バージョンを指定してエクスポートします。
エクスポートによって"CVS"管理ディレクトリを含めないでソースを抽出することができます。
タイムスタンプも、レポジトリ上の最終更新日に揃えられます。
# mkdir export
# cd export
# cvs export -r rel20080326 sample_project

■コメントを編集する■
# cvs admin -m1.3:'変更後コメント' a.c

■パーミッションを変更する■

やむを得ず(go+xやパg+wなど)パーミッションを変更する場合は、レポジトリのファイルパーミッションを直接変更することになります。
レポジトリのパーミッション+umaskで最終的なパーミッションが決まります。
動かなくならないよう、常識的な範囲で変更して下さい。

■ログインしてモジュールをチェックアウトする■
# cvs login
Logging in to :pserver:cvs@hostname:2401/cvsroot
CVS password: ********
# #【重要】必要に応じてumaskを変更します。
# # 755, 644系のパーミッションを期待するなら、umaskは022とします。
# umask 022
# cvs co sample_project # <- co は checkoutの略

■ファイル追加■

ファイル'addtest.ksh'を追加します。
# cvs add addtest.ksh
# cvs commit -m"新規登録" addtest.ksh

■バイナリファイルの追加■
# cvs add -kb binry.o

■ディレクトリ追加■

ファイルが存在しないディレクトリは存在しないのと同じです。
# mkdir adddir
# echo "test" > adddir/adddir.ksh
# cvs add adddir
# cvs add adddir/*.ksh
# cvs commit -m "dir以下のファイル追加"

他の人が追加したディレクトリを自分の作業ディレクトリに表示させるときは
cvs update -d -P を実行します。

■ファイル編集■

viでも何でも使って適宜編集した後、update, commitします。
'cvs commit ファイル名' とすると指定したファイルのみをcommitします。
# cvs update
# cvs commit -m"コメント" fileName.txt

■ファイル削除■

実際にはファイルは削除されません。レポジトリから見えなくなるだけで、いつでも復活させることができます
# rm filename
# cvs remove filename
# cvs commit -m "Removed unneeded files"

cvs remove -f とすれば、rmコマンドは不要です。
# cvs remove -f filename

# 削除後でもログは参照可能です。
# cvs log filename

■ファイルの削除を中止する■

手順概要(1)remove 実行前
# rm filename
# cvs update

手順概要(2)remove 実行後(commit未済)
$ rm filename
$ cvs remove filename
$ cvs add filename
$ cvs commit

手順概要(3)remove/commit後
$ cvs add filename
$ cvs commit

■ディレクトリ削除■

実際には削除されません。リポジトリには空のディレクトリが残ります。
# rm adddir/*.ksh
# cvs remove adddir/*.ksh
# cvs remove adddir
# cvs commit -m "adddirを削除"
# cvs up -d -P

■ファイルの名前を変更する■■
# mv addtest.ksh renametest.ksh
# cvs remove addtest.ksh
# cvs add renametest.ksh
# cvs commit -m "addtestをrenametestに変更"

■ディレクトリの名前を変更する■
特別な手順はありません。新規のディレクトリを作成し、ディレクトリ中のファイルを作成したディレクトリに移動させます。

■古いファイルとのDIFF■
# cvs diff -r[rev] [ファイル名]

■コメント/履歴確認■
# cvs log [ファイル名]

■バージョン確認■
# cvs stat [-v] [ファイル名]

'-v'を追加するとタグ(リリースバージョン)情報を出力します。

■最新バージョンに書き戻す■
$ rm ファイル
$ cvs update ファイル
U ファイル

【あるいは】

$ cvs update -C ファイル

■古いバージョンに書き戻す■
# cvs log ファイル
# cvs diff -r[rev] ファイル # 戻したいファイルとのDIFFを確認する
# cvs update -p -r [rev] ファイル > ファイル
# cvs update
M ファイル
# cvs commit ファイル

■追加を途中でやめる■
# cvs remove -f ファイル

commitは不要です。

■作業ディレクトリがいつチェックアウトしたものか分からなくなった■
作業ディレクトリで'cvs update'するか、一旦作業ディレクトリを削除してcheck outします。
(当たり前か)

■終了する(release)■

cvs releaseコマンドを使います。
'-d'オプションを使うとディレクトリまで削除してくれます。
# cvs release -d sample_project


【参考】
CVS--Concurrent Versions System (in Japanese) - CVS のコマンド便覧 必要十分なリファレンス
CVS使用法メモ(Hishidama's CVS usage Memo) 実践的なコマンドメモ
リリースブランチ(cvs) ちょっと毛色は違いますがブランチの意義について

2008年4月4日金曜日

DIFF出力結果の見方

申し訳ないほど基本的なことですみません。
普段ぼおっと見ているもので、今回意識的に整理してみました。
【凡例】
$ diff File1 File2

Lines Affected in File1 Action Lines Affected in File2

1. Number1 a Number2[,Number3]
2. Number1[,Number2] d Number3
3. Number1[,Number2] c Number3[,Number4]

読み方
1. 'File1'の'Number1'行に、'File2'の'Number2'(から'Number3')の行を追加
2. 'File1'の'Number1'(からNumber2')を('File2'の'Number3'から)削除
3. 'File1'の'Number1'(からNumber2')を'File2'の'Number3'(から'Number4')の記載へと変更

[tmp/] $ cat a.txt
aaa
bbb

ccc

ddd

[tmp/] $ cat b.txt
xxx
yyy

iii
jjj


[tmp/] $ diff a.txt b.txt
1,2c1,2 (1)
< aaa .
< bbb .
--- .
> xxx .
> yyy .
4c4,5 (2)
< ccc .
--- .
> iii .
> jjj .
6d6 (3)
< ddd .


(1) a.txtの1~2行目の(aaa, bbb)を、b.txtの1~2行目(xxx,yyy)に変更し、
かつ
(2) a.txtの4行目(ccc)を、b.txtの4~5行目(iii,jjj)に変更し、
かつ
(3) a.txtの6行目(ddd)を削除すれば、
a.txtとb.txtは同じファイル

といった読み方になりましょうか。

2008年3月18日火曜日

EclipseからCVSを使う

ソースコードが増えてきたのでCVSを使ってバージョン管理をしたいと思います。

■手順あらまし
(1)AIX環境にCVSサーバを立てる
(2)Eclipse(Lomboz)からCVSを利用する

■手順詳細
(1)AIX環境にCVSサーバを立てる

a. 下記URLからcvsとzlib(CVSの前提)をダウンロードします
AIX Toolbox for Linux Applications
3/18時点では、下記のrpmが最新でした。
cvs-1.11.17-3.aix5.1.ppc.rpm
zlib-1.2.3-4.aix5.2.ppc.rpm

b. インストールします
# rpm -i zlib-1.2.3-4.aix5.2.ppc.rpm
# rpm -i cvs-1.11.17-3.aix5.1.ppc.rpm
#

c. 各種設定
1:ユーザの作成
smitty mkuser

などでCVSユーザ(ここではcvsuser)を作成します。
パスワードも忘れずに付けておきましょう。(rootで"passwd cvuser")
(手順は割愛)

2:CVSレポジトリの作成とinit
ディレクトリを作成し、cvs initを実行します。
# su - cvsuser
$ mkdir cvsroot
$ export CVSROOT=/full/path/to/cvsroot # .profileなどにいれてしまいましょう
$ cvs init
$

3:/etc/servicesに以下のエントリーを追加
/etc/servicesに以下のエントリーが存在しない場合(存在しないと思いますが)追加します。
cvspserver      2401/tcp # CVS client/server
cvspserver 2401/udp # CVS client/server

4:/etc/inetd.confに以下のエントリーを追加
cvspserver stream tcp nowait root /usr/bin/cvs cvs -f --allow-root=/full/path/to/cvsroot pserver

5:inetdを再起動
# stopsrc -s inetd
0513-044 /usr/sbin/inetd サブシステムは停止を要求されました。
# startsrc -s inetd
0513-059 inetd サブシステムは始動しました。サブシステム PID は 1536230 です。

この状態で'cvsuser'でEclipseから接続し、使うことができます。

6:(オプション)cvsのpasswdを作成
/full/path/to/cvsroot/CVSROOT
以下にpasswdファイルを使います。書式は
userid:encrypt_password:valid_userid
です。パスワードが空で、cvsuserと同じ権限でアクセスできる'cvs'ユーザを作成する場合は、
cvs::cvsuser

というエントリーを追加すれば良いです。
ファイルのエンクリプトにはApacheなどに付属する'htpasswd'ユーティリティを使えばよいようですが、個人的な環境なのでそこまではしません。

(2)Eclipse(Lomboz)からCVSを利用

Eclipse(Lomboz)を起動し、'Window' → 'Open Perspective' → 'Other' から、'CVS Repository Exploring'を選択します。

'CVS Repository'ビューを右クリックし、'New' → 'Repository Location'を選択します。

'Add CVS Repository'で以下の項目を入力し、'Finish'をクリックします。
Host: CVSサーバのホスト名
Repository Path: /full/path/tocvsroot
User: cvsuser
Password: ******* (cvsuserのOSパスワード)

これでCVS Repositoryが表示され、使えるようになります。

2008年3月13日木曜日

awkメモ(シングルクォーテーションを出力する)

awkでシングルクォーテーションを出力する方法です。
# echo a | awk '{print "\047"$1"\047"}'
'a'

例えばこんな利用方法です。
db2 -x "select itemid from icmstcheckedout with ur" | awk '{
print "select sourceitemid,targetitemid from icmstri001001 where sourceitemid=\047"$1"\047 with ur"
}' > $TMP_SQL
db2 -vf $TMP_SQL >> $LOG

2008年2月27日水曜日

罪悪感との戦い(1)

顧客に迷惑を掛けてしまった時の罪悪感に、どう対処するか。システム構築の仕事をする時に、大事な課題です。
罪悪感を強く感じてしまうタイプの人は、その感情にドライブされてハードワークにのめり込み、体か精神を壊してしまうという結果にハマりやすいといえるでしょう。
そこまでいかないまでも、何となく「何でオレがこんな目に・・・」「知るかよ!」的な感情に囚われ、モチベーションが下がったり、訳もなく顧客に悪い感情を持ってしまうこともあるかと思います(顧客だって同じことを言いたいんですが、そこまで気が回らなくなるんですね)。
罪悪感をあまり感じずに、モチベーションを維持できる人は非常にお得です。私も常日頃そうなりたいものだ、と思っているのですが・・・

以上の説明では、分かる人には分かるかも知れませんが、分からない人には何のことだかさっぱり分かりませんね。もう少し具体的に書いてみましょう。まず、迷惑を掛ける原因の方です。

【ケース1】「自分の書いたソースにバグがあってシステムが止まってしまった」時の罪悪感。

→ まあ、私に関して言えば、この場合に割とまっとうで適切な罪悪感が得られますね。なんと言ったって自分が書いたものが問題を引き起こして周りに迷惑を掛けた訳ですから、自分で責任を取りたい。
体調を崩さない限り、出来るだけ努力して修正して、必要であれば徹夜も辞さない、ということを、モチベーション維持しながら遂行できるような気がします。
当然、懲罰的な徹夜待機とか、出来るだけ無意味な作業は勘弁して欲しいということはありますが、それでもまあ仕方がないか、と納得できます。

【ケース2】「自分の部下(指導する後輩)の書いたソースにバグがあってシステムが止まってしまった」時の罪悪感。

→ 他人(隣接プロジェクト|他社|他チーム)のバグならば極端なとばっちりはないので、ケースからは除外します。こちらで再テストが発生したとしても「お互い様」というのが大人の対応でしょう。
そうではなく、直接自分が手を下したわけではないが、強めの連帯責任が発生するパターンです。
この場合は「後輩のミスの後始末を助けてやるいい上司(リーダー|先輩)」というイメージを無理矢理自分に重ねつつ、心の中では部下に「このやろー」と思いつつ、何とかモチベーションを維持する、といったところでしょうか。部下から一言「ありがとうございました」あるいは「ご迷惑をお掛けしました」と言って貰えれば「俺も昔は失敗したもんだ」などと余裕の対応できると思います。

【ケース3】「第三者が開発したが、それを引き継いでしまい責任を追うことになったプログラムに(略)」時の罪悪感

→ 前任者のソースコードを引き継いで管理しているようなケースです。
前任者が自分と同じ会社の人間かそうではないかによって微妙に異なりますが、前任者の顔が見えている分【ケース2】よりも気が楽ではないでしょうか。「やらなきゃならないことはやるけど、俺の書いたソースじゃねーし」みたいな感覚です。罪悪感がない分「なんで俺が」というのはあるかもしれませんが、周りも事情は理解しているでしょうから、そう無体なことは言われないような気がします。やらなければならないことを淡々と進める感じですね。

【ケース4】「第三者から提供されたが、こちらで責任を負わなければならないプログラムに(略)」時の罪悪感

→ ケース3と違い、第三者の顔が見えないパターンです。具体的にはプロダクトの障害や、多くの開発メンバーを束ねるゼネコンのリーダーやマネージャにとってのプログラム障害がこれに該当します。
プロダクトと一口に言っても、OS、Oracle、WebSphere、DB2、WebLogic などいろいろなものがあります。これらを扱っている、いわゆるインフラ構築SE、基盤SEが時折直面する障害です。障害が発生したけれども、それは自分やプロジェクトチームで作成した個所ではない。じゃあ誰の書いたコードで障害を出しているんだ、と言えばどこにいるかも分からない見知らぬ他人です。開発拠点はアメリカか。はたまたインドか中国か・・・。しかし、その障害の責任は現場で取ることになるのです。その責任の取り方(取らせられ方)も、誰が、という具体的な個人名ではなく、法人名になってくるのが普通です。法人名で個人が責任を取らされるわけです。
リーダーやマネージャーにとってのプログラム障害も同様です。一応自分のチームで作っていることになってはいるが、実際のソースなんか見たこともない(スキルを問題にしているのではありません。数千本のソースを全て見て理解できるわけがない)。せいぜい特徴的な設計とテストケースの概要と、これまで起きた障害を把握している程度、上等なプロジェクトマネージャと言えましょう。それでもプロジェクトで障害が発生して、大問題になる。「知らねーよ」とは口が裂けても言えない。これも、プロダクトの障害と似ていて、個人ではなく、法人名で責任を追及されてしまうパターンです。

【まとめ】
やはり一番きついのが4のケースでしょう。自分がコントロールできたわけでもない障害について、法人名で責任を問われるのですから。
障害が発生してがっかりしている顧客に申し訳ないと思いつつ(中には張り切っている顧客もいるかもしれませんが)、自身の罪悪感を何とかマネジメントしなければなりません。
恐らくは適度な罪悪感が必要なのでしょう。過度に思いつめると、行き着く先は肉体的/精神的ダウンです。あまりに罪悪感がなさすぎると、顧客からは信頼されなくなります。
顧客の立場は理解した上で、障害対応に必要なモチベーションは保持しつつ、不安に駆られた顧客からの、理不尽な対応は適宜断るなり、有効なものを逆に提案するなりできる、そんな適度な精神状態になりたいものです。

(続く・・のか?)

2008年2月25日月曜日

技術の変遷とその弊害(2)

技術の変遷がセールスパーソンというフィルタを通ると、ある滑稽な状況を生み出します。

それは、技術があたかも魔法のソリューションのように語られるようになることです。もちろん顧客の誰もそんな売り文句を全て信じているわけではないでしょう。しかし、それを生み出す方は必死です。世の中を見回せば、ある技術が「とても素晴らしい」ものであり「顧客の抱える問題を見事に解決する」ものであるキャッチフレーズに溢れているようです。曰く、複雑怪奇な環境で行る非効率なビジネスプロセスを革新して、目覚しい生産性をもたらす。あるいは、そこかしこに散ったビジネスロジックを統合して業務の効率化だけではなく運用体力の軽減を実現する。あるいは企業の意思決定を迅速に行えるようにし、国際競争力を強くする、などなど。

しかし、そのセールストークが指すのがEJBであったり、SOAPであったり、Web Servicesであったりすると、エンジニアとしては苦笑せざるを得ません。一体この技術の何が/どこがお客様のプロセスを革新するのか、と。
恐らく、このような新しい技術を利用して、業務を革新することは可能ではあるでしょう。しかし、それは多分EJBやSOAPを使わずともできるはずです。それにEJBを利用して業務革新が出来たということは、必ずしもEJBを利用すれば業務革新ができる、ということを意味しません。技術は手段に過ぎないのです。

このようなことを言うと、技術なんてどうでもいいのか、と批判されてしまうかもしれませんが、そうではありません。
技術が変ることによって、人が活性化します。ITシステムを作るのは結局のところ人ですから、人(特に若い人)が楽しめる、夢中になれる技術は必要です。そのためには技術は常に変って行く必要があると思います。
そういう意味では、ピントが外れたようなよく分からないセールストークも、新しい技術をお客さんに売り込むためには必要です。買ってもらえなければ、その技術も使われることがありませんから。

問題は、大風呂敷を広げる営業が技術を理解していないことではなく、現場を左右する意思決定を行う人が技術に疎い、という状況が広がることです。

技術を理解していなくてもマネジメントは出来る。システムの運用が出来る。そういう言葉が大手を振っているのが現状です。それは決して間違いではない。Javaのシステムを理解し、意思決定するために、必ずしもJavaのコーディングが出来る必要はありません。C言語でもCOBOLでも、何らかの開発経験があればアナロジーによって理解はできるし、意思決定もできるでしょう。

しかし、その「技術を理解していなくてもマネジメントができる」「システムの運用ができる」というスローガンの下には、実は暗黙的な過去の技術の蓄積があるのではないでしょうか。Javaは知らない。知らないけれどシステムの上流設計は出来る。だからJavaの知識は不要だ。本当でしょうか。COBOLやC言語の知識があるから、Javaで実装するシステムの設計ができるのではないでしょうか。

Javaやそこから派生した新しい技術が進化する度に、暗黙的な過去のノウハウに依存した「新しい技術を理解しなくてもよい」という思い込みが、どんどん時代遅れになってくるような気がしてなりません。さすがに今さらJavaを勉強して欲しい、などと言うつもりはありません。昔の技術でも何でもよいのです。とにかく現在のシステムを理解しようと努力すること。現在のシステムが人によって作られていることを理解すること。その努力がなければ、これからもどんどん現場とマネジメント層が乖離して行くことになるのではないでしょうか。現場とマネジメントが無理解によって乖離して行けば、理不尽な意思決定が行われることも増えますし、それにつれて現場のモチベーションも下がって行きます。

技術が変って行くその背景を見つめ、適切に理解し、セールストークに踊らされず、現場をきちんと理解すること。これらのことが重要になってくるだろう、と私は思います。

技術の変遷とその弊害(1)

IT業界ではどんどん技術が移り変わって行きます。
昔のホスト中心の構成、一昔前のクライアント・サーバー構成、そして現在のJavaアプリケーションサーバが中心になった三層Webアーキテクチャ構成。
この遷移が、何を背景として進んできたのか。それには例えば以下のような理由があるでしょう。

■ ハードウェアの進歩
安価なハードウェアが普及したことによって、リッチなクライアントを展開しやすくなったこと。
→ ホストからクラサバへ。
コストと比較して、相対的にサーバの処理能力が向上したため、サーバでロジックを実行できるようになったこと。
→ クラサバから三層アーキテクチャへ。

■ インターネットの普及
インフラとしてのインターネットが一般化し、その上で稼働させるアプリケーションの実行環境が整ってきたこと。(Java Webアプリケーションサーバ、PHP、Ruby)
それによってイントラネットでもWebアプリケーション実行環境が利用されるようになった。
→ クラサバから三層アーキテクチャへ。

上記の他にも理由はいろいろありそうですが、ここで少し根本的な問いを立てて見ましょう。
それは「なぜ、ソフトウェアが変らなければならなかったのか」という問いです。

ソフトウェアの技術の変遷と軌を一にして、環境が変ってきたことが分かりました。どちらかと言えば環境が先に変化し、それに伴ってソフトウェアの状況が変ってきたように見えます。この流れは不自然ではありません。ハードウェアの変化は('ハード'らしく)硬直的です。つまり、ソフトウェアに合わせてハードウェアが変るよりもハードウェアの変化に合わせてソフトウェアが変る方がよほど自然だからです(一部の例外はありますが)。

しかし、ここでもう一度問いを立ててみましょう。「ハードウェアが進歩したからといってソフトウェアは本当に変らなければならなかったのか」。

実はここには必然的なつながりはないのです。ホストの処理能力が向上したのなら、ホストで稼働させるアプリケーションもそのまま早くなります。ハードウェアを置き換えて、アプリケーションをそのまま稼働させればよい。
クラサバも同様です。クライアントが早くなれば、そのままクライアントアプリケーションの処理が早くなる訳ですから、単にリプレースすればよい。

ソフトウェアが変化しなければならないのは、主に二つの理由からです。

一つは技術を活性化すること。技術を新しくすることによって、新しい人材をひきつけることができます。また、若い技術者は昔の技術に早晩慣れてしまい、それを改善したがります。彼らは学習意欲が活発で、また吸収も早い。この場合ソフトウェアの変化は、ソフトウェアの改善に結びつきます。
もう一つはビジネス上の理由です。新しいビジネスを作り出すために、ソフトウェアを変化させるのです。これをさらに二つに分けてみます。
一つは、いわゆるプロダクトのバージョンアップです。プロダクトに新しい機能を付け、バグを修正してリリースします。このサイクルを進めることで、昔のバージョンのサポートを切ることができます。サポートを切れば、サポートに必要な人体力を別の仕事に振り分けることができますし、顧客にはバージョンアップ費用を要求することができます。
使いもしない機能を売りつけるのか、とか、バグを直しただけの製品に新たに金を取るのか、という意見もあるかもしれませんが、ソフトウェアビジネスとしてはやむを得ない流れでしょう。
もう一つのタイプは、セールスのための新技術開発です。これは実体としてはプロダクトのバージョンアップと一体になっています。新しい技術はこんなに素晴らしく、お客様の問題を解決するものだからぜひ買ったらいいよ、という提案に結びつきます。

(続く)

2008年2月15日金曜日

プロジェクトと、そうでないもの

プロジェクトとは何でしょうか。
普段プロジェクトに関わっている人にとっては、十分過ぎる程の実在感を伴っているこの言葉ですが、悲惨なプロジェクトを経験したり、理不尽な顧客の要望を聞いていると、改めて「プロジェクトとは?」と考えさせられることは少なくありません。

ざっとWebや辞書で調べたところ、プロジェクトとは「一定の期間で何かを作り出すこと」と定義できるようです。これをベースとして、例えば「複数人で遂行するものである」とか「コストが決まっている」とか「新しい物を作る」とか付随的な定義もあります。

しかしまあ、はっきり言ってこのような定義に意味はないですね。なぜなら、かなり高いレベルまで抽象化されてしまっているからです。ここまで抽象化してしまうとシステム開発だろうがビルの建設だろうが、商品の開発だろうが、何にでも適用されます。無難ではありますが、空虚な定義です。

ここではもう少し具体的に踏み込んで考えて見ましょう。具体的に、とはどういうことでしょうか。それはもうこの上なく具体的に「いや、こんなのプロジェクトじゃねーよ」と心から叫びたくなるような、そんな無体なプロジェクトについて考えてみたいと思います。

さて、ここから本題に入りましょう。

いきなり本質に飛びますが、何のかんの言ってプロジェクトそれ自体が存在するわけではありません。存在するのはあくまで人です。人が集まっては喧々諤々と、あるいはしんねりむっつりと、何事かやっているわけです。要するに、プロジェクトはそれ自体が実体として存在するのではなく、人がやるものです。つまり、プロジェクトは人次第、ということになります。

プロジェクトの中でもシステム開発のそれには特徴があります。それは「成果物がなかなか見えない」ということです。とにかく文書は大量に出来上がる。コードのサイズも増えて行く。でも、最終的な成果物=システムがちゃんと動くかどうかはなかなか見えてきません。確かにテストをすれば分かります。でも、そのテストがちゃんとシステムの必要十分条件を満たしているかどうかは、非常に分かりづらい。

分かりづらいから人は何とかしようとします。何とかしようとするわけですが、そのやり方がまずいと「こんなのプロジェクトじゃないよー」という状況になります。

話が拡散してきました。要するにマズいプロジェクトは結局の所人の問題だ、というのが結論なのですが、一旦要件定義作業にフォーカスを当てましょう。

上手く行かないプロジェクトの特徴の一つに「要件がなかなか決まらない」というものがあります。あれもしたい。これもしたい。それも必要だ。いろいろ要件がでてきます。積極的な(=ポジティブな)要件を決めることは比較的簡単です。あれができます。これができます。出来ることを言っているだけなら誰にも文句はありません。
難しいのは消極的(=ネガティブ)な要件の決定です。このシステムではこれができません。このシステムを使うと、このデータは切り捨てる必要があります。この意思決定の方がよほど難しく、重要なのです。
何かを決定すれば、必ずどこかにリスクなり制約が発生します。意思決定とはそういうものです。ある選択をするということは、別の選択肢を捨てることなのです。それによってリスクや制約が発生する。しかし、そのリスクを取りたがらないお客さんがいる。そうなるとなかなか話が進まないことになります。あるいは進んだとしてもあれもこれも機能を盛り込むことによって納期、コスト、品質に悪影響が出てくる。悲惨なプロジェクトの始まりです。

このようなプロジェクトが始まると(そしてマネージャーがきちんと顧客の要求をマネージできないと)ひたすらTODOが定義されて、納期だけが決められ、品質が悪くなるかあるいはTODOがいつになってもこなせない、皆が不幸になるプロジェクトとなってしまいます。

こういう顧客をマネージすることでまず大事なのは、顧客と対立するのではなく、顧客をこちらの立場に引きこむ、ということです。リソースと時間が有限であるという課題を共有し、限られた中で一緒に何とかしてゆく、というスキームに持ち込むことです。コラボレーションして行く中で「できないものはできない」という当たり前のことを理解してもらうのです。
それから「未定義の領域がある」ことを理解してもらうことも大事でしょう。例えば「システムはこう動く」と定義して、お互いに同意して、その通り作ったとします。当然定義されていない個所については、顧客の思った通りにはならないこともあります。それはベンダーだけではなく顧客の責任でもあるのです。それを理解して貰えないと、いつまでたっても改善要望が尽きることはありません。顧客も「聞いてなかった」「知らなかった」では済まされないという意識を持ってもらう必要があります。
出来上がった後で気に入らない点が出たとしても、それはお互いの責任だ、ということです。システム開発プロジェクトでは、お客様は神様ではないのです。限られたリソースの中で一緒にモノを作って行くチームの中で、とりわけ責任が重いメンバー(リーダー)の一人なのです。

システム化するとは、あるプロセスを単なるデータの流れへと抽象化する、という側面があります。つまり、システム化によって何かが切り捨てられるのです。切り捨てられることによって、効率的になるのです。
ですからシステム化することによって何か出来ないことが出てくるのを恐れる必要はありません。それもまた、システム化の目的の一つなのですから。

2008年2月13日水曜日

システム開発原理主義に注意

原理主義とは、何らかの教義を絶対視してそこからの逸脱を許さないような態度を意味します。ここではもう少しやわらかく「ある課題が、100%、完ぺきに遂行されることを求める態度」を指すことにしましょう。

厄介な態度ですね。しかし、普通の緊張感を持ってプロジェクトを運営していると、しばしば出くわす精神状態に違いありません。「徹底的に調査しろ!」「バグは許さない!」などなど。

プロジェクトを運営しているとそう言いたくなることはよくあります。しかし、実際のところこのような原理主義的スローガンは「百害あって一理なし」と言えるでしょう。

「なんだと?バグは徹底的に潰すべきだ!」「セキュリティホールは許されない!」「ミスなんかありえない。再チェック、再々チェック、再々々チェックだ!!!」というプロジェクトマネージャー(あるいは体育会系のリーダー)からの声が聞こえてきそうです。

ちょっと待ってください。冷静になって考えてみましょう。私も別にミスを許容している訳ではありません。明らかなミスやセキュリティホールは潰すべきでしょう。しかし、その手のタスクを「スローガン化」してしまうことの弊害は、今一度考えた方がよいと思うのです。

「徹底的にやります」「全てチェックします」という宣言は一見頼もしいものです。真面目な部下が青ざめた顔でこう報告した時「うむ。そうか。頑張れ!」と言いたくなることもあるでしょう。しかし、部下を信頼して後押しするとしたら、残念ながらあなたはマネージャー失格です。

なぜでしょうか。それは具体性が欠けているからです。「徹底的」とは、「全て」とは具体的には何を指しているのでしょうか。実際の所あなたの部下はどのように作業をするつもりなのでしょうか。それはひょっとしたらムダな作業ではないでしょうか。

「セキュリティホールをなくす作業が、ムダなはずはない!」あるいは「バグを潰す作業がムダであるはずがない!」とおっしゃるでしょうか。残念ながらそれもまた原理主義的スローガンから派生する無思考に過ぎません。

具体的に考えましょう。例えば今構築しているのがイントラネットを対象としたWebアプリケーションだとしましょう。するとセキュリティホールの重要性は急激に下がります。バグにしたって業務に関係あるものだけを探せばいいわけです。では、どうやって探すか。業務に関係ある操作は、テストケースで網羅されているのではありませんか?だとすれば、テストが実施されていれば、業務に関係のないバグは、既に潰れているはずですよね。そうでないとすれば、やるべきはテストケースの漏れをチェックすることであってバグの洗い出しではないでしょう(テストケースの見直しが手遅れでなければよいのですが・・・)。

そもそも見てすぐに分かるようなバグなら、既に直っているはずです。これまでの切り口では見えないものが潜在バグなのです。とすれば、潜在バグを顕在化させるためには、顕在化させるような切り口が必要となります。具体的にそのような切り口が定義できるなら、そしてその切り口が現実的ならば実際に調査してもよいでしょう。しかしいたずらに「潜在バグ」という言葉のおどろおどろしさに不安になって「徹底的に潜在バグを潰す」というスローガンが生まれたとすれば、それは単なる無思考に過ぎません。

このようなバグをゴキブリに例えてみましょう。ゴキブリがいるかどうかは分かります。ゴキブリを実際に見つければいいのです。死骸でも写真でもいい。とにかくいることは分かります。目の前にゴキブリを見つけながら、いない、というのは要するにウソです。
しかし、ゴキブリがいないことはいつまでたっても分からない。確かに目の前にはいない、でも背後にいるかもしれない。物陰に潜んでいるかもしれない。気が付いていないだけかもしれない。いないことはいつまでたっても証明はできません。だとすれば「バグを0にする」などは不可能なのです。せいぜい「これまでのところゴキブリは見つかっていない」としか言うことはできない。明日ゴキブリが見つかるかもしれないのです。

人間の認識は本来有限なものです。真実はなかなか分からない。それを忘れて簡単に原理主義的な発想に陥るのは、思い上がりに過ぎません。そしてその思い上がりは大抵はよく考えられていない不安から生じるものです。そのような原理主義がプロジェクトを破壊してしまうことは、容易に想像できるのではないでしょうか。

2008年2月12日火曜日

DB2 v8.2 表スペースをDMSに変更する(4)

まだ手順が足りないようです。
select count(*) from RMOBJECTS

1
-----------
SQL0668N 操作は、理由コード "1" のため、表 "RMADMIN.RMOBJECTS
に対して許可されません。 SQLSTATE=57016

というエラーが発生しています。
DB2のInfocenterによると

表がチェック・ペンディング状態にある。 表の保全性が強制されておらず、表の内容が無効である可能性があります。 従属表がチェック・ペンディング状態である場合は、チェック・ペンディング状態でない親表または基本表に対する操作も、このエラーを受け取る可能性があります。

とのことなので、チェックペンディングを解除します。
db2inst1@ホスト名 [20080207_DMS/] $ grep -v ^-- setIntegrity.sql
set integrity for rmadmin.rmobjects immediate checked
db2inst1@ホスト名 [20080207_DMS/] $ db2 -f setIntegrity.sql
DB20000I SQL コマンドが正常に終了しました。

これで、SQL0668Nが出なくなりました。(それにしても自分のやっていることが分かってないのがバレバレですな)

DB2はリストアしたら'何とかペンディング'状態になるので要チェックです。(ほとんどFAQですが)

(2/13追記)
今はアーカイブロギングモードのDBでロードすると、デフォルトではバックアップペンディング状態になってバックアップが強制されるようです。
大量のデータが存在するときは要注意のようです。
参考)DB2逆引きWiki データをロードするには