2011年6月30日木曜日

技術者と英語:「読める」だけで嬉しい三つの事

TOEIC 800点前後というと、レベルBに当たる。

TOEIC が提示している相関表の説明によると、「どんな状況でも適切なコミュニケーションができる素地を備えている」、「日常会話は完全理解」、「業務上も支障なし」とされている。

なんて言っても、実は「話す」「書く」が未経験でも「聴く」「読む」がソコソコできれば、割と取れる点数だったりする。自分もだいぶ前に 800を越えたが、恥ずかしながら全然喋った事なんかない。

つうか話す相手も機会も今のところ一切無いし、従って興味も沸かず努力のしようもないんだけど、ソフトウェア開発技術者としては、「読める」だけで結構役に立つ事がある。

以下の3つ。

◆ 英語でググれる
言うまでもなくインターネットは日本語情報より英語情報の方が圧倒的に多いし、技術情報は特に英語圏発のものが時間的にも先行している事が多い。

英語が読めると、Google の search settings で、English を設定しておいても別に問題ないから、情報量の少ない日本語サイトを避けて効率的に検索できる。

技術系BBSでのディスカッションやトラブルシューティングでも、海外の方が盛んで検索にもヒットしやすい。


◆ 技術系の洋書が読める
最近は、少しだけ改善したようだけど、一昔前の邦訳書は、かなり読みにくいものが多かった。APIリファレンス的なものはまだマシな方だけど、ちょっとユーモアを交えた語り口になったり、思想的に難解な話になったりすると、途端に日本語がおかしくなる。

邦訳書の日本語に対する文句は至る所で聞くけど、最初から原書にしておけば、無駄なイライラをせずにすむ。つうか、無駄な変換レイヤーが無いという点で情報処理の視点からもある意味合理的なわけで、技術者ならなおさら原書を選択すべき。


◆ ソースコードが英語に見えてくる
意外と意識されていない事が多いが、ほとんどの高級プログラミング言語は英語ベースだったりする。

まず単語の面で、言語のキーワードにおいても、各種 API の識別子においても、そのほとんどは英語の語彙、つまり何百年も前からアングロサクソンが日常のコミュニケーションの中で使ってきた言葉で構成されている。

また文法の面でも、意識的にか無意識的にか知らないけど、メソッド呼び出しの文が、「subject.verb(object)」みたいな感じで、だいたい英語の語順に従うようになっていたりする。

なので、上手に書いたプログラムは自然言語としての英語に近くなり、下手なコメントやダイアグラムなんかよりも、書き手の意図を読者に、より効率的、より直截に伝える高い表現力を持つ事ができる。

英語が「読める」ようになるだけでも、人間同士が意思疎通するコトバという観点から見たプログラムの良し悪しがより分かるようになり、ソースの読み書き両面に良い効用が出てくる。

====
ちなみに、逆に英語が全然読めない人の場合、英文としての表現力さえ備えるに至った良質なコードを前にしても、解読を要する意味不明な記号の羅列か、もしかすると ASCII アート的な図形にしか見えていないのでは、と疑わしくなる場面がよくある。

こういう人達って、「ソースを読む」ではなく「ソースを解析する」という言い回しを好む。「ソースが言葉である」なんて事は脳内ですら成立したためしがないから、「読める」コードなんか自分でも書ける訳もなく、必然的に無駄なコメントだらけの残念なコードを書いてしまうことになる。

2011年6月27日月曜日

java の static メソッドの mocking

しばらく .net 案件とかやってた間に、いつの間にか Java でもstatic メソッドに mocking が適用できるようになっていたらしい。

ググってみると、今のところ PowerMockJMockit が、どうやら使えそうな感じ。

前に Java をやっていた頃、mocking には jMock(いわゆる「流れるようなインターフェイス」が何とも素敵な奴だったのだが)を使っていたが、この jMock の制約のせいで java では static メソッドは迂回もすり替えもできないと諦めていた(final 外しはあったが)。

考えてみれば、.net の Mole も TypeMock も instrumentation を使っていたわけだけど、Java だって 5.0 からは instrumentation があったんだから、とっくにできていて不思議はなかった。

しかし、どっちを使えば良いか悩む。

つうか、昔と同じく jMock を使うという選択肢も捨てきれないから、3択になる。いや、PowerMock は EasyMock か Mockito と組み合わせるからもっと選択肢が増える(jMock との組み合わせもできないことないらしいが、ちょっと不安)。

カバレッジツールとの相性とか、微妙な使い勝手の優劣とかもあるだろうし、急いで決めねばならない状況ではあるんだけど、拙速な決断は危ない。

うーん、もっと早く気づいていればなあ…

2011年6月24日金曜日

Fedora 15/Jenkins/Maven3/Subversion/Helios

空きマシンに入れていた Fedora 12を Fedora 15 Lovelock に入れ替えて、自宅環境の筆頭開発マシンに昇格させ、ついでにその他開発ツールもセッティングした。その作業の超ザックリメモ。

====

Helios: Fedora の「ソフトウェアの追加と削除」から GUIでインストール。Fedora 12の時は Galileo だったが、Fedora 15 では Helios (Eclipse 3.6)になっていた。

Maven 3: apache のダウンロードサイトからバイナリを落としてきて適当に展開。README.txtを見ながら適当に設定、"mvn --version" で 3.0.3 が入ったことを確認。

Subversion: これも「ソフトウェアの追加と削除」でインストール。バージョン 1.6.16 が入ってきた。"svnadmin create" で適当にリポジトリを作っておく。差し当たり file://でアクセスするだけなので、これ以上の設定は保留。

m2eclipse: Helios の「新規ソフトウェアのインストール...」で、更新サイトに[このURL]を指定してインストール。

Subclipse: Helios の「新規ソフトウェアのインストール...」で、更新サイトに[このURL]を指定して、Subclipse, Client Adapter, JavaHLを選択してインストール。

Jenkins: [ここ]を見てインストール。"sudo /etc/init.d/jenkins start"で起動し、ブラウザで localhost:8080 を開いて確認。で、とりあえず stop しておく。

テスト用プロジェクト作成:

  • Helios に戻る。
  • maven-archetype-quickstart を指定して新規 Maven Projectを作成。
  • pom.xml で JUnit のバージョンを4.8.2に変更して、依存性を更新。
  • App.java を書き換える
    public class App {
        public String getMessage() {
            return "Hello World!";
        }
    }
  • AppTestを書き換える
    public class AppTest {
       @Test
          public void testGetMessage() {
          String message = new App().getMessage();
          Assert.assertEquals("Good-Bye World!", message);
       }
    }
  • Maven package を実行して、テストでビルドが失敗するのを確認。
  • 上で作ったリポジトリを指定して"プロジェクトの共用"を実行し、適当にファイルを選んでバージョン管理に追加して、敢えてテスト失敗版をコミット。

Jenkins に Jobを追加:

  • "java -jar /usr/lib/jenkins/jenkins.war"で起動する。(init.d/jenkins startだと、file:// で SVNリポジトリにアクセスできなかったので、とりあえず war を直接起動。)
  • ブラウザから開いて、[Manage Jenkins]/[Configure System] から、右のように Maven の設定 → Install Automatically をアンチェックして、上でインストールした Maven のパスをMAVEN_HOMEに指定。
  • Jenkins の[New Job] で [Build a maven2/3 project] を選択。"Source Code Management" で Subversion を選択し、上で作ったリポジトリを指定。

Build 確認:

  • [Build Now] を実行し、テストでビルドが失敗している事を確認。
  • Helios に戻ってテスト・メソッドの"Good-Bye"をHelloに変えて、SVNコミット。
  • 再び[Build Now] でビルド。今度はテストを含むビルドが成功している事を確認。

====

TODO

  • svn+ssh でのリポジトリアクセス
  • サービス化
  • Trac の導入とSVNとの連携
  • SVNコミットをトリガーとする自動ビルド
  • FishEye導入
  • Emma 導入

2011年6月21日火曜日

アジャイルの資格

アジャイル関連の資格について調べてみた。

アジャイルと認定資格って、なんとなく相容れないイメージで、開発者の間でも物議を醸しているらしいが、最近は結構、動きが活発になってきている。

まあ他の資格と同じで、保有しているからといって実務能力があるって事にはならないだろうが、少なくとも会話が成立するレベルの語彙を持っている事の表明くらいにはなると思う。もちろん実践で成功している人なら、我流に偏らないお墨付きの正統な知識も持っているという事になり、鬼に金棒で言う事が無い。

====
◆ Scrum Alliance: 認定スクラムマスタ 等
すでに日本でも取得者が増え始めている、Scrum Alliance の認定資格。

トレーニングコースに参加する必要があり、日本開催時の受講体験記をネットでも散見する。ただ、これが結構高い。ざっと検索したところ、同時通訳付きで20万、通訳なしで15万といったところか。会社の金ではなく個人で受けるとすると、ちょっとした決断が必要になる。

なんか昔のイメージだと、お金を払って2日間の講習に座ってるだけで取れるって感じだったけど、調べてみると、今は一応、インターネット経由の試験で知識を証明する必要があるらしい。

ちなみに Master 以外には、ProductOwner, Developer, Professional, Trainer, Coach といったものがある。


◆ Scrum.org: Professional Scrum Master 等
URLはここ

Professional Scrum Master I (Fundamental) :
トレーニング・コースも提供されているが必須ではなく、自信があればいきなりネット試験を受験してもよく、そこで 85%とれば合格らしい。

Professional Scrum Master II (Intermediate):
これもネット試験だが、小論文もあるらしい。費用もやや高額。

Professional Scrum Developer:
これはコース受講と試験の両方が要るらしいが、ただトレーニング・コースの方は欧米中心の開催。アジアでは今のところインドだけっぽい。

Professional Scrum Product Owner:
これもコース受講とネット試験。


◆ PMI: PMI-Agile Certified Practitioner

先月、Pilot Programが始まったばかりの PMIのアジャイル認定試験

以前、PMP の勉強をしていたとき、使っていた問題集でも参考書でも、道路の延伸工事プロジェクトだとか、小売業の店舗拡大プロジェクトだとか、予想外にソフトウェア開発と離れた設問ばかりで、PMBOKってそう言うものだったのかと、ちょっと驚いた。

でも、同じ PMI の資格でも、PMI-Agile のFAQを見ると、馴染みのあるアジャイル開発の用語ばかりで、ソフトウェア開発者としてちょっとテンションが上がってくる。


◆ ICAgile:
ユースケースで有名なコーバーンとかが創立した、ICAgile なる組織でも、認定試験を提供する計画らしい。

組織自体が発足したてで、試験などはまだまだ形になっていないようだけど、この記事を読む限り、結構おもしろそうではある。

Java のスレッドプール

More Effective C#』の item 11 に、"Use the Thread Pool Instead of Creating Threads"というのがある。new で スレッドを生成するのではなく、.Net で提供されるプールを使えと。

で、昨日『Effective Java (2nd edition)』を調べていると、item 68 で、ThreadPoolExecutor が紹介されていたので、ちょっと試してみた。

====

以下のような状況を想定する。

  • クライアントが Socket をつなぐと、サーバは ServerSocket で accept()して、1から10までの整数乱数を 1000個返す。
  • クライアントはその整数を一つずつ読み取り、幾ばくかの処理時間を要する何らかの処理を行う。ここでは、読み込んだ整数をミリ秒の時間間隔と捉えて、その分だけ sleep() させるようなコードを書く事にした。

クライアントは以下のようなコード。

public class Receiver {

   private static final short LISTEN_PORT = 3434;
   private static final int CORE_SIZE = 1;
   private static final int MAX_SIZE = 100;
   private static final int KEEP_ALIVE = 10;

   private static final ExecutorService pool = 
      new ThreadPoolExecutor(
         CORE_SIZE, MAX_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, 
         new LinkedBlockingQueue());

   public static void main(String[] args) throws IOException {
      Socket socket = new Socket("localhost", LISTEN_PORT);
      DataInputStream in = new DataInputStream(socket.getInputStream());

      long start = System.nanoTime();
      for (int i = 0; i < 1000; ++i) test(in.readInt());
      System.out.println((System.nanoTime() - start) / 1000000000.0);

      in.close();
      socket.close();
  
      pool.shutdown();
   }

   private static void executeTimeConsumingProcess(int interval) {
      try {
         TimeUnit.MILLISECONDS.sleep(interval);
      } catch (Exception e) {}
   }

   private static void test(int interval) throws IOException {
       //ここを書き換えて比較する
   }
}

で、この test() メソッドの中身を変えて、①逐次実行(並列なし)、②スレッドを生成する方式、③スレッドプールを使う方式で比較してみる。(サーバー側は、DataOutputStream で整数を返すようなコードになるが、ほぼ自明なので省略。)

① まず、並列処理をしない逐次実行のパターン

private static void test(int interval) throws IOException {
   executeTimeConsumingProcess(interval);
}
実行時間は、約 5.6秒と出た。平均 5ms の処理が1000回なので、だいたい想定どおりの結果。

② 次に、スレッドを生成するパターン。

private static void test(final int interval) throws IOException {
   new Thread(new Runnable() {
      @Override public void run() {
         executeTimeConsumingProcess(interval);  
      }
   }).start();
}
これは約 0.14秒と出た。当然ながら、逐次実行よりは大幅に速い。

③ 最後に、スレッドプールを使うパターン。

private static void test(final int interval) throws IOException {
   pool.execute(new Runnable() {
      @Override public void run() {
         executeTimeConsumingProcess(interval);
      }
   });
}

結果は約 0.028秒で、スレッド生成の5倍のスピードが得られた。やっぱり、それなりにパフォーマンスは良いらしい。

====

プールの初期サイズ、最大サイズなどを調整すると、若干値が変わってくる。実務で使うときは、実行環境に応じて調整できるような仕組みを作っておくと良いかもしれない。

ThreadPoolExecutor よりさらに手軽に使えるスレッドプールが、Executors の newCashedThreadPool() や newFixedThreadPool() などのメソッドで得られるが、一応試してみたところ、ThreadPoolExecutor を直に使うより、若干遅かった(倍くらいの所要時間)。もちろん、簡易な分、調整は利かない。

2011年6月20日月曜日

Effective Java からperformance 関連項目

以下、『Effective Java (2nd Edition)』 から、パフォーマンスに関係する項目。

:パフォーマンスに特に関係する項目
:パフォーマンスに割と関係する項目
:パフォーマンスに一部だけ関係する項目

Item 1: Consider static factory methods instead of constructors
オブジェクトの生成が抑えられるという static factory methods の利点について、instance-controlled class というキーワードで記述されている。Flyweight パターン(GoF)的に活用するとパフォーマンス向上に効果あり。

Item 5: Avoid creating unnecessary objects
「immutable なオブジェクトは常に再利用しよう」とか、「boxing / unboxing による暗黙のオブジェクト生成に気をつけよう」などといった注意喚起。

Item 6: Eliminate obsolete object references
メモリリーク対策として紹介されているが、オブジェクトへの参照を常に null out することは、無駄に内部品質(すなわちコーディングの生産性)を損なうバッド・プラクティスとされている。null 代入するなら、メモリリークを起こし易い場所に見当を付けて集中的に行うべき。

Item 7: Avoid finalizers
デストラクタ感覚で使うのは止めましょうという話だが、往々にして期待通りに動かないし、そもそも必要性からして疑わしい。さらに深刻なパフォーマンス劣化さえもたらしうるとの記述がある。

Item 9: Always override hashCode() when you override equals
極端な例だが、hashCode()で定数を返したりなんかすると、リニアなアクセス時間になって、本来 のHashXXX の パフォーマンスが得られないって話。

Item 15: Minimize mutability
クラスを定義する際には、できるだけ immutable にして行こうという、今ではかなり普及したプラクティス。パフォーマンスとの関連でいえば、無駄なオブジェクト生成を抑えられるという利点と、値が変わる度に新規インスタンスが必要になるという欠点の、両面があるので注意。

Item 48: Avoid float and double if exact answers are required
業務アプリのプログラマには常識的な話。ただ、あるデータ項目について、どの程度正確な値が求められているか、必ずしも自明ではない。BigDecimal で精度をとるかプリミティブ型でパフォーマンスをとるか、やはり要件によりけり。

Item 49: Prefer primitive types to boxed primitives
Integer や Long などを無自覚に使うと、暗黙の boxing / unboxing で深刻にパフォーマンスが落ちうると言う話。

Item 50: Avoid strings where other types are more appropriate
特にパフォーマンスへの言及は無いが、普通に考えればわかるとおり、本来それ専用の型定義で表現されるべきデータ構造を、下手に文字列なんかで扱ったりすると、内部品質にもパフォーマンスにも悪影響がある。

Item 51: Beware the performance of string concatenation
まあ常識。StringBuilder を使いましょうと。

Item 53: Prefer interfaces to reflection
普通のメソッド呼び出しより reflection の方が遅いという普通の話。

Item 54: Use native methods judiciously
昔と違って JVM が大幅に速くなった今では、JNI経由の native メソッド使用は、期待するほど効果がないという話。

Item 55: Optimize judiciously
「未熟な最適化は諸悪の根源」という Knuth の言葉など、今よりもメモリが小さくCPUが遅くてパフォーマンスが難問だった時代から受け継がれる先人の警句を引用して、やみくもなコード最適化/パフォーマンス・チューニングについて警告している。このブログでも書いてきた(これとかこれ)。

Item 57: Use exceptions only for exceptional conditions
Exception をループ境界の判定などの制御フローに使ったりするのは、本来の使い方から逸脱しているだけでなく、パフォーマンスにも悪影響がある。まあ、そこまでバカな事する人なんか見たことないけど。

Item 66: Synchronize access to shared mutable data
「アトミックなデータならば、パフォーマンス向上のための同期コード省略が可能」という迷信を批判。

Item 67: Avoid excessive synchronization
同期コードによる待ちが増えると、並行性の機会が奪われたりしてパフォーマンスが上がらないという普通の話。synchronize ブロックから alien method を呼ばないとか、synchronize ブロックを小さく留めるとかヒントが書かれているけど、まあ普通はそれほど簡単にいかないので、工夫のしどころではある。

Item 68: Prefer executors and tasks to threads
スレッドプールが標準APIで提供されているので活用すべし。

Item 69: Prefer concurrency utilities to wait and notify
高パフォーマンスな concurrent collection の紹介など、java.util.concurrent パッケージ活用のすすめ。

Item 71: Use lazy initialization judiciously
パフォーマンスに良かれと思って lazy initialization を書いても、大抵は逆効果だから止めておけと。どうしても必要なら、static field には lazy initialization class イディオム、instance field には double-check イディオムを使うこと。

Item 75: Consider using a custom serialized form
デフォルトのシリアライズ形式は、ロジカルなデータ形式に着目したカスタムのシリアライズより、パフォーマンスが悪い事が多い。

2011年6月19日日曜日

CodeComplete からパレートの法則など

リファクタリング』第2章 の「リファクタリングとパフォーマンス」を読んでいたら、Steve McConnell の『Code Complete (下巻)』への参照があった。

以下、「25章 コードチューニング戦略」から抜粋。

パレートの法則:
成果の80%は作業の20%から得られる

Barry Boehm:
20%のルーチンがプログラムの実行時間の80%を消費

Donald Knuth:
プログラムの4%未満がプログラムの実行時間の50%を占める

Jon Bentley:
あるプログラムで、0.5%の行が実行時間の80%を占めていた

Steve McConell:
あるプログラムで、1%未満のコードが実行時間の90%を占めていた

まあ、数字は異なるが言ってる意味は一緒で、要するにパフォーマンスに影響を及ぼしている部分はプログラム中のかなりちっちゃい部分だって事。まあ、アプリケーションのパフォーマンス改善の経験者には、割と常識かと。

「25章 コードチューニング戦略」では、この他に「コードを書くそばから最適化すべきだ」という稚拙な戦略についても、バッサリ斬っている。これは以前の自分のポストでも同じような事を書いた。

また、チューニングを段階的に繰り返して効果を累積させる戦術を、さかんに奨励している。ただし当然ながら、それをやるには、それ相応の内部品質=コード保守性が維持されていなければならない。例えば、パフォーマンスと引換えに内部品質が落ちるような箇所に関して、これを局所化するようなリファクタリングを併用しつつ、コードチューニングを積み重ねるというやり方が必要になると思われる。

あと、コード・チューニングはパフォーマンス改善策の一部であって、実は、アーキテクチャやデータ構造によるパフォーマンスへの影響に比べたら意外と大したことない場合が多いとも言っている。当たり前のことだけど結構大事だと思う。

2011年6月18日土曜日

保守性とイマドキの開発

「ソースを綺麗にしてコードの保守性を高めよう」なんて事は誰もが言うし、「規約を作って静的チェックツールにかけよう」とか、「UnitTest を書いてリファクタリングも奨励しよう」なんて感じで話が進む事もあるけど、いざとなるとグズりだす怪しからんヤツらが実に多い。

スケジュールがきついからとか、コスト面の制約で人が足りないからとか言うのが、お約束のエクスキューズなんだけど、うーん、だからこそなんだがなあ・・・

で、ちょっとコードの保守性について、特に、それが大事なのは何故なのかという点を中心に考えてみる。

====
実はまず、「保守性」ってネーミングが、そもそも良くなかったりする。語の使用の歴史的経緯から見ても、どうしても納品後の保守フェーズに係る品質要素という印象が拭えない。最も適切と思われる内部品質は意外と知られていないし、可読性だと読むだけみたいだし、「綺麗なソース」とか「健康なソース」では情緒的過ぎて説得力に欠ける。

ここで「保守性」を、開発者の素朴な観点から言い換えると、「保守作業の生産性」って事になる。ISO 9126の分類は、論点が違うのでここでは割愛

この生産性が低ければ、当該の保守作業にもその分だけ時間とお金が掛かったりするわけだけど、じゃあ、その保守作業とは何だっただろうか。

再び開発者観点で素朴に考えると、「既存ソースを読んで意図と構造を理解し、追加・書き換え・削除等の編集を施す」、さらに短くは「ソースを読んで編集する」という、それだけの事だとわかる。

で、これを踏まえて言い直すと、「保守作業の生産性」とは「ソースを読んで編集する作業の生産性」という事になる。…が、はて?こんなのは今時、保守フェーズに限定された作業なんかでは全然ない。

極論すれば、6ヶ月の開発期間における最初の10分くらいから、「ソースを読んで編集する作業」は早くも発生し始めるし、反復型の開発なら第二イテレーション以降は大部分がその意味で「保守作業」になる。

「動いてるコードはイジらない」的な昔の開発風景ならともかく、既存コードを編集しながら成長させる現代の開発では、「保守性」はプロジェクト全期間に渡る重大ファクターであり、開発初期から「実装生産性」そのものに漸近していく。

なわけだから、正しくは「スケジュールがキツいし人も足りない」-> 「ならば生産性を上げよう」-> 「つまりソースを改善して保守性を上げよう」と考えなければならない。これの逆をやってるから、各反復で負のスパイラルを誘発し、ドツボにハマっていく。

====
というのが、コード保守性に関する不見識への、差し当たりの抗議。

「そもそも保守性の高いコードとは何か」と、「保守性向上作業(リファクタ等)の工数はどうしてくれるんだ」という2つの事項についても説明が必要だが、別の機会にする。

まあ現場では、こうして理詰めで説明しても、心理的・組織的圧力で生産性を考慮しないままなし崩しに開発が進んでいってしまう事は往々にしてあるんだよなあ。

2011年6月17日金曜日

コード最適化の個人的三原則

最近、アプリケーションのパフォーマンス・チューニングやコード最適化ついて、自分の考えを開陳する機会が何度かあった。

足し合わせて要約すると、以下のような感じになる。

◆コードを直す前後に計る
コード最適化に当たっては、修正を加えるに先立って、まず現時点の速度やその他のパフォーマンス指標を計測し、プロファイラ等を用いてボトルネックを突き止める。しかる後に、該当コードをピンポイントで修正し、それから再度パフォーマンスを計り直し、修正前と比べて効果を確認する(効果が無ければ元に戻す)。

…というのが、まあ、常識的な流れだが、実際の現場では意外とちゃんとできていない。

場当たり的な思いつきのパフォーマンス改善策を、システム全体のソースコードにバラまいて内部品質を下げてしまい、パフォーマンスチューニング改善作業そのものを停滞させてしまう場面がかなりよくある(これはまた、別の機会に詳述したい)。

カンに頼ったやみくもな対処は止めて、とにかく実測で把握する事がまずは必須。

◆数値目標を立てる
まあパフォーマンス向上というか、ほとんど何にでも一般的に当てはまる仕事の基本とも言える事だから、まあ、あまり説明がいらない気がする。

ところが、実際の現場では「とにかく速く」なんてお題目の下で作業が行われる事が結構多い。

こういうのは、要するに精神論・根性論みたいなものだから、そういったスローガンのもとでの作業は、無駄にプレッシャーが増加するだけで結局はあまりうまく行かない。

効果の薄い小手先の対応をせいぜいガンバッテみたりはするけど、具体的な目標を立てておけば決断できたはずの本来的な抜本対策が、なんとなく見過ごされたり、あるいは見てみぬふりをされたりする。(目標の立て方にもいろいろあるが、詳しく立ち入るのは別の機会にする。)

具体的な測定可能な目標を立てることが肝要。

◆最初から継続的に計る
これも「何事も早めに確認して早めに対応するのがよろしい」という、パフォーマンス話に限らず人の営みのほとんど全域に当てはまりそうなプラクティス。

まあ、リスク管理の基本中の基本でもあるはずなんだけど、なぜかシステム開発に限ってはなおざりにされている。かなり多くのプロジェクトで、パフォーマンスに関するテストや最適化を開発期間の一番最後にもってきて、関係者一同、毎度頭を抱えることになる。

開発初期から、継続的に計測する仕組みを作って監視するのが大切。

====
という3点について、自分としては、かなり当たり前の言わずもがなの事だと感じているが、いろんな反応があった。単なる同意だったり、なるほどと感心されたり、わかってはいるけどウチじゃ難しいんだよなって感じの苦笑いだったり。

まあ、各現場でそれぞれ文化や事情があるからなあ。

2011年6月13日月曜日

Scrum + XP

国内外のいくつかのIT技術系サイトから、RSS 経由で配信される記事をいつも読んでいるけど、この数年、Agile系記事の中での XP 関連の話題がめっきり少ない。

と言っても XP が陳腐化したのではなくて、XP のプラクティス群が、その根底にあった価値観と一緒に既に広く普及し、実績を上げ、評価を確立し、換骨奪胎した形でこの業界に取り込まれて、いまさら extreme (究極)なんてキーワードで語られる必要が無くなったって事なのだと思う。

もちろん、XP の導入に挫折した残念な技術者たちが、プロジェクトの失敗の原因を自分たちの力不足ではなく、XP それ自体に帰結させて、「やっぱ、XP なんかダメだね」なんて言ってるような、底辺な現場も少なくないだろうけど。


で、少なくなった XP の話題の代わりに、スクラム関連の記事が多くなってきていて、たまに Lean と Kanban の話題が上がってくる以外は、ほとんどスクラムの話ばかりになっている。

また、ちょうど一年くらい前に、マイクロソフトのプリセールス技術者から、Team Foundation Server の導入事例について話を伺う機会があったが、そこでも開発プロセスは Scrum を採用していると聞いた。

そんな昨今だけど、XP でアジャイルに入ってきた人が、スクラム導入のために XP のプラクティスを捨てたり、XP的の価値観を矯正したりする必要はほとんどない模様。

XP はプラクティス に着目していて、一方、スクラムは組織と管理に着目していて、両者の対象範囲は意外と重ならなかったりする。重なっている部分も無くはないが、同じ事を言っているだけだったりする。

どうやら、スクラムという枠組み=フレームワークの中で、XP のプラクティスを実践していくというのが、正しい方向なのだと思う。というか今にして思うと、それぞれを単体で導入・実施するより、スクラム+XP の組み合わせの方が、大抵の開発現場ではむしろ摩擦が少なかったような気がする。

2011年6月12日日曜日

このギャラ、損か得か?

開発案件のギャラを比べるとき、単純にどっちが得でどっちが損か比較するのは意外と難しかったりする。

難しい理由は、案件の内容だとか契約条件だとかいろいろあるが、今日は、契約条件に含まれる清算方式の違いを解消して比較する方法を考えてみる。

例1)
見込み稼働時間数が 月240h の半炎上プロジェクトで、140h〜180h上下割60万の条件がついたとする。これと等価な単金固定プロジェクトのギャラはいくらか?
答え:
60万 + 60万 ÷ 180h ×(240h−180h) = 80万
言い換えると、これまで 140h〜180h上下割60万で仕事を受けてきた技術者が、見込み月240h の単金固定案件を受けるとすると、80万もらわないと割に合わないということになる。


例2)
単金固定72万の案件の引き合いがあって、プロジェクト内容を聴いたらとっくにデスマ化していて稼働300hに達している。これを140h〜200h上下割に換算するといくらになるか。
答え:
x + x ÷ 200h ×(300h−200h) = 72万
これを解いて、x = 48万。
このご時世、72万なら悪くないと思うかもしれないが、稼働時間によっては、上下割換算で40万台のしょっぱい案件になり得るとわかる。(ちなみに、この例を上限180hで換算すると43万2千円)


比較用に標準的な条件を設定して、複数案件を比較するという手もある。例えば「162h の時給換算」とか。(行政機関の年間営業日243日を採用し、これを12ヶ月で割ると、一月あたりの勤務日数は20.25日となり、これに8時間を書けると162時間となる。)

例3)
他の条件(通勤、やりがい等)が同じとすると、得なのはどれか。
① 60万単金固定 (想定220h)
② 57万(140-200) (想定220h)
③ 55万(140-180) (想定220h)
④ 54万(140-180) (想定200h)
答え:
① 60万 ÷ 220h × 162h =44.1818万

② 57万 × (1 + 20h/200h)=62.7万
  62.7万 ÷ 220h × 162h =46.17万

③ 55万 × (1 + 40h/180h)=67.2222万
  67.2222万 ÷ 220h × 162h =49.5万

④ 54万 × (1 + 20h/180h)=60万
  60万 ÷ 200h × 162h =48.6万

得な順に、③→④→②→① となる。

①、②、③は清算方式の違いによって、見掛け上の単金と受け取り額の順序が逆転し、従って損得も逆転する。
①と④では、受け取り額が同じだけど、稼働時間の違いにより、損得が逆転する。

====
こんな感じで、案件を比較できる。

(理想的には、8時間働いた後に追加で働く1時間と、12時間働いた後に追加で働く1時間では、同じ1時間でも価値が違うので、これを考慮する変換方式にする必要があるが、かなり複雑になる。)

換算方式を工夫すれば、他の要素も取り込んで標準化して比較できる。特に、通勤にかかる時間は、実は交通費以上に結構大きく影響する。往復30分と3時間では、実質的に失う時間が大きく違うので、比較するには一工夫必要になる。

2011年6月11日土曜日

なぜ UnitTestが生産性を上げるか

前回、UnitTest の必要性について書き始めて、どうやらコーディングの生産性向上らしいってとこまで来た。今回はその続き。

====
UnitTest はコーディングの生産性を高める。

・・・というのは少し大雑把で、より正確には、高い生産性を得るために最も重要な条件の成立に、UnitTest が不可欠であると言う事になる。

で、その条件とはすなわち「内部品質が高い」と言う事である。

この内部品質を高める事により、「ソースを読む」、「書き足す」、「書き直す」、「間違いを探す」と言った、コーディングの大部分を構成する作業に要する時間を大幅に削減できる。内部品質が高まるほど、コーディングの所要時間が下がり、生産性が上がってくる。

では、なぜ UnitTest が内部品質の向上に寄与するのか?

これは、UnitTest が内部品質向上作業の生産性を高めるからなのだけど、この「内部品質向上作業」とは、ザックリ言うとリファクタリングの事である。

このリファクタリングを徹底的に(Mercilessly)に行う事により、高度な生産性を実現できるような、高いㇾベルの内部品質に初めて到達できる。ここで UnitTest は、リファクタリング作業それ自体の生産性を高めるために(同時にリスクを下げるために)利用される。

これ以上の説明は不要な気がしないでもないが、なぜ UnitTest によってリファクタリング作業の効率性が高まるかというと、コード修正による「意図せざる事象」をもっとも効率よく検出できるからと言える。
前にも書いたが、UnitTest とは「doing the right things(正しい動作をしている事)」ではなく、「"doing things right"(正しく動作している事)」を検証するものである。ここで「正しく」とは、「意図した通り」という意味なわけで、つまり UnitTestとは「意図せざる事象」を発見する仕組みという事になる。

リファクタリングという作業は、極論すれば仕様なんて知らなくても良いとうホワイトボックスの中身だけに着目した作業と言える。また、プログラムの成長に伴い、随時(常時)、実施していくものなので、必然的に反復作業となる。

従って、外観の振る舞いを変えずにソースコード修正を繰り返すリファクタリングという作業に当たっては、自動化ホワイトボックステストである UnitTest が最適なプラクティスということになる。FunctionalTest でも結果的に「意図しない動作」を検出できないでもないが、直接性において UnitTest に比べて劣る。

ちなみに、そもそも JUnit を用いた UnitTest が広く一般に認知されたのは、ファウラーのリファクタ本で紹介されたのが契機だったりするので、歴史的にも UnitTest は リファクタリング と密接に関連付けられたプラクティスだと言える。(JUnit を使う事が UnitTest であるとは必ずしも言えないが)

以上、コーディング生産性向上と UnitTest の関係についてだいたい説明したが、振り返ってまとめると、以下のようになる。
UnitTest を充分書く

リファクタリングの生産性が高まる

内部品質が向上する

短い時間で正しいコーディングが簡単にできるようになってくる


これは、以下のように逆に書くこともできる

UnitTest を書かない

リファクタリングできない

内部品質が低下する

頭をかきむしりながら長時間かけてるのにバギーなコード


====
実際の現場では、逆に書いた版のプロジェクトが、未だに多いんだよなあ。

UnitTest をしないからリファクタできないパターンと、リファクタリングをしない(或いはそもそも知らない)から、UnitTestの必要性が分からないってパターンの両方考えられるが、どっちにしても低レベルすぎる。

といったわけで、内部品質の劣悪なソースコードと UnitTest の不履行との間には高い相関があるわけだけど、内部品質だけじゃなく外部品質、つまりバグやらパフォーマンス劣化やらにも巡り巡って因果関係が及んでいたりする。
そのうちこれも書こうと思う。

なぜ UnitTest が必要か

以前、2種類の品質、製品品質とコード品質について対比した。
また FunctionalTest と UnitTest についても違いを比べた。

前回は、製品品質の保証のためには、FunctionalTest が必要であることを説明したが、それらを踏まえて、今回は UnitTest の必要性とは何かを、考えてみる。

====
UnitTest を書く理由、得られる効用について考えると、テストって語が含まれてるぐらいだから、品質向上のためじゃなかろうかと最初は思う。

でも違う。仕様通りに動くかどうかといった製品品質(外部品質)の検証なら、前回書いたように、UnitTest ではなく FunctionalTest の出番になる。また製品品質ではなくソースコード品質(内部品質)の検証だとしても、静的チェックツールがふさわしい。どちらの意味の品質についても、UnitTest の出番ではない。

品質じゃないとしたら、何だろう。UnitTest といえば、自動化テストなわけだから、つまり生産性向上?

うん、生産性の向上である事は間違ってなさそうだけど、でも何の生産性か。ソフトウェア開発にはいろんな作業が含まれるが・・・

ならば、直訳で「単体テスト」だから、単体テストの生産性の向上?

うーん、いわゆる「単体テスト」、つまり旧来のウォーターフォール・モデルで言うような、「一個のモジュールが、後工程の結合テストを実施するに足る品質を満たしているかどうか検証する作業」という意味なら、やっぱり違ってるだろう。

「単体→結合」の観点で見た場合に、そこで単体に要求されている品質は、単体レベルの機能要求がブラックボックス的にで満たされていればそれで良い訳で、これを検証するのは FunctionalTest という事になる。繰り返しになるが、UnitTest は品質(機能・非機能要求の充足)、つまり"doing the right things"は検証しない

単体テストじゃないし、設計でもなさそうだし、そんじゃ、コーディングの生産性か・・・

まあ、結局そういうことになる。

がしかし、そもそもテストを書くという追加作業をしてるのに、生産性が上がるのはなぜだろう。それにコーディングといってもいろいろあるし、どの局面の何のコーディングだろう。

====
長くなったので、次に続く

2011年6月8日水曜日

FunctionalTest の必要性

以前、 UnitTest と FunctionalTest と の違いについて、「コードが正しく書かれている事」のテストと、「コードが正しく動いている事」のテストとして対比した。

今日は、前者の FunctionalTest について例を上げて説明し、その必要性について考えてみる。

例えば、こんな問いがあるとする。

数字を受け取って平方根を返すプログラムを書いた。
ところが、平方根ではなくて二乗が返されるというバグが出た。
さて、どんなテストを書いておけば、これを防げたか。

最近 xUnit や TDD を始めたばかりの人なら、「xUnit で UnitTest を書いてカバレッジを 100%にしておけばよかったんじゃない?」なんて考えるかも知れない。

だけど、xUnit の実践経験がそこそこある人なら、それじゃ十分ではないとすぐ分かると思う。

これが単なるコーディングミス、つまり「正しく仕様を理解した上でのコードの書き間違い」によってのみ起因するバグならば、例えば 4 から 2が得られる事を確かめるアサーションがテストコード中にあるはずなので、誤りはすぐに検出される。この場合は、上の答えでもあながち間違いではない。

ところが、同じバグは単なるコーディングミスによるのではなく、「誤った仕様理解の下での意図通りのコーディング」によっても生じてくる。

例えば何かの拍子で、プログラマが平方根と二乗を勘違いして取り違えていた場合、本体コードに二乗の計算が書かれるのみならず、テストコードにも 4 から16を返すアサーションが書かれてしまうので、バグは検出されない。

平方根と二乗の勘違いなんて何だか馬鹿馬鹿しい例だが、実際のシステム開発のドメイン(問題領域)は、普通はもっと複雑だから、こうした認識ズレがバグの原因に占める割合は非常に多い。(また、上のような概念Aと概念Bの取り違えの他にも、仕様の見落としや失念、変更の連絡ミスなど、様々な事が認識ズレの原因になる。)

一般に、「要求されている仕様」と「実装者の認識」のズレから生じたバグは、テストコードにも同じ誤認を織り込んで書かれてしまっているため、本体のコーディングと連動して生産された UnitTest によっては検出できない。

この種のバクを防ぐためには、仕様を理解し責任を持っている人が作業に介在しなければならない。

仕様担当者がプログラマが書いたテスト仕様書をレビューしたり、仕様担当者自身がテスト仕様書を作ったりといった形で、「わかってる人」の意識をくぐり抜ける必要がある。上の例では、「平方根を返す」という要件を定義した人が、テスト仕様書に「4 ならば 2」という「条件 ---> 結果」のペアが含まれている事を確認していなければならない。

つまり最初の問いの答案としては、仕様が分かっている人にレビューされた仕様ベースのテスト、つまり FunctionalTest が必要ということになる。

(ちなみに、テスト仕様書の作成はコーディング前でも良いし、またテスト実施に自動化ツールを使ってもいい。ただし、ソースコードベースの DeveloperTestとは視点が全然違うことに注意する必要がある。例えば、網羅率はコードカバレッジではなく、仕様から起こしたテスト項目の実施率となる。)

====
といったような事は実はかなり基本的な事項なんだけど、かなり多くの底辺現場では理解されていない。で、「コードカバレッジが100%に近づいているのにバグが減らない。って事は、xUnitや TDD は役に立たないって事になるんじゃね?」なんて、アホな論理がまかり通るようになる。やれやれ…