ishida

2012.11.12

PHPerのためのJenkins

序文

ソフトウェア開発とはままならないものだ。そもそも開発と銘を打たれているにも関らず製造工程のような量的単位「工数」で管理しようとしているので致し方無いのかも知れない。そう、開発と言うからには「作ったことの無いものを作る」ということであるにも関わらず、

・「何人がかりで何日で出来る?」と聞く

・「何日くらいですかね、やってみないと分からないけれど」と答える

・「分からないけれど」はとりあえず無視して、何日かの余分をとってスケジュールを作る

そんなことをしていれば古典落語に出てくる「致し方ないけれど起こる残念な出来事」のようなオチにしかならないのは目に見えているのだが、これは現実によく有るやりとりである。しかし、これでもまだマシな部分も有る。少くとも作ろうとしてとしている人が「何日で出来そうか?」について答えているからだ。作ろうとしている人ではない人が当てずっぽうに答えてスケジュールが決まっているケース等はもう目も当てられない。筒井康隆の小説のような毒と狂気に満ちたスラップスティックドタバタ劇を再現しようとしているのではなかろうかと。ソフトウェア開発は開発なのだ。「1キログラムの米の中から1粒ずつ確認して砂利と米をより分けろ」というような定量で計れる作業ではない。それは「存在しないものを、それを作ったことの無い人が作ろうとしている」からだ。挙句の果てには、存在しないのを良いことに作っている途中で作ろうとしているモノが変わっていったりもする。このように、そもそもが「何をもって完成とするのか」ということですら抽象的な作業に対して「何人がかりの何日で作成可能か」と聞くのも「何日くらいで作成可能である」と答えるのも「スケジュール」を引くのも「完成を望む」のも「納品する」のも愚かだと言えるのかも知れない。ならば、この作業はどう実施されるべきなのか? 諸兄においてはいくらでも挙がってくることだろう。例えば、僕ならばパッと思い付くだけ並べても以下のように挙げる。

・スケジュールを変更可能なものとして定義する(見積もりがし辛い作業であるという前提に立つ)

・運用の続く限りリファクタリングを続け、少しの改善を毎日リリースする(Webアプリでしかあり得ないが)

・発見した欠陥が0になるまで追加も変更もしない(欠陥の上に作り込んだら取り返しが付かないため)

・そもそも完成を目指さない(明確に定義できない完成に向かうこと自体が迷走であるため)

しかし、何はともあれスケジュールである。スケジュールそのものが成り立ち難い作業であるにも関わらず、我々はスケジュールという言葉が大好きな人々に囲まれている。そんな人々に囲まれて八方塞がりである。勿論、理解を求める努力はしていくべきである。しかし、理解を得るのには時間がかかる。しかも、時間をかけたとて理解を得られる保証は無い。得られる理解は少しずつであろう。むしろ、少しでも進むのであれば大進歩と言えるのかも知れない。我々はその途方もない時間の中でも身を守っていかなければならない。その為にはスケジュールをより現実に近い値にしていく必要がある。つまり、見積の精度を上げるのだ。上に挙げたような夢のような日々が、いつかくるにせよ、見積の精度が上がるに越したことはない。では「見積の精度を上げるにはどうしたら良いのか?」 それこそ語り尽くされた永遠のテーマとも言えるが、その中でも最も実現が容易で効果の期待できる対策だ。

「複雑度を作り込まない」 という対策

いきなり元の話からかけ離れた回答のように見えるかも知れないが怒らずに読み進めて欲しい。つまり、作業が見積からかけ離れない方法ということなのだ。「実際は見積った工数は作ろうとしているものに対して、そう外れていない」のに「作業をしているうちに作業が見積からかけ離れてしまった」ために「見積もりが外れた」ように見えるのだとしたら? という仮説である。見積そのものの精度の上げ方についてならば色々と手法は有るだろう。手法はともかくとして「可能な限り要件を漏らさない」「実現性の検証を徹底する」「曖昧な値を可能な限り減らす」「可能な限り小さい単位で見積る」「概算だからすぐに答えてという言葉に騙されない」ということに尽きると思っている。これらについては語り尽くされていると思う。にも関わらずスケジュールが大幅にズレる。それは「変更が多過ぎる」か「作り込んだ複雑度のせいで作業時間が見積からズレていく」のケースが多いのではないかと体感している。今回の対策は「作り込んだ複雑度のせいで作業時間が見積からズレていく」に効果が期待できる。

複雑度について

ここで言う複雑度とは単純にプログラムの難解さと言い換えても良い。

・長過ぎるメソッド

・大き過ぎるクラス

・大き過ぎるテーブル

・多過ぎるflgという名前のフィールド

・複雑過ぎる条件分岐

・再利用性の低い構造

・そもそも構造の無いソースコードの羅列

・コピペコードの山(ついでにバグもコピられている)

・etc

諸兄においてはいくらでも思い付くことだろう。あんなソースコードとか、こんなソースコードとか。たとえ、新規の開発だったとしてもプロジェクト半ばにさしかかってくる頃にはそこいら中に転がり始めるプログラム。そんなプログラムが持っている無用な複雑度のことを指して前段では複雑度と呼んだ。そういった複雑度は現在直接エラーを吐いたりデータを破壊したりしていなくとも、その箇所を修正するのにも、周辺部分の修正に伴なう回帰テストを実施するのにも時間がかかる、そして修正の度にバグを呼びやすい。言わば「潜在バグ」の一種である。そして、それらを「潜在バグ」とするのならば、元々守られていたアーキテクチャを無視して構造を破壊するメンバーや、やっつけでflgというフィールドを追加してパターンを指数的爆発に増やすメンバーや(にも関わらずifの分岐は明らかにパターン数を満たしていないので不吉な臭いがしたり)、あんなメンバーやそんなメンバーも「歩く潜在バグ」と言えよう。とは言え「潜在バグ」は「潜在バグ」でしかないのだ。早期に発見すれば修正は容易である。逆に発見が遅れれば遅れた分だけ、修正が遅れれば遅れた分だけ傷は広がる。その発見を少しでも早く、そして出来るだけ多く、なるべく自動で見つけるための仕組の紹介、と言うのが今回の本題である。

具体的には以下の事柄について、Jenkins を 用いて実施することである。

・Unitテスト及びカバレッジレポートの可視化

・ソースコードの静的解析と問題点の可視化

「歩く潜在バグ」はなにも本質的に悪人であったり愚かであったりする訳ではない。単に今現在ソースコードに埋め込んでいるものが「潜在バグ」であるということを認識していないに過ぎないのだ。虫歯は本格的に痛くなってから治す派の人に過ぎない。こういった仕組を導入して「現在、痛くないだけで口の中は虫歯だらけなのだ」と、そして「その虫歯を皆で共有しているから虫歯になったら困るのだ」ということを可視化してしまえば治そうという気にもなってくれるだろう。さて、前置きが長くなり過ぎたが本題に入ろうと思う。とりあえずJenkinsを回すサーバーを用意して欲しい。PHPとJavaがインストール出来て、ネットワーク上に存在してさえいればどんなサーバーでも構わない。

Unitテスト

本質的には回帰テストの自動化であり「アホなバグ」を減らすことを目的とするものであるが、それ以上に期待出来るのが「実行可能なテストを書くためには複雑なメソッドを複雑なままにしておくことが出来ない」ことと「実行可能なテストを書くためにはテスト可能な構造でなければならない」ということ。その構造が美しいかどうかはともかくとして最低限の「テスト可能な構造が保たれる」はずである。これは「見積もりを逸脱しない状態を維持する」ための要と言える。

Unitテストを回すためのフレームワークPHPUnitのインストールはpearから行う。

なお、日頃、あんまり pear からインストールしてない場合は、チャンネルのauto_discoverフラグを立てておくとハマり難いかも知れない


sudo pear config-set auto_discover 1

PHPUnitのインストール


sudo pear upgrade pear
sudo pear channel-discover pear.phpunit.de
sudo pear install --alldeps phpunit/PHPUnit

また、PHPUnitには実行したテストがどれだけのプロダクトコードを保護したかをレポートする機能がある。このカバレッジレポートはJenkinsを用いて可視化することが出来る。カバレッジの低い範囲へテストを当て込む度に、このレポートの値が健全になっていくことを確認出来るはずだ。その度に「構造が保たれた」「メンテナンスが容易であり」「見積りを逸脱しない」ソースコードになっていくことを実感出来る。これはメンバーのモチベーションの維持へ貢献することだろう。

Unitテストについては、ここで詳述するととても足りないので、手前味噌であるが Qiitaで書いた記事 を一読して欲しい。

phpcpdについて

コピペされていると推測出来るコードを抽出して指摘するツールだ。そもそもUnitテストを書いている場合には、このような箇所は少ないはずだ。なぜならばコピペした分だけテストを書かなければならないので意地でもコードを再利用しようとするはずだからだ。とは言え、それでもコピペは起こる。コピペはバグごとコピる。それはやがて手が付けられなくなる。コピペを見付けたら即座に潰せるよう可視化していこう。

phpmdについて

長過ぎるメソッド、大き過ぎるクラス。こういったコードを抽出して指摘してくるツールだ。これもUnitテストを書いている場合にはこのような場所は少いはずだ。長過ぎるメソッドに対して自動テストを書くのは端っこがギザギザの10円玉で大根おろしを作る位に無謀な挑戦だからだ。それでも長過ぎるメソッドや大き過ぎるクラスは現れる。手を付けられなくなる前にリファクタリングしよう。そこにかけた労力は将来のシステムの成長速度となって取り返せるはずだ。何より、いつまでも楽しく仕事していける健全なソースコードはメンバーのモチベーションに関わってくる。そのソースコードがWebサービスであるならば事業の成長速度に大幅に関わってくるポイントだ。

phing

先に挙げたツール類を取り纏めるための親玉である。Apache AntというJava向けのビルドツールのPHP移植版である。バージョン管理ツールからソースコードを抜いてきてデプロイの前に設定ファイルを本番向けに差し替え、ユニットテストを実施し、その上でrsyncかけるなどと言うような用途にも使える。勿論、コマンドラインから使うことも出来るのだが、今回はJenkinsと各ツールを繋ぎ込むための要として利用する。

phingのインストールはpearで行う。
上記のツール類はphingと一緒にインストールすることも出来る。


sudo pear channel-discover pear.pdepend.org
sudo pear channel-discover pear.phpmd.org
sudo pear channel-discover pear.docblox-project.org
sudo pear channel-discover bartlett.laurent-laville.org
sudo pear channel-discover pear.phing.info
sudo pear upgrade pear
sudo pear install --alldeps phing/phing

もし、この時点で php -i など単にphpを実行しただけでもxdebug周りのエラーが表示されるようならば、php.iniの設定を見て欲しい。zend_extensions=[xdebugへのフルパス]が無いようであれば追加すると良化すると思う。phingのサンプルのソースを用意したのでダウンロードして、普段使っているバージョン管理システムに登録しておこう。

https://github.com/t-ishida/jenkins-sample

本稿では、build.xml についての細かい説明は省かせてもらう。説明しだすとキリが無いことと、サンプル中のコメントを見ればなんとなく伝わると思ったからだ。変更する際には必要に応じてググればなんとかなるはずだ。

Jenkinsについて

インストール

継続的インテグレーションという言葉とセットでよく紹介されるソフトウェアである。Javaさえインストールされている場所であればすぐに動かすことが出来るのがとても有り難い。このソフトウェアで何をしようとしているかと言うと「機械的に検出出来る潜在バグを早期に発見する」ということ。 CentOS系のサーバーであれば以下のコマンドでインストールできるはず。


wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
yum install jenkins

これで該当のサーバーの8080ポートへブラウザから繋げば、すかしたおっさんの顔が現れるはずだ。

すかしたおっさんの顔を見ることが成功の証であるのは少し残念な気もするが大丈夫だ。運用を続けていくうちに、このすかしたおっさんのことを頼もしく思えるように、そして好きになってすらくる自分に気付くはずだ。諸兄には、このすかしたおっさんのことが好きになるまで運用を続けてもらえるようお願いしたい。放っておくとリロードされるのでリロードされたら初期設定を行う。ここではPHPで問題の発見を早くするための各種プラグインをインストールする。

メニューからJenkinsの管理をクリックする。続いて、

プラグインの管理 => 利用可能プラグイン

へと遷移する。

1、Phing Plugin に チェックを入れる。

2、DRY Plugin に チェックを入れる。

3、PMD Plugin に チェックを入れる。

4、Clover PHP Plugin に チェックを入れる

5、「インストールして再起動」をクリックする

新規ジョブ作成

続いて、実際にJenkinsを稼動させる準備を行おう。Jenkinsでは定義した動作の一連を「ジョブ」と呼ぶ。このジョブを作ってみようとしている。
メニューから「新規ジョブ作成」をクリックする。

1、ジョブ名に適当な名前を付ける

2、フリースタイル・プロジェクトのビルドを選択する

すると、こんな画面が現れると思う

それぞれ ? マークの付いた部分をクリックすれば適切な説明が読める。プロジェクトごとに適切な設定は異なるだろう。そこは自分の判断で入力して欲しい。ソースコード管理システムを監視してコミットが有る度に回すのが基本だ。なお、ソースコード管理システムとしてGitを利用する場合は別途プラグインが必要となる。Gitを利用する場合にはインストール項で触れたプラグインのインストール部分を参考にしてインストールをしてもらいたい。

1、ソースコード管理システムを選んでリポジトリを登録して欲しい

2、ビルド・トリガのSCMをポーリングにチェックする

3、現れたテキストエリアに「0 * * * *」と入力する。これは crontab の 時間指定と同じフォーマットだ。この指定であれば1時間に1回実行されることになる。

3、ビルドの項目 「Phingの呼び出し」を選択する

4、高度な設定をクリックする

5、ターゲットに以下を指定する


test
coverage
cpd
pmd

6、ビルドファイルの項目に、先程配置したソースコードの中に有る 「build.xml 」 と入力する

7、ビルド後の処理の追加「PMD警告の集計」を選択

8、集計するファイルに「**/pmd.xml」を入力

9、ビルド後の処理の追加「重複コード分析の集計」を選択

10、集計するファイルに「**/cpd.xml」を入力

11、ビルド後の処理の追加「Clover PHPカバレッジリポートを集計」を選択

12、集計するファイルに「biuld/reports/clover-coverage.xmll」を入力

13、Clover HTMLレポートを公開するに「biuld/reports/clover/index.html」を入力

14、ビルド後の処理の追加「E-mail通知」を選択

15、自分のメールアドレスを入力

16、保存をクリックする。

これで自動テストを回し、コピペコードの検出とレポート、無駄に複雑過ぎるコードのレポートを一時間に一回集計して、ビルドが失敗した場合に通知してくれるようになる。あとは実際にソースコードをコピペしたり、無駄に複雑にしたりして、手動でビルドを回したりしながら継続的に使っている自分をイメージして欲しい。

※試してみるとphingのcpdがコケるマシンがあった。対策を調べているが難航しそうなので、もし、あなたの用意したマシンでcpdがコケるようならば一旦cpdを外してし実行してみて欲しい。ビルドがコケたら対象ビルドのコンソール出力から確認できると思う。

Q&A

Q:本当にこれで変わりますか?

A:変わるかも知れないし、変わらないかも知れない。変化はゆっくりかも知れないが諦めずに続けて欲しい。

Q:上司の理解を得られません。どうしたら良いですか?

A:当たり前だ。スケジュールと金の流れについて気にするのが仕事の相手にコードの話をしてどうする。相手がコードに対する意識のある相手ならともかく、そうでなければ通じる訳が無い。そもそもコードの品質を握るのが我々の仕事だろう。一日に一時間を空けるために集中して仕事して、こっそり一日一時間ずつ準備すれば良い。それが後ろめたいのなら残業して準備すれば良い。形が見え始めたらチームにやっていることを共有して勝手に改善を始めると良い。

Q:もううんざりです。なにも改善できません!どうすれば改善できますか?

A:質問に質問で返して申し訳ないが、君は変えられるエンジニアか?変えられないエンジニアか?君が変えられないエンジニアならば既に出来上がっている場所に転職することを勧める。変えられることだけが職能ではないから、君をもっと必要としている会社に行った方がお互いのためだろう。君が変えられると自負するエンジニアならば、まずは半径5mを見回して欲しい。そこには疲弊している仲間達がいることだろう。まずは半径5mの仲間達が楽しそうに仕事をしている姿を想像出来ただろうか?彼等のために一歩ずつ改善を進めみよう。半径5mを幸せに出来たら次は世界だ。世界を変える舞台に上がったら、そこで会おう。

アライドアーキテクツでは芝居がかった文章を書くエンジニアと世界を変えられるエンジニアを募集しております。