読者です 読者をやめる 読者になる 読者になる

JJUG CCC 2016 FallでBlue-Green Deploymentの導入事例について登壇します。

2016/12/3にベルサール新宿グランド コンファレンスセンターで開催されるJJUG CCC 2016 fallで、10:00からG+H会場で「実録Blue-Green Deployment 導入記」で登壇します。

www.java-users.jp

JJUG CCC 2014 Fallのセッション「私がTDDできないのはどう考えてもお前らが悪い!~エンタープライズJava開発でのTDD適用の勘所~」で事例として取り上げたサービスのその後についてです。ユーザーの拡大に伴いサービス無停止でのリリースに取り組む事になり、TDDの推進をミッションとしてプロジェクトに参画した発表者はデプロイの自動化、そしてインフラ運用を担当するようになりました。

疎結合アーキテクチャーのシステムを、サービス無停止でリリースできるように作り替える上でサブシステム相互の連携をどう作り込むのか、DNSロードバランサーの挙動にあわせて稼働系システムの連携をどう作り込むか、そしてその中でテストの自動化がどう役割を果たすか。最終的にBlue-Green Deploymentになるまで1年以上に及び、今なお改善をつづけているインフラ運用の実態について発表します。

構成は「GSLB(Global Site Load Balancing)によるサイト切替」「Blue-Green Deploymentにおけるデータベースの扱い」「Blue-Green Deploymentにおける実装及び運用上の留意点」になる予定です。朝早い時間帯ですが、Blue-Green Deploymentの導入について興味がある方は是非お越しください。

同時上映のid:syobochim さんと @_Dr_ASAさんによる「SIerもはじめる、わたしたちのDevOps」もお楽しみに!

Bambooとdocker-composeの連携について

このエントリーは、Atlassian Advent Calendar 2015 - Qiitaの7日目のエントリーです。

docker-composeによるCI環境上でのコンテナー間の連携

昨日のAtlassian Community Day 2015 | Peatixで、弊社グロースエクスパートナーズより、「Atlassian Summit 2015でのDevOps関連の取り組みについて」というテーマで講演致しましたが、その中で取り上げたdocker-composeの連携のデモについてこのエントリーでは紹介します。

CI界隈では「ポータブルなビルド」という言い方がされます、これはビルドをするために必要な環境の情報をDocker等を使用して構成管理の下に置くことで、ビルドの環境をBambooやJenkinsなどのCIサーバー上の環境とは疎結合にして、ビルドの独立性、安定性、スケーラービリティーを得ることを目的にしています。

コンテナー上でビルドを行う際に、ビルドのプロセスが単独で完結していればいいのですが、データベースなどの外部のサーバープロセスとの連携が必要になる場合もあります。そのための方法はいくつかありますが、ここではdokcer-composeを用いてコンテナ間の連携を行います。

このデモはAtlassian SummitのSummit 2015 - Docker, Continuous Integration, and You | Atlassianというセッションで紹介されたものの改良版で、外部コンテナーとしてPostgreSQLとElasticsearchを起動して連携しています。

デモレポジトリについて

デモに使ったレポジトリFork元に比べて

  • DockerのPostgreSQLイメージスキーマ初期化の仕様がかわったのでそれの対応
  • デモが作成された当時はdocker-composeがDockerに統合されておらずfigだったため、fig.ymldocker-compose.ymlにリネーム
  • 依存ライブラリを毎回ダウンロードしていてビルドに時間がかかるので、ローカル環境でキャッシュするようにした

という点に手を加えています。

Bambooの設定について

Bambooは以下のような構成になっています。

f:id:setoazusa:20151207124052p:plain:w200

docker-compposeのセットアップ

まずGitからコードを取得した後の、最初のステップはdocker-composeのセットアップになります。予めBambooサーバー上にdocker-composeがセットアップされている場合は不要です。

f:id:setoazusa:20151207133538p:plain

curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-Linux-x86_64 > docker-compose

chmod +x docker-compose

ディレクトリの初期化

次のステップは依存性をキャッシュするディレクトリの初期化になります。 f:id:setoazusa:20151207124221p:plain:w300

mkdir -p target/.m2
mkdir -p target/.lein

docker-composeによるビルド

f:id:setoazusa:20151207124324p:plain:w300

set -x

sudo ./docker-compose kill 
sudo ./docker-compose rm --force
sudo ./docker-compose build
sudo ./docker-compose up

この中では、docker-compose.ymlの設定にしたがってelasticsearchPostgreSQLのイメージを起動し、次にDockerfileに従ってアプリケーションのビルドを行っています。この際に、EXPOSEしているポートがオープンになるのとサーバーのプロセスがreadyになるのにタイムタグがあってたまにビルドがこけるので、ポートをポーリングするスクリプトを使ってコンテナの中のポートが立ち上がるのを待つようにしています。

ビルド結果の取得

f:id:setoazusa:20151207125734p:plain:w300

CID=`sudo ./docker-compose ps -q transfer`

sudo docker cp ${CID}:/code/testreports.xml .

sudo chown bamboo:bamboo testreports.xml

sudo docker cp  ${CID}:/home/transfer/.m2 target/

sudo docker cp  ${CID}:/home/transfer/.lein target/


sudo chown -R bamboo:bamboo target/

後処理としてDockerからテスト結果のxml(JUnit形式)ならびに依存性キャッシュをコンテナーからコピーする段取りになります。ここでコンテナーから取得した依存性のキャッシュを次回のビルドの時に戻すことによって、ビルド時間を節約しています。

テスト結果のパース

f:id:setoazusa:20151207130108p:plain:w300

前のプロセスで取得したテスト結果(testreports.xml)をここでパースしています。

CleanUp

f:id:setoazusa:20151207130302p:plain:w300

sudo ./docker-compose kill
sudo ./docker-compose rm -f

ここはコンテナの後始末になります。

ビルド結果

f:id:setoazusa:20151207130606p:plain:w400

はまりどころ

  • PostgreSQLのDockerイメージの仕様変更でビルドが走らなくなっていたため、発表の前日にデバッグしていたこと
  • Macbook ProVagrant上でBamboo+Dockerを動かしていたのですが、最初CPU×2、メモリ2Gにしたらelasticsearchにリアルタイム連携しているところが間に合わなくてテストがfailしたこと。CPU×3、メモリ4Gにしたら動いた。

最後に

Bamboo+docker-composeによるCIビルドの例でした。Atlassian Summitの事例報告と言うことでBambooを使った発表になりましたが、Bambooの特定機能には依存していないので、CIサーバーでのDockerの使用例の一つとしてみていただければと思います。

しょぼちむにテストファーストについて説明してみる

このエントリーは、ソフトウェアテストあどべんとかれんだーの11日目*1ならびに、しょぼちむ Advent Calendar 2014 - Adventarの19日目エントリーです。

最初は、JJUG CCC 2014 Fallの懇親会の時に、「しょぼちむでTDD」というオーダーを受けていたので、

describe "しょぼちむ財布もってる?" do
   let (:she) { FactoryGirl.build(:syobochim) }
   it { expect(she.answers).to eq "はい" }
end 

...というのを題材に、TDDを説明してみようとおもったのですが、specがどうやっても緑にならないため*2思いのほか話が深まらないため、TDDの中の重要な要素、テストファーストについてお話ししたいと思います。

JJUG CCCのセッションの中で、「テストファーストにこだわらない」とことに言及させてもらいましたが、

実際にTDDを進めていく中で私がテストファーストを軽視しているかというとそうではなくて、むしろ重視しています。それについては直後にこう呟いた通り。

というわけで、このエントリーでは、TDD、ひいてはソフトウェアテストの中で、「テストファースト」がなぜ重要かということについて、書きたいと思います。しょぼちむはどうしたということに関しては、このアドベントカレンダーでは、↓のような話もあるし、まあいいんじゃね?

実務的な話

私がプログラマーとして、なぜテストファーストに拘るかですが、率直なことを申せば「テストファーストしてないテストは怖い」です。

テストそのものをソフトウェアコンポーネントとして捉えた時に、テストに求められている機能は、テスト対象(SUT)が意図している時に成功(Green)となり、そうでない時に(Red)となること、即ちテスト自身がテストの結果を判断できることです。

この観点から見た時に、テストファーストしてないテストの弱点は、テストを失敗させていないため、テストの信頼性がその分下がるということです。これはTDDの「まず失敗させるテストを書く」プラクティスに乗っ取ることができないためです。このことを差して、JJUG CCCのセッションでは「失敗していないテストのカバレッジは50%しかない」と表現しました。

「どのようにテストを成功させるか」と同じくらい、「どのようにテストを失敗させるか」ということは重要です。どのような条件を満たすとテストが緑になり、どのような条件で赤になるかということを明確にすることで、テストは品質を支える基盤として必要な堅牢さを得ることができます。

Testing と Checking

ソフトウェアテストには、対象のシステムを探索し、学び取る(Learing)Testingとしての側面と、アサーションが通るか、失敗するかを判断するCheckingとしての側面があります。ユニットテストに代表される、開発者のテストは、Checkingに軸足を置いています。

ソフトウェアを開発していく上で、Checkingとしてのテストの果たす役割は、どのようにステップを踏んでいけばゴールに到達できるかということを差す、道しるべとしての役割です。そして、テストが道しるべとしての役割を果たすためには、プロダクションコードを書く前に、Checkingとしてのテストをチェックポイントとして置いておく必要があります。

システム開発の工程の中では、ユニットテストだけでなく、結合テストシステムテスト、非機能要件のテストなど、様々な粒度のテストを行います。その中でのシステムテストは「指定された要件を満たすことを実証するため」に行いますが、システムテストの段階でシステムが「要件を満たしていない」ことが明らかになっても手遅れです。システムが要件を満たすことを確認するためには、上位レベルのテストのCheckする内容と、ユニットテストなどの下位テストのレベルがCheckする内容がお互いに関係を持ち、内容を補完し合っていて、テストの工程を回す中でフィードバックループを適切に回す必要があります。そして上位レベルのテストと下位テストのレベルが連携するためには、V字のモデルを描く必要があるので、やはりテストは先に作成されるべき、ということになります。

ではどうすればいいか

要は、システムを完成させるために、どうステップを踏んで、ゴールに近づいていくかです。その中でテストファーストを導入できるレベルにあれば、導入すればよいし、テストファーストを導入できる段階でなかったとしても、End to Endレベルの試験項目は先に作成するなど、システム開発工程全体の中で、Checkingする工程を適切にまわせていけばいい、ということになります。

ただ、IT技術者として、持っている手札の数は多い方がいいと思いますので、写経等で、テストファーストの考えに触れてみることは、非常に有益だと思いますので、お勧めします。

しょぼちむアドベントカレンダー、明日のエントリーはfukai_yasさんです。

*1:今日は何日だって話ですが...ごめんなさいごめんなさいm(__)m

*2:仮実装でこっちから財布わたせばいいんですかね...?

MyBatis MigrationsとBambooとSchemaSpyでデータベースの構成を管理する

このエントリーは、ゆかむアドベントカレンダー - ゆかむ | Doorkeeperの7日目エントリーです。

はじめまして。@setoazusa です。こんなハンドルですが、男性です。 都内のSIerで、TDDの導入や、DevOpsの推進、プロジェクトメンバーのフォローなどを仕事にしています。 IT技術者のコミュニティでは、TDDBCなどのコミュニティなどを中心に活動しています。よろしくお願いします。

ゆかむさんとは、第2回 ゆかむ勉強会 - connpassで、「CIサーバーとSchemaSpyでデータベースのドキュメント作成を自動化」というお題で話をさせていただきました。

今日は、その話の続きで、データベースのスキーマ管理のために、ツール同士をどう連携させているかという話をします。

データベース管理のあるある

このエントリーをご覧になっているみなさんは、リレーショナルデータベース(RDB))を使ったシステムの開発に従事されている方が多いと思います。その中で、このようなことに遭遇した方はいらっしゃらないでしょうか?

  • テーブル設計書と実際のテーブルの内容がずれている
  • ステージング環境と本番環境でテーブルのカラム構成が違う
  • 「テーブル設計書最新20141201.xls」
  • テーブル設計を変更するのにやたら手間がかかる

バージョン管理、コードの共同所有、ドキュメンテーションなどの開発のベストプラクティスがデータベース設計については適用されないことを、書籍「SQLアンチパターン」では「ディプロマティック・イミュニティ(外交特権)」と命名しています。

SQLアンチパターン

SQLアンチパターン

上記の問題に対応し、ローカル環境と開発環境とステージング環境と本番環境それぞれの構成管理をスムーズにするために、このエントリーでは、MyBatis MigrationBamboo、そしてSchemaSpyを使って、データベースの構成管理を行う時の流れを取り上げます。

説明に使っているデータベースの構成

RDBMSPostgreSQLで、データベースは、ローカル環境用*1、CI用、ステージング、商用環境の4つがあります。

テーブルはOracleのサンプル等でお馴染みのHRのスキーマーで、empテーブルが先に出来ている想定です。

f:id:setoazusa:20141207233251p:plain

1. MyBatis Migrationsでテーブル定義を変更する

まず、MyBatis Migrationsでマイグレーションを作成します。(MyBatis Migrationsそのものや環境の構築方法についてはMyBatis Schema Migrationを使ってみる - @katzchang.contextsDB Migration 入門 vol.1 #CDStudy を開催しました。 — うさぎ組を参照ください。このエントリが好評だったら環境構築やディレクトリ構成について追加エントリを書く。かも。)

C:\Users\azusa\git\migration\migration>..\bin\migrate new "create dept table"
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
------------------------------------------------------------------------
-- MyBatis Migrations - new
------------------------------------------------------------------------
Creating: 20141207150330_create_dept_table.sql
Done!

------------------------------------------------------------------------
-- MyBatis Migrations SUCCESS
-- Total time: 1s
-- Finished at: Mon Dec 08 00:03:30 JST 2014
-- Final Memory: 7M/479M
------------------------------------------------------------------------

C:\Users\azusa\git\migration\migration>

なお、migrate newコマンドの引数として渡す文字列には日本語も使えますが、渡した引数は上の出力を見て分かるとおり、マイグレーションのファイル名にそのまま使われます。バージョン管理がGitで、MacOSの開発環境がある場合、ファイル名に濁音や半濁音が混じった場合に、LinuxWindowsの環境上で、見た目が同じファイル名のファイルが2つできるという問題があるため*2、コマンドの引数として渡す文字列はAsciiの範囲に留めるほうが無難です。

migrate newコマンドで作成したファイルを、以下の通り編集します。

C:\Users\azusa\git\migration\migration>vim scripts\20141207150330_create_dept_table.sql
--// create dept table
-- Migration SQL that makes the change goes here.

CREATE TABLE dept
(
  deptno numeric(2,0) NOT NULL,
  dname character varying(14),
  loc character varying(13),
  versionno numeric(8,0),
  active numeric(1,0),
  CONSTRAINT dept_pkey PRIMARY KEY (deptno)
)
WITH (
  OIDS=FALSE
);

COMMENT ON TABLE dept IS '部署';
COMMENT ON COLUMN dept.deptno IS '部署番号';
COMMENT ON COLUMN dept.loc IS '地域';
COMMENT ON COLUMN dept.versionno IS 'バージョン番号';
COMMENT ON COLUMN dept.active IS '有効';



--//@UNDO
-- SQL to undo the change goes here.

DROP TABLE dept;

MyBatis Migrationsではマイグレーションの定義をSQLで行うため、マイグレーションのファイル中にはcreate文やalter文等のDDLを直接書きます。

マイグレーションを作成したらmigrate upコマンドでローカル環境に適用します。

C:\Users\azusa\git\migration\migration>..\bin\migrate up
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
------------------------------------------------------------------------
-- MyBatis Migrations - up
------------------------------------------------------------------------
========== Applying: 20141207150330_create_dept_table.sql ======================

------------------------------------------------------------------------
-- MyBatis Migrations SUCCESS
-- Total time: 0s
-- Finished at: Mon Dec 08 00:22:02 JST 2014
-- Final Memory: 12M/479M
------------------------------------------------------------------------

ローカル環境にデータベースが作成されます。

f:id:setoazusa:20141208002314p:plain

2. Bambooによるデータベースのマイグレーション

ローカル環境での確認が完了したら*3、Gitレポジトリにpushします。

f:id:setoazusa:20141208002448p:plain

Gitレポジトリマイグレーションがpushされると、連携しているBambooでビルドが実行され、CI用の環境にデータベースの変更が適用されます。

f:id:setoazusa:20141208003612p:plain

f:id:setoazusa:20141208015253p:plain

この時に、Bamboo上でSchemaSpyスクリプトを呼び出して作成したドキュメントをArtifacts*4として保持していますので、Bambooのビルドに対応するドキュメントを下記のリンクからたどれるようになっています。

f:id:setoazusa:20141208004037p:plain

リンクをたどると、以下の様なドキュメントを参照できることができます。出力したドキュメントのイメージはSchemaSpy - d66vta52qi8mtp.publicから参照することができます。

f:id:setoazusa:20141208004816p:plain

なお、SchemaSpyでドキュメントを作成すると、SchemaSpyの作者のアカウントでgoogleadsが挿入されるのですが、それを出さないようにするにはSchemaSpyの実行時に-noadsオプションをつけます。

Bembooによるデータベース構成のデリバリー

さて、ここまでだと「それJenkinsでもできるよ」という感じなのですが、Bambooの色が出るのはここからです。ビルドのサマリー画面に「Create release」というボタンがあるので、それをクリックします。

f:id:setoazusa:20141208010702p:plain

Bambooには「リリース」という概念があり、任意のビルド結果に名前をつけて、任意の環境にデプロイできるようになっています。

f:id:setoazusa:20141208011046p:plain

リリースを作成すると*5、Bambooのリリースを管理する画面に遷移し、そこから任意の環境にデプロイができるようになっています。ここではステージング環境にデプロイします。

f:id:setoazusa:20141208011204p:plain

f:id:setoazusa:20141208011435p:plain

f:id:setoazusa:20141208014543p:plain

f:id:setoazusa:20141208012001p:plain

そして、ステージング環境での動作確認がOKだったら、ステージング環境で動作確認済みのマイグレーションスクリプトをそのまま本番環境にデプロイすることができます。*6

f:id:setoazusa:20141208011829p:plain

f:id:setoazusa:20141208012110p:plain

f:id:setoazusa:20141208014652p:plain

f:id:setoazusa:20141208012218p:plain

Bambooによるトレーサビリティーの確保

そしてBemboo上で、今データベースに適用されているスキマーがどのビルドで作成されて、バージョン管理のどのコミットが含まれているのかをトレースできるようにあっています。

すなわち、現在の本番環境やステージング環境のスキーマーの状態と、SchemaSpyによって作成されたドキュメントの間で、常にトレース出来る状態になっているということです。

f:id:setoazusa:20141208012724p:plain

f:id:setoazusa:20141208012840p:plain

まとめ

MyBatis MigrationsとBambooとSchemaSpyでデータベースの構成を管理する際の方式についてまとめました。Jenkinsに比べると地味な存在なBambooですが、デリバリーのためのワークフローをプラグインなしで構築できるということに関しては一日の長があるんです、やれば出来る子なんです><

今回の手順ではバージョン管理には、Stashを使用していますが、その部分はGitHubやgitbucket等、他のバージョン管理システムとも組み合わせることができます。MyBatis MigrationsやSchemaSpyについては独立したツールですので、その環境構築の実際や、Bambooの設定で行っていることについては、機会があればまとめたいと思いますので、このエントリーについてのフィードバックお待ちしています!

*1:実際にはVagrantを使用して開発者個人の環境にそれぞれデータベースがある想定です

*2:クライアントの設定で回避できるのですが、1台でも設定をしてないクライアントがあると全クライアントに影響がおよびため、筆者のプロジェクトではGit管理下で日本語をファイル名として扱うのは諦めました...

*3:実際には、データベースに関連付いているアプリケーションのコードベースが、pushできる状態になったら

*4:Jenkinsと同一の概念

*5:ここではリリースを作成→デプロイという流れを分かりやすくするために手動でリリースを作成していますが、Bambooのビルドが成功した段階で自動でビルドを作成し、デプロイを即実行することもできます。

*6:それにしても、データベースマイグレーションのデモをするときの「ほら、ここにテーブルが出来てます!」「...で?」という流れはどうにかならいんですかね、裏で色々頑張ってるんですけど...

JJUG CCC 2014 Fall でTDDについて発表しました #jjug_ccc

先日開催された http://www.java-users.jp/?page_id=1284:JJUG CCC 2014 Fallで「私がTDDできないのはどう考えてもお前らが悪い!~エンタープライズJava開発でのTDD適用の勘所~」というタイトルで発表をしました。

この発表ですが、8月のプロジェクトのサービスイン打ち上げでクライアント様から「技術部門賞」として表彰された時に「そうだ!このプロジェクトの成果を公の場に残そう!」と思い立ったのがきっかけになります。その勢いで帰宅してからすぐCfPを書き始めて、そのまま勢いで出した応募が採用され、今回発表の機会をいただきました。

一部の方はご存じの通り、自分の勤務先はJJUG会長のお膝元ですが、お手盛りでなく、公募セッションにふさわしい発表が出来たと自負しています。

資料の準備を作成しているときは、「まあ、メインホールのきしださんの裏番組だし、JavaコミュニティでTDDの話題ってアウエーだから半分くらいうまればいいかな?」と思いつつも「でもガラガラだったらどうしよう...」とか心配していたのですが、立ち見が出るとは予想していませんでした。ちょっとあせったのですが、でもJavaコミュニティのみなさんがTDDについてこれだけの興味をもってくれているということで、非常に勇気づけられました。

スライドを作成したら60枚を突破しても収集する気配を見せず、当日の発表直前にも削ったのですが、83枚の大作になりました。チームビルディングの話と技術サイドの話でどう見ても2回分のボリュームだったのですが、「一つのプロジェクトでTDDを導入するときに何を考え、どう行動したか」ということをどうしても一つのスライドで盛り込みたくて、無理をさせていただきました。ただ、技術的な各論の話をしていたときにかなり駆け足になり、そういう方面の話を期待していた方には申し訳なく思っています。

「私がTDDできないのはどう考えてもお前らが悪い!」というタイトルで聴講をためらっていた方もいたみたいなんですが、これについてはJavaコミュニティの一部にある「スライドにネタを織り込まないと気が済まない」という悪しき風習に流された結果と申しますか...^^;

ただ、「ミッションを共有する仲間が集うコミュニティで、プログラマーが一人一人知恵を持ち寄り、議論し、勇気づける場が出来たときに、世界は変わる」の時にいきものがかりの歌詞を使ったのは意図的にやってます。そうでないと、次のスライドの「あなたがコミュニティです」につなげられないので。

笑ってたいんだ/NEW WORLD MUSIC

笑ってたいんだ/NEW WORLD MUSIC

後、IT技術者を続ける理由を語るからには「君にも君を動かしているものがあるでしょう?」は外せないですよ、部長萌えですよ萌え

夢喰いメリー (1) (まんがタイムKRコミックス フォワードシリーズ)

夢喰いメリー (1) (まんがタイムKRコミックス フォワードシリーズ)

今回のJJUG CCCですが、登壇者、参加者の方からもすごい熱気を感じました。そういう参加者の前で、一つのプロジェクトでやってきたことをオープンに出来て、満足感を感じています。これからも、コミュニティで活動すること、そしてコミュニティとしての現場で価値を生み出すことに、コミットしていきたいと思います。

最後になりますが、発表の機会をいただいたJJUGのみなさま、そしてプロジェクトを一緒に支えていただいたプロジェクト関係者のみなさま、ありがとうございました!

部屋とSIと私

本年1月1日にグロースエクスパートナーズ株式会社というSIerに入社して7ヶ月がたち、入社して初めて関わったプロジェクトが無事サービスインしました。転職活動を始めてから今日まで、多くの方々にお世話になりました。御礼申し上げます。

現在は都内の大企業に常駐して、法人向けサービスの開発をやっています。クライアント様がアジャイル開発を進めたいということで、その上で「TDDの推進をしてほしい」ということでアサインされた(らしい)のですが、そういうこともしていますが、プロジェクトの中では

  • 認証基盤のミドルウェアと格闘したり
  • Backbone.jsとCoffeeScriptと戯れたり
  • データベースマイグレーションを導入したり
  • 社内のアトラシアン担当と色々やりとりをしたり
  • SpringとHibernateの挙動に慣れなくて苦労したり
  • Spring Batchを実戦投入したり
  • BambooでCIとデリバリーの設定をしたり
  • Serverspecでデプロイスクリプトの受入テストを書いたり
  • chefを試したり

ということをしています。今現在、技術的にはビルドパイプラインの運用を軌道にのせること、Gebを使ったend to endのテストの自動化、およびフロントエンドのユニットテストの自動化が大きなテーマになっています。開発環境としてはMac+IntelliJ+Vagrantで、開発インフラはアトラシアンフルスタック(Confluence+JIRA+Stash+Bamboo)です。

転職して環境を変えるとなると、「ウォーターフォールアジャイルか」とか「SIerかサービスか」というわかりやすい軸が注目されます。それもそうだと思うのですが、開発プロジェクトがコミュニティとして何に重きを置くかということが、技術者としての個人の価値観とマッチングするか、というところが重要だと感じています。

そういう観点から今のプロジェクトを見ると、

  • サーバーサイドはユニットテストを書くのが定着している。
  • リファクタリングのための工数が確保できる。
  • 技術的なこと、プロジェクト運営について、チーム内で議論できる。
  • 見積もりについてチーム全体で議論する。担当者が見積もった工数が何かと理由をつけられて削られるということはない。
  • 企画サイドの方と開発メンバーが直で相談できる。
  • 原則休日出勤はしない。

と、健全な方向を目指しているのと、頭数を集めるだけでなく、ちゃんとチームを作るということが出来ているのは自分の考えとマッチしていると思います。これで「過度な残業はしない」となればかっこいいのですが、クライアントの一部担当の方に負荷が集中してしまう傾向があるのがちょっと残念なところです。それでも、リリースの二週間前にはコードベースを凍結できましたし、リリースのあった週も本番作業の準備があったメンバー以外は定時で帰れた、というのは自慢したいところです。(もちろん最初からそういうチームづくりができたわけでもないし、色々うまくいかなかったことや苦労もあったのですが、それについては寿司でも食べながらということで...w)

これからも技術者として、プロジェクトで成果を出すことと、コミュニティで活動することをちゃんとリンクしていきたいと思います。よろしくお願いします。

TDDをめぐる、最近の議論についての私見。

はじめに

DHH氏のTDD is dead. Long live testing. (DHH)のエントリは、国内でもさまざまな議論を呼び起こしました。ですが、そのセンセーショナルな見出しの影響もあり、「(TDDと同一視した上での)ユニットテストは不要」などの、ミスリードされた論調も見られます。乗り遅れた感もあるのですが、前述のエントリに限らず、TDDについて最近考えていることをまとめたいと思います。

TDD=テストファーストではない

ケントベックの「テスト駆動開発入門」や、Uncle BobのTDD三原則の影響もあり、TDDでは、まずテストファーストするのだ、という印象をお持ちの方がいると感じてるのですが、いきなりテストファーストするというのは、教条主義なところがあり、現場に適用するのは敷居が高いのは確かです。

TDDを実践する上で大事なのは、テストによって開発が駆動されることです。すなわち テストコードを書くことと、テストに対になるコード(プロダクションコード)が作業として近い関係にあり、テストコードが、プロダクションコードに対しフィードバックをもたらしてくれることが重要です。なので、「テストコードを先に書く」ことに固執することよりも、まず、どんな形でもいいので、自分の書いたコードに対してテストを書いてみた上で、改善のサイクルを回すのが大事だと思います。

TDDは設計プロセスですが、TraditionalなWebMVCアーキテクチャーの場合、プレゼンテーション層と永続化層の間にレイヤーが押さえつけられているため設計の自由度が低く、設計プロセスとしてのTDDの出番が少ない面はあると、私個人は感じています。そのようなアーキテクチャーの是非はありますが、アーキテクチャーに対し、TDDに限らずどのようなテストで品質をカバーしていくかはプロジェクトごとに検討が必要ですし、全てのプロジェクトの全てのコードに対しTDDが有効というわけではないという点には、私も同意です。

TDD戦略の必要性

これも散見する誤解なのですが、TDDは「全てのコードに対し、1対1でテストを書く」ことを目指していません。 また、プロダクションコード内の各モジュールの粒度に対し、テストの粒度が大きかったり、逆に小さかったりすると、仕様変更や機能追加に耐えられないテストスイートになったり、リファクタリングの妨げになります。

なので、TDDの中で、どこのレイヤーに対しユニットテストを書いて、どこの部分をユニットテストでないテストでカバーするのか、ということをプロジェクトごとに設計する必要があります。

開発者テストと品質保証

TDDは有用なのかそうではないのかという議論を進める上では、TDDは何を目的とするのかという点をはっきりしておく必要があります。まず、原則として、TDDで書かれるテスト(開発者テスト)は、品質保証を目的としません。だからといって、TDDのテストは品質を向上保証しないというのは、それはそれで誤解であり、いわゆる実装(製造)工程と品質保証の工程の関係の理解が不足しているのではないか、と考えます。

重要なのは、開発者テストと品質保証のためのテストはお互いを補完し合う関係にある、ということです。開発者テストでのテストは、開発者の意図を確認するために書かれます。対して品質保証のテストは、ユーザー側の視点から、「ストーリーの受け入れ基準を満たしている」ために行われます(書かれます)。

開発者テストのレベルで「開発者の思ったとおりに動く」ことが保証されているからこそ品質保証のテストは品質の検証に注力でき、品質保証のテストが後詰めとして控えているからこそ、開発者テストはプログラムの中の勘所を押さえることに注力できるのです。この2者間のバランスが崩れていると、品質保証のためのテストでプログラムのカバレッジを意識することになったり、開発者テストとして書くユニットテストの中でシステムとしての品質保証を意識する羽目になります。

そして開発者のテストと品質保証のためのテストに限らず、「テストを書く」ということは、継続的インテグレーションや静的解析、コードレビューやペアプロミングなど、チーム開発のための各種プラクティスと連携して、初めてその力を発揮します。そのことを踏まえずに、TDDだけを取り上げて、開発に役に立つ、役に立たないという論調には、議論としての危うさを感じます。

さいごに

ユニットテストとTDDを同一視してしまう論調に代表されるように、TDDを取り巻く各要素はそれぞれが複層的に折り重なっていて、一つ一つを単独の要素として理解することには困難が伴います。だからこそ、各々の現場の開発プロセスの中で、TDDがどういう位置づけにあるのか、開発プロセスの中でどうやって総合力を発揮するかという点を意識した議論を望みます。