だから、逆流させるんだ。
前回の記事で言ったように、ソフトウェア開発って言うのは問題発見とその解決で、
組織戦略と同等レベルの知的作業なんだ。
そして、ソフトウェア会社というのは、この2つが密接に絡み合う。
ソフトウェアアーキテクチャはそのまま組織戦略だといっても過言じゃない。
それだけソフトウェアアーキテクチャは重要なものなんだ。
だから、アーキテクトは必要充分な情報を得る必要がある。
そして、各ステークホルダーに対しての問題を再定義して解決する必要がある。
わかってる。多くの場合、それはうまくいかない。
アジャイル開発プロセスほどに新進気鋭な(といってももう十分な歴史があるが)界隈ですら、
恐れて言わないようなことだ。
何もかも管理したいか、
何も管理しないかのいずれかしか手段を知らないような一般のマネージメント層やスーツの連中に
それが理解できる訳が無いんだ。
そういったわけで、アーキテクチャもシステムも新しい物好きの宇宙飛行士によってぐちゃぐちゃにされたり、
人間嫌いの破滅主義者によって未定義エラーを吐き続けることになるんだ。
そして、その理由をマネージメントにおっかぶせて、安い酒の肴にするんだ。
そんなわけで、システムを知らないマネージメントと要求と対話しないアーキテクトによって、
とても信頼のできる法則が生まれることとなった。
それは「コンウェイの法則」だ。
「システムを設計する組織は、その構造をそっくりまねた構造の設計を生み出してしまう」
問題意識を一度でも持った人にはあまりにも有名で、最低限度の教養なんだが、
問題意識を持ったことが無い(得てして最も知っていなければならない)人には全くの無名なこの法則は、
半世紀近くたった今でも有効でむしろ、世界中で実験的に実証され続けている。
じゃあ、何もかも捨てて逃げ出すのか。
そうじゃない。この法則は信頼できる。だからこそ使える手段があるんだ。
この法則を逆流させるんだ。
つまり、
「アーキテクチャ設計が、組織構造を再設計する」
ということだ。
そんな馬鹿なことがあるか。と読んでいる人は言うかもしれない。
でも、馬鹿げているのは組織構成がアーキテクチャに反映されるということも同様で、
それだけこの2つが相互作用を起こすという事実が捉えがたいことだということだ。
これのざっくりとした機序を説明するなら、(屁理屈なのはお互い様だ)
アーキテクチャは、ソフトウェアプロダクトの拡張に対してコストコントロールをするものだ。
組織構造は、コストに強く反応する。20のアウトプットを得るのに100の資源が必要なものは採用されないが、
10の資源で済むものであれば鈍重ながらもそれを採用するインセンティブが働くからだ。
これは要するにクーデターだ。「射撃しつつ前進」なんて甘っちょろいものじゃない。
気づかないうちに、コンウェイの法則を逆流させて、ソフトウェアアーキテクチャが、組織構造・思考をも支配し始めるんだ。
わくわくしない?
このクーデターがうまくいく条件は、しつこいけどソフトウェアによる「問題の再定義と解決」だ。
だから、望んでいるものが全く違っていれば話にならない。
問題が解決された暁には、もしかしたら、マネージメントもその魔法に驚いて何が起きたのかということを不思議がるかもしれない。
そのとき、ようやく組織とソフトウェアの関係に気がつくかもしれない。
で、次に君はこう言う。
「ご高説はわかった。
だけど、今のアーキテクトには、組織の問題もソフトウェアの展望もリードできるような能力は無いし、
そもそも誰も問題の所在なんてわかってないんだ。」
なるほど、そうなると選択肢は決まってくる。
自分がアーキテクトになるか、
アーキテクトになれる誰かに問題を理解するための情報を集める人になるか、
その誰かが生まれてくるように仕掛けをつくるか。
あるとしても、この3つくらいだよね。
おっともう一つあった。
この全部をやることだ。
アジャイルプロセスの「よさ」は何に由来するか。
アジャイルソフトウェア開発というものの「よさ」を慎重にとらえる必要があるだろう。
わかりやすい表現としては、ウォーターフォールなプロセスに対して、アジャイルソフトウェア開発が優位としているところは、「計画単位の細分化による適応性」だとしている。
これは計画単位が大きいと、不確定な要素によって出戻りや品質の低下が起こってしまうからだ。
この論旨はとてもわかりやすいのかもしれない。
しかし、どうにも歯切れが悪いように感じる。いや、ある種の人々にとっては我が意を得たりといったところなのであろうが、
それはただ単にその人が陥っている状態に対して、アンチテーゼ的であり、そしてそのワードがはやることによる社会的効果に期待してのところだろう。
だから、はじめに断っておくが、アジャイルソフトウェア開発プロセスに関して異論を唱えたいのではない。むしろ、それはすばらしいことだ。と思っている。
ただ、ある種の「よさ」はソフトウェア開発の「本質」を表現していないのではないか、と違和感を感じてしまうのだ。本質にまわりをかすみとりながら、狡猾にそれを覆い隠してしまうもののように感じるからだ。
そして、その「本質」に近づくことは危ういととらえているからだ。本当は理解しているのに、そこに踏み込むことによって自分たちもまた殺されてしまうのをさけるために。
では、ソフトウェア開発の本質とは何なのか。
それは「問題の発見と解決」だ。
顧客ないし社会に内在する問題の発見と解決。それがソフトウェア開発だ。
そして、ソフトウェアエンジニアは、問題の発見と解決を行う知的労働者であって、それをなすことが職責であるということだ。
なので、「どう作るか」は「なぜ創るか」に強く依存している。一体として「なぜそう創るか」が最も重要なこととなる。
既に存在しているソフトウェアを創ることは、大きな意味は無い。すでに解決されている問題を再度同じ方法で解決することにもはや価値はないからだ。
「どう作るか」は製造業的あり方だ。同じものを効率よく制作するものの考え方だ。アジャイルソフトウェア開発プロセス論は強くソフトウェアの特殊性を主張していながら、
トヨタ改善方式やセル生産方式と同様とのメタファーを採用しているように思える。ここに違和感の源泉がある。
企業、社会において、ソフトウェア開発に近いものに「戦略」がある。
国家戦略、人材戦略、販売販促戦略、流通戦略、商品戦略、などなど、ありとあらゆる戦略が企業において必要とされている。
これもまた、問題の発見と解決だ。ある種の「ソフトウェア」を企業にあるいは社会に敷衍させることによって問題を解決しようとしている。
つまり、ソフトウェア開発プロセスというものは、戦略構築のプロセスと対比してとらえられるべきものであって
製造プロセスと対比してとらえるものではない。
だから、アジャイルソフトウェア開発プロセスがより価値を生む理由は、製造業的なメタファーによって表現されてはならない。
それが価値を生む理由は計画者と実行者が同一人物で、短いサイクルによって問題発見が促され、その問題がチームによって共同化しているからだ。
何故創り、どう創り、どう敷衍していくのかのすべてに対して、一貫性、主体性、自主性がソフトウェアの品質を決定する。
つまり、くりかえしになるが「どのような問題かを認識し、どのように解決するか。」がソフトウェアのすべてだ。
よいソフトウェアとは、「複雑に見えた問題をシンプルに捉え」「スマートに解決している」ものだ。
ここにソフトウェア開発者の生産性が10倍から100倍以上の開きを生む理由がある。
それは経営者が生み出す富に10倍から100倍以上の開きを生む理由と同じだ。
よいソフトウェア開発プロセスとは、それを促すものであって、決まりきったものを短期に作ることを意味しない。
このことをソフトウェア開発に従事する人は強く主張するべきなのだ。
そしてのその主張の責任として知的な生産によって証明してみせることなのだ。
だが、それはとても難しい。だから、そこを幻想のままにしておきたいと思っているひとびとがいるのも確かだ。
本当は理解しているのに、そこに踏み込むことによって自分たちもまた殺されてしまうのをさけるために。
Data::Visitor::Liteなるものを書いてみた
https://github.com/hirokidaichi/p5-Data-Visitor-Lite
Data-Generatorはshipitするときに名前が重複していたので、
Data-Enumeratorになりました。
こいつは、Data::Visitor::Callbackに依存していたんですが、D::V::CはMooseに依存していて
いらついたので、簡易版をつくりました。
Data-Generatorで非決定性計算(修正版)
とりあえず、問題も何も書かなかったので修正。
まったく最適化をしていないので、結構試行回数が多いけど、宣言的に書ける
use strict; use warnings; use Test::More; use List::MoreUtils qw/uniq/; use Data::Generator qw/pattern generator/; use Data::Dump qw/pp/; =pod Baker, Cooper, Fletcher, MillerとSmithは五階建てアパートの異なる階に住んでいる。 Bakerは最上階に住むのではない。Cooperは最下階に住むのではない。 Fletcherは最上階にも最下階にも住むのではない。 MillerはCooperより上の階に住んでいる。 SmithはFletcherの隣の階に住むのではない。 FletcherはCooperの隣の階に住むのではない。 それぞれはどの階に住んでいるか。 =cut my $try = generator( { Baker => pattern( 1 .. 5 ), Cooper => pattern( 1 .. 5 ), Fletcher => pattern( 1 .. 5 ), Miller => pattern( 1 .. 5 ), Smith => pattern( 1 .. 5 ), }) ->where( # 全員が異なる階に済んでいる sub { is_uniq( values %{ $_[0] } ); } ) ->where( # Bakerは最上階でない sub { not( $_[0]->{Baker} == 5 ); } ) ->where( # Cooperは最下階でない sub { not( $_[0]->{Cooper} == 1 ) } ) ->where( # Fletcherは最上階にも最下階にも住むのではない。 sub { not( ( $_[0]->{Fletcher} == 1 ) or ( $_[0]->{Fletcher} == 5 ) ); }) ->where( # MillerはCooperより上の階に住んでいる。 sub {( $_[0]->{Miller} > $_[0]->{Cooper} )}) ->where( # SmithはFletcherの隣の階に住むのではない。 sub { not ( abs( $_[0]->{Smith} - $_[0]->{Fletcher} ) == 1 );}) ->where( # FletcherはCooperの隣の階に住むのではない。 sub { not ( abs( $_[0]->{Fletcher} - $_[0]->{Cooper} ) == 1 );}); sub is_uniq { my (@list) = @_; return ( scalar( uniq(@list) ) == scalar @list ); } is_deeply( $try->list,{ Baker => 3, Cooper => 2, Fletcher => 4, Miller => 5, Smith => 1 });
Data-Generatorで非決定性計算
use strict; use warnings; use Test::More; use List::MoreUtils qw/uniq/; use Data::Generator qw/pattern generator/; use Data::Dump qw/pp/; my $try = generator( { Baker => pattern( 1 .. 5 ), Cooper => pattern( 1 .. 5 ), Fletcher => pattern( 1 .. 5 ), Miller => pattern( 1 .. 5 ), Smith => pattern( 1 .. 5 ), } )->where( sub { my $value = shift; return if ( $value->{Baker} == 5 ); return 1; } )->where( sub { my $value = shift; return if ( $value->{Cooper} == 1 ); return 1; } )->where( sub { my $value = shift; return if ( $value->{Fletcher} == 1 ); return if ( $value->{Fletcher} == 5 ); return 1; } )->where( sub { my $value = shift; return unless ( $value->{Miller} > $value->{Cooper} ); return 1; } )->where( sub { my $value = shift; return if ( abs( $value->{Smith} - $value->{Fletcher} ) == 1 ); return 1; } )->where( sub { my $value = shift; return if ( abs( $value->{Fletcher} - $value->{Cooper} ) == 1 ); return 1; } )->where(sub{ my $value = shift; my @uv = uniq values %$value; return ( scalar @uv == 5 ); })->select( sub { my $value = shift; my %value = %$value; [ @value{qw/Baker Cooper Fletcher Miller Smith/} ]; } ); is_deeply( $try->list,[3,2,4,5,1]); ::done_testing;
Data-Generatorっていうの書いてみてる
そんなたいしたもんじゃないんだけど、網羅的なtestを書くときなどに使えるといいなと。
https://github.com/hirokidaichi/p5-Data-Generator
use Data::Generator qw/pattern generator/; my $cases = generator({ hoge => pattern(qw/a b c/), fuga => pattern(qw/x y z/), fixed => 0 }); for my $case ( $cases->list ){ print pp($case); } # { hoge => 'a',fuga => 'x'} # { hoge => 'a',fuga => 'y'} # { hoge => 'a',fuga => 'z'} # { hoge => 'b',fuga => 'x'} # { hoge => 'b',fuga => 'y'} # { hoge => 'b',fuga => 'z'} # { hoge => 'c',fuga => 'x'} # { hoge => 'c',fuga => 'y'} # { hoge => 'c',fuga => 'z'}
直行したパターンを生成するときとかに使う。
my $cases = generator({ hoge => pattern(qw/a b c/), fuga => pattern(qw/a b c/), fixed => 0 })->where(sub{ $_[0]->[0] ne $_[0]->[1]})->limit(0,3); for my $case ( $cases->list ){ print pp($case); } # { hoge => 'a',fuga => 'b'} # { hoge => 'a',fuga => 'c'} # { hoge => 'b',fuga => 'a'}
みたいにwhere句とかlimit句とかも使える。
テスト
本当の問題はとてもシンプルだ。
問題を複雑にするのは、人の中にある変化に対する恐れだ。