こんにちは!パンダ大好きエンジニア、宮原です。先日、上野動物園で無事にパンダの赤ちゃんが生まれましたね!\めでたい!/
さて、今回はPHPでの開発環境改善にあたって考えた環境設計思想と、実際に構築した仮想環境や導入ツールを紹介します。
目次
- 目次
- 私が入社したときのチーム状況
- 開発環境をゼロから構築するチャンス〜ポイント交換開発の主担当に選ばれる
- 開発環境構築にあたっての思想
- 構築した環境・導入したツールの紹介
- 各種ツールを導入しての所感
- 今後やってみたいこと
- おわりに
私が入社したときのチーム状況
私が入社時に所属したチームでは、動作確認と開発環境を兼ねた共有サーバ上で開発が行われていました。共有サーバでの開発にはメリットもありますが、以下の課題があるとも感じていました。
- コードを変更するとサーバ利用者全員に影響する
- 動作確認用途も兼ねたサーバで複数の人が同時に開発しているので、動作が重たいことがある
- サーバの記憶領域に気を付けないと、残容量がカツカツになる
共有のテストサーバ上で開発を行っていると、コード変更の度にちょっとドキドキします。もし間違ってプログラムを壊してしまったら、他の人にも影響を与えてしまうからです。エラーケースのテストを行うために、デバッグコードを埋め込んで意図的にエラーを起こすこともありますが、そのような時は周りの使用状況を確認したり、つど声をかけたりする必要が出てきます。
コミュニケーションを取ること自体は悪くないのですが、どうしても開発スピードに影響が出てしまいます。
また当時は静的解析などのツールが導入されておらず、コメントの書き方やコーディングスタイルに関する開発ルールもありませんでした。そのため人によってコーディングのスタイルが異なるほか、一つのファイルの中に複数のスタイルが混ざっていることもあり、コードが読みにくいと感じることがありました。
開発環境をゼロから構築するチャンス〜ポイント交換開発の主担当に選ばれる
そんな中、チームで新しいサービスとしてポイント交換を開発・リリースすることになりました。当時「新しいことにチャレンジしたい」と思っていたので立候補したところ、念願叶って開発の主担当に選ばれます。それまでは既存サービスの改修や機能追加が多かったのですが、今回はスクラッチ開発です。
せっかくスクラッチ開発できるので、しがらみがない分いろいろな開発施策を試してみようと思いました。私自身はPHPでの開発経験はあまりないため手探りでの挑戦です。
開発環境構築にあたっての思想
スクラッチ開発をはじめるにあたり、最初に開発環境を構築する必要があります。動作確認環境や、開発支援ツールを動かす環境など、どのような開発環境にするかということを考えた結果、以下コンセプトでの構築を提案しました。
- 他の人を気にせず使える(壊せる)動作確認環境
- CIサーバがなくても各種ツールを簡単に実行できる
- エンジニアがやらなくても済むことは機械に任せる
他の人を気にせず使える(壊せる)動作確認環境
自分が書いたコードの最初の動作確認や、試してみたい機能のテストなど、他のエンジニアを気にせず実行できるようにしたいと思っていました。そこで、まずは各自のローカルPC上に仮想の動作確認環境を構築することに。実装時に、他のシステムやAPIを使用する箇所は、全てスタブやモックを用意するようにしました。
CIサーバがなくても各種ツールを簡単に実行できる
今回は私も含めて、手探りでの開発環境構築。チームにとっての「理想的な開発環境」がどんなものなのか分かっていませんでした。そのため誰でも簡単に環境構築や各種ツールの設定・実行ができる、柔軟な環境づくりを優先させました。
ツールの設定・実行を容易にするため、ツール自体もリポジトリに格納するスタイルを取りました。バージョンアップのフットワークが重くなるという弱点があるものの、ツールのバージョンが固定される、リポジトリをcloneさえすれば(他に特別なインストール等せずとも)すぐにツールが使える、というメリットが生まれました。
実行自体の手間も減らすため、ツール類は全て実行用のシェルスクリプトを用意し、各自のPCからコマンドを叩くだけですぐに使えるようにしています(当時はCIサーバを構築する環境が整っていなかったのですが、現在は整備されています!)。
エンジニアがやらなくても済むことは機械に任せる
例えばコードレビューで、インデントの調整などをエンジニア(レビュアー)がわざわざ時間を割いて指摘するのは手間も時間もかかります。スペルミスがないかの確認など、機械的に解決できる部分はなるべくコンピュータに任せたいと考えました。
構築した環境・導入したツールの紹介
次に、実際に構築した動作確認環境や、各種ツール導入のきっかけ、良かったことを紹介していきます。
1. VirtualBox、Vagrant
各自のPC上に仮想環境を作り、動作確認ができるようにしました。仮想環境の構築には、VirtualBoxとVagrantを使用し、他モジュールと連携する部分は、全てスタブを用意しました。
社外のXML APIサーバと連携する部分についてはXMLを返すモックサーバを構築。Node.jsのexpressフレームワークを利用しています。
動作環境の構築後は共有で使っている環境やDBを壊す恐れがなくなり、気軽にコードを試せるようになりました。さらに、スタブやモックをしっかりと用意したことで、外部システムや外部APIサーバが予測外の動作をするケースでも非常にテストしやすくなりました。
2. PHP Coding Standards Fixer
私はGo言語を使った経験があるのですが、Go言語では言語自体にコードを自動整形するgofmt
というツールが用意されていました。PHPでもコーディングスタイルに悩んだり、スタイルに沿っていないことをレビューでわざわざ指摘したりしたくなかったため、同じような自動整形ツールがないか探してみました。
他に欲しいと思っていた機能は以下のとおり。
- よく使われているルール(PSR2)を利用できること
- ルールをカスタマイズできること
- 「どんな整形が行われるか」を確認できること(dry-run機能)
- 整形してファイルの上書き保存ができること
これらを踏まえたうえで、候補に挙がったのはPHP Coding Standards Fixerと、PHP_CodeSniffer。あまりツールの検討に時間を掛けたくなかったので、ひとまず新しい方のPHP Coding Standards Fixerを導入してみることに。使ってみて不満が出てきたら他も試してみようと思っていますが、今のところ不満はありません。
自動整形の例をお見せしますと、以下のような残念なコード*1が……
<?php function hogeFunc() { if(true) { echo "true"; } else { $a = 1; $bbbbbb = 8000; $ccc = 10; } if (false) { echo "hogehoge"; } }
ツールを適用すると、あら不思議!
<?php function hogeFunc() { if (true) { echo "true"; } else { $a = 1; $bbbbbb = 8000; $ccc = 10; } if (false) { echo "hogehoge"; } }
読みやすいコードになりました!
また、リポジトリにpushする前にツールを適用することで、コミット間のdiffにスタイルの違いが現れなくなりました。
pushする前にツールを通せば良いので、実装中は各々好きなスタイルでコーディングしてもらっています。ツール自体もシェルを叩けば実行できるようにしておいたので、実装に使うエディタも各自好きなものを使うことができ、チームメンバーにもストレスなく開発してもらえていると思います。
私達のチームでは、ツール実行用のシェルスクリプトを、対象にしたいファイルやディレクトリを引数に指定して実行するようにしています。適用したいルールはシェルスクリプトの中で指定しており、内容は以下のようになっています。
php php-cs-fixer.phar fix $1 --rules=\ '{ "@PSR2": true, "concat_space": {"spacing": "one"}, "method_argument_space": false, "native_function_casing": true, "no_empty_phpdoc": true, "not_operator_with_space": true, "binary_operator_spaces": {"align_double_arrow": true, "align_equals": true}, "phpdoc_add_missing_param_annotation": true, "phpdoc_align": true, "phpdoc_no_access": true, "phpdoc_types": true, "phpdoc_order": true }'\
適用できるルールを一通り眺めてみて、PSR2をベースとし、(私の好みで)適用した方が良さそうなルールをエイヤで決めました。各ルールの詳細は、公式サイトをご参照ください*2。
3. PHPMD
コードレビューで「この関数長すぎない?」「この関数複雑度高すぎない?」といったことを指摘したり、それぞれの上限を定義したりするのは骨が折れる作業です。
PHPの静的解析ツールをいくつか探した中で、「無料」「いろいろな種類の解析を行ってくれる」「Linux上で簡単に実行できそう」というところから、PHPMDを選択。オープンソースの静的解析ツールのなかでも利用ユーザが多いので、「多くのユーザが慣れ親しんだ開発ルールが採用されている」というのも決め手になりました。
導入してからは、(当たり前ですが)ツールが検出してくれることはコードレビューで指摘する必要がなくなりました。あわせて変数のスペルミスや、宣言だけ残っている変数の存在など、コードレビューで見落としがちな点も検知できて助かっています。関数の長さや複雑度の上限もはっきり定義され開発が進めやすくなりました。
コード品質の向上だけではありません。今までそれほど関数の長さや複雑度などについて気にしていなかった開発メンバーから、「シンプルなコードを書く癖がついた」「ツールが導入されていないプロジェクトでも、『こう書いたらツールに指摘されるな』という気持ちが働き、無意識にきれいなコードを書けるようになった」と言ってもらえました。プロジェクトの成果物(コード)の品質を上げようとして導入したツールにより、メンバーのスキルの底上げに繋がったのはとても嬉しかったです。
なお、私のチームで適用しているルールは、以下のようになっています。
<?xml version="1.0"?> <ruleset name="PHPMD rule set for point-exchange" xmlns="http://pmd.sf.net/ruleset/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> <description> My custom rule set for point-exchange </description> <rule ref="rulesets/cleancode.xml"> <exclude name="ElseExpression" /> <exclude name="StaticAccess" /> </rule> <rule ref="rulesets/codesize.xml" /> <rule ref="rulesets/controversial.xml" /> <rule ref="rulesets/design.xml" /> <rule ref="rulesets/naming.xml" /> <rule ref="rulesets/unusedcode.xml" /> </ruleset>
ツールのデフォルトルールは基本的に全て適用していますが、一部除外したルールもあります。
PHPMDで除外したルールとその理由
ElseExpression
ElseExpressionは、簡単に言うとElseが不要なときは書かないようにしよう、というルールです。しかし私は、マーティン・ファウラーさんの有名な本『リファクタリング』の一節
「if-then-else構造が使われるときは、if部にもelse部にも同じウエイトが置かれています。これは、プログラムの読み手に対して両方とも等しく起こり得ること、等しく重要であることを伝えます」という主張を支持しており、除外しました。
StaticAccess
StaticAccessは、その名の通りstaticアクセスを許さない、というルールです。こちらは、使用しているフレームワークや、外部のライブラリなどによってはアクセスが必要になったりもするため、除外しました。
時折、PHPMDで出てきた機械的な指摘がポイント交換開発の開発現場に即していないケースもあります。その場合は開発者間で相談した後、アノテーションをつけることで警告が出ないようにしました。PHPMDでは、SuppressWarningsというアノテーションをクラスコメントや関数コメントにつけることで、警告を抑制できます。何を抑制するか、を個別にルール指定することも可能です。私達のチームでは、SuppressWarningsをつけるときは以下のように必ず理由も添えるようにしています。
@SuppressWarnings(PHPMD.ShortVariable) // CodeIgniter のガイドに従い $CI を使用するため、除外する
4. PHPCPD
昨年、ドイツで開催されたInternational PHP Conference 2016にて知ったPHPCPDというツールは、コードの重複部分を検知してくれます。検出された重複部分は、関数への抽出やライブラリ化を検討することで、コードの保守性向上が狙えると考えました。
導入時点では他に似たようなツールがあるかどうかは知らず、かつConferenceで紹介されており、PHPUnitの作者が作っているということで安心感もあると思ったため、このツールの導入を決めました。実際に使ってみると、やはりコードレビューしていても気づかないような重複を検知することができ、保守性向上につなげることができたと思います。
コード規模が大きくなればなるほど、コードレビューでの重複の検知漏れが起きやすくなりますし、重複部分を関数やライブラリに抽出する効果が大きくなると思うので、このツールを導入して良かったと思います。
実際の出力例は、以下のような感じです(数値やファイルパスはフィクションです)。
phpcpd 2.0.4 by Sebastian Bergmann. Found 9 exact clones with 700 duplicated lines in 6 files: - /home/vagrant/point-exchange/program/Controller-1.php:14-97 /home/vagrant/point-exchange/program/Controller-2.php:14-97 - /home/vagrant/point-exchange/program/Model-1.php:142-314 /home/vagrant/point-exchange/program/Model-2.php:164-336 ... 23.40% duplicated lines out of 2992 total lines of code. Time: 123 ms, Memory: 6.00Mb
5. phpDocumentor
今までドキュメントを残す文化があまり強くなかったため、ドキュメントを残すようにしていきたいと思っていました。ドキュメントはコードを読む足がかりになるため、既存コンテンツの複雑なコードを読み解くときの助けになります。
一方で、ドキュメントを作成する時間をかけても、ついついコードとの乖離が大きくなりがちなので、保守性も高めるためにもコードからドキュメントを生成するツールを導入することにしました。
いくつか調べた中でも、一番有名そう、かつ一番使われていそうなphpDocumentorを使ってみることにしました。適切に書いたクラスコメントや関数コメントからドキュメントを生成してくれるツールです。
導入後は、様々なモジュールで共通に使うライブラリのパブリック関数の使い方などもドキュメント化され、コードを読み解く負担が減りました。いつかフローチャートやシーケンス図が生成できるようになるといいなと考えています。
また、ドキュメントを生成させるために、きちんと決まったフォーマットでコードにコメントを書く必要があります。ツールの導入によってクラスコメント・コードコメントのフォーマットが整うとともに、コードコメント自体が充実するという副次的な効果を得ることができました。
各種ツールを導入しての所感
今回、スクラッチでのサービス開発において、いろいろなツールを導入してみました。コードの品質を計測することは難しいですが、潜在的なバグ抑制や保守性の向上に効果があったと感じています。
開発の効率化やコード品質の向上以外にも、嬉しい効果がありました。メンバーのスキル底上げに繋がったり、コードコメント自体が整備されたり、といったような副次的な効果が、すごく良かったと思っています。
今後やってみたいこと
今後の展望としては、以下のことを考えています。
- CIサーバを構築し、ツール類を自動実行できるようにする
- 既存サービス(コード)にも適用し、開発効率を高める
- 成功した施策を他コンテンツへ導入し、他のチームへも展開
おわりに
今回は、PHPでのスクラッチ開発プロジェクトについて、さまざまな開発支援ツールを導入したお話を紹介させていただきました。 次は、もう少し実装に近いレベルで、どのようにツールを導入したのかをご紹介できればと思っています。