はじめに
猫会社として有名な 弊社 ですが、創業から受託開発を生業として15年が経ちました。社内の git リポジトリはプロジェクトが既に 300 プロジェクト、送られたマージリクエストは 5500 を超えました。30人前後(猫社員含む)の規模の会社にしては、経験値として胸を張って良いのではないかと思っています。
さて、そんな弊社ですが、2018年はとある大きな案件が始まったのを機に、これまでの集大成とするべく様々な開発手法を取り入れてみました。その結果、比較的経験の浅いメンバーで構成されたチームでも、品質を維持しつつレベルの高いアプリケーションの開発が出来ましたので、開発プロセスのモデルとして紹介させていただこうと思いました。
一つ一つは目新しいものではなく、これまでにもあった開発手法ですが、意味があったのは、私以外がほぼ開発1年目の新人を中心としたチームであったという事です。社内でもこれを成功体験とし、今後の開発のスタンダードとして取り入れようという意識が芽生えて欲しい、という希望と、これから大規模なシステム開発に挑もうとしている若手エンジニアの皆さんの参考になればと思い書かせていただきます。
プロジェクトの概要
プロジェクトは Zend Framework1 で作られていたとあるシステムのリプレイスです。機能数はざっくり60くらい。PHPだけで16万行、HTML/CSS入れると30万行規模のコードを Laravel でリプレイスするというのが今回のミッションでした。幸いにもリプレイスということで、既に動くコードと動く環境があったため、仕様の把握という時間はある程度省略できましたが、それでも機能数が多く、正攻法ではとても無理なので、いかに効率よい仕組みと体制を作るか、というのが当初の課題でした。
様々な議論の結果、最終的に採用した手法を順に紹介していきます。
シングルページアプリケーションの採用
ここ数年でWebのアプリケーションの構成は、ページごとに全てのHTMLを受け取り表示するレガシーな公式から、シングルページアプリケーション (SPA) と呼ばれるフロントエンド中心の開発方式に移行しつつあります。受託開発の現場でも、バックエンドとフロントエンドのコードの完全分離ができそれぞれの役割を分担できるので、人的リソースの調整がしやすいというメリットもあり、プロジェクトの大小にかかわらず採用が進んでいます。
今回のプロジェクトでも SPA の方式を採用し、フロントエンドの実装に Vue.js を使用しました。私個人の感想ですが、React / Angular / Vue.js を使用した案件をいくつか経験しましたが、一番直感的で学習コストが低いと感じたのが Vue.js だった、というのが採用の理由です。
フロントエンド側はその他にも Vuex や Bootstrap を使用し、UI コンポーネントはなるべく Vue のプラグインを使用して車輪の再発明をしないように努めました。
良かった点や得たものなど
- バックエンド側のロジックがJSONを受けて返すだけなのでとてもシンプルになり、再利用性が高まったこと。
- 似たような機能はコピペ+一括置換で複製できた。
悪かった点や苦労したこと
- ES6 の記述に慣れるのに時間がかかった。
参考
jQuery の徹底排除
Vue.js の採用により、必然的に jQuery は不要なものとなり、プロジェクト内のルールとしても jQuery の DOM 操作は禁止としました。理由としては、jQuery は DOM の構造に依存しますので、DOM の変更がダイレクトにロジックに影響します。また、jQuery がリアルな DOM を操作するのに対し、Vue.js は Virtual DOM と呼ばれる仮想的な DOM 環境を持っているため、処理内容に齟齬が発生する事、などが挙げられますが、そもそも Vue.js で DOM を操作する必要が無いというのが大きな理由です。
しかし、若いエンジニアの大多数は jQuery の経験しか無い、というのが当初心配ではありました。しかし若い人は吸収が早いですね。全くの杞憂で、むしろ Vue.js のリアクティブなシステムの方が直感的である、と、すぐに受け入れてもらえました。
良かった点や得たものなど
- DOM に依存したコードを書かなくて良いため、記述がシンプルになった。
悪かった点や苦労したこと
- Bootstrap の Javascript Component が使用できないため、ダイアログなどは自前で作成が必要であった。
- Laravel の mix 環境が jQuery 依存であったため、完全な排除が出来なかった。
参考
Homestead の採用
昔は開発用の物理サーバに環境を作り、修正したファイルをSFTPでアップしてブラウザで確認、というちまちました作業を繰り返して開発していました。最近では vagrant や docker などの仮想環境構築ツールも充実していますので、各自の開発マシン内に本番に近い環境を構築し、どの環境でも同じ動きを再現できる状態で開発を進めるのがデファクトスタンダードとなっています。
この方式の最大のメリットは、プロジェクト途中でヘルプの人材が来た場合でも、環境構築が自動化されているため、必要なツールをインストールするだけで手元に開発環境が準備できる、という事です。今回ももちろん仮想環境を使用しましたが、vagrant そのものを使用するのではなく、Laravel 公式の vagrant box 環境である、Homestead を採用しました。Laravel の開発に特化した専用の box であるため、環境構築や設定で無駄な時間を取られることがありません。また、一つの仮想環境で複数のプロジェクトを持つことができ、さらに PHP のバージョンもプロジェクトごとに変更が出来るため、古い案件を抱える受託開発では必須、ということで採用しました。
チーム内で学びがあったのは、PHPUnit の実行は Homestead 内ではなく、ホストOS上でコマンドを実行することで劇的に速くなる、という事でした。開発メンバは全員 Mac ですので、ホストOS上の php コマンドを使用し、vagrant のポートフォワーディング経由で DB にアクセスすることで5倍以上実行時間に差が出ます。PHPStorm など IDE との連携も出来るのでとてもおすすめです。
良かった点や得たものなど
- 環境構築に時間が取られない。
- 環境依存の問題がない。
悪かった点や苦労したこと
- チーム内でマシンスペックに差異があり、ストレージ不足や実効速度が効率に影響した。
参考
Linter ツールの導入
受託案件でも、他社との合同のプロジェクトや、ソースコードそのものを納めるプロジェクトでは、コーディングルールが厳しく決まっていたりしますが、結局忙しかったり、途中参加のエンジニアには共有できておらず、機能ごとに記述が違ったりと癖が出てしまうものです。
コーディングルールは誰が書いても同じコードになる、というのが理想で、統一することによってコードの可読性を上げ、保守性や拡張性を上げる目的があります。直接的にプログラムの実行結果には影響しませんので、どうでも良いと思われがちですが、書いたプログラムは書いている瞬間だけではなく、納品後もバグ調査や機能改修時など、長く続く案件では10年後もメンテが発生します。同じ機能でも人によって書き方が違ってしまうと、意味があって違うのか、あえてそう書いているのか、迷いが生じることもありますので、そういった迷いが10年間続くと考えると、どうでも良いことではありません。
今回はこれを防ぐために Linter ツールを導入し、チェックを自動化しました。バックエンドは PHP CodeSniffer、フロントエンドでは ESLint でコードを監視しています。後述の CI とも連携し、Linter のチェックに引っかかったコードは master にマージできません。当初これが足枷にならないか心配しましたが、editorconfig など、IDE のサポートもあり、慣れてしまえばどうということはありませんでした。
良かった点や得たものなど
- 開発者の経験や癖に左右されずコードの記述が統一される。
悪かった点や苦労したこと
- Linter のエラーがわかりにくいので、慣れるまでどこが悪いか気が付かない。
参考
GitLabフローの徹底
弊社では社内案件のソースコードを全て独自ホスティングの GitLab で管理しています。しかし運用フローとしては他社共同案件で GitHub を使用することもあるため、GitHub フローを採用していました。
今回は社内メンバーのみの開発となるため GitLab が推奨する「GitLabフロー」を採用しました。作業開始時に master からトピックブランチを作成し、マージリクエスト時は CI とコードレビューを実施してから master にマージする運用は、コードの品質を高めるだけでなく、若いエンジニアにとっても安心感があるようです。難点があるとしたら、レビュワーの負荷が高いという事ですが、これはまぁ何とか時間を作るしか無いかなと思っています。
コードレビューではコーディング標準ではカバーできないような設計面での記述を指摘することで、実装のブレを是正できます。エンジニアのレベルや経験に左右されないコードを維持するためには、コードレビューは不可欠だと思います。
また、今回は WIP(Work in Progress) フローも採用しました。WIPフローの説明は省略しますが、誰が何をやっているかという進捗が把握しやすいという点と、実装の経緯が共有できるというメリットがあります。ただ、GitLab のマージリクエスト画面でのリベースとローカルでのリベースがコンフリクトすると言った事故が一度あり、途中から使用を控えました。ここは今後の検討課題としたいと思います。
良かった点や得たものなど
- コードの品質が高まる。
- レビューやWIPブランチによって問題点がチーム内で共有できる。
悪かった点や苦労したこと
- レビュワーに負荷がかかる。
- WIP ブランチのリベースでコンフリクトが起きることがある。
参考
TDD
TDD(テスト駆動開発)は、これまでなかなか社内に浸透しなかったのですが、今回のプロジェクトで徹底したことにより習慣化しました。「逆にテストしていないプロジェクトが不安」という意識も生まれ、TDD推進派の私としては一番の収穫だと思っています。
その背景として、TDDの難しい理論から入ったのではなく、自分の書いたコードが正しいかどうか、他人の書いた機能に影響しないか、デグレにならないか、と言った不安を払拭してくれるツールとしてのユニットテスト、という認識であったのが、浸透した理由として大きいと思います。
また、前述の通り、バックエンドのコードは REST API で JSON を返すものがほとんどですので、ユニットテストがしやすいという副産物もありました。今回は Laravel の PHPUnit ベースのテストケースクラスを多用しましたが、ひたすらレスポンスのJSONをアサーションするというシンプルなテストが中心となり、さらに PHPUnit のデータプロバイダ機能を多用することで手軽にテストパターンを豊富に用意できるため、テストクラスの作成にかける時間も少なく済みました。
あと、PHPUnit のカバレッジ機能もテストの習慣化に役立ったと思います。GitLab 上ではテストの網羅率が数字化されコミット単位でその推移が見えます。誰か一人でもテストをサボると全体のカバレッジが低下しますし、コードレビューでも突っ込まれるので、自分で書いたコードは自分でテストを書く、という習慣が根付きました。
良かった点や得たものなど
- テストの意識が浸透した。
- ミドルウェアやライブラリのバージョンアップ時にも、どこで影響が出るかが一目瞭然だった。
悪かった点や苦労したこと
- Laravel Passport の問題でブラウザのテストに制限があり、フロントエンドのテストが網羅できなかった。
参考
CI
今回、GitLabフローやTDDなどのルールが定着した理由の一つにプロジェクトの早い段階で CI を導入した事だと思っています。今回は GitLab を使用していますので、標準で提供される GitLab CI を使用しました。CI のパイプラインでは、ビルドフェーズにてバックエンドの構築とフロントエンドのビルドを実施し、テストフェーズでは PHP CodeSniffer でのコーディングルールチェックと PHPUnit のテストを実行します。これらがコミットごとに実行され、失敗した場合はマージリクエストを承認できないようになっていますので、システム上の仕組みとしてフローが徹底されるわけです。
また、ルールやテストなどは面倒ですが、自分の書いたコードがコミットの度に合否判定される仕組みは、クリアしたり失敗したりというゲーム感覚に近く、この感覚も今回のルール徹底の手助けになったと思います。
良かった点や得たものなど
- コミットごとにエラーが共有できバグの混入を防ぐことが出来た。
悪かった点や苦労したこと
- パイプラインの実効速度が遅くチューニングに苦労した。
参考
まとめ
大規模プロジェクトではいかに早く量産体制を作れるか、がポイントだと思っています。似たようなページはできるだけ抽象化し、差分記述だけで実装できるようなリファクタリングクラスを作るなど、コピペベースで量産できるような仕組みがあれば軌道に乗ることが出来ます。しかし、量産された機能をチェックする仕組みが無いと、品質面での不安がどうしても拭いきれません。そこでコードレビューやユニットテストでその実装の品質をチェックする仕組みが最優先で必要だと判断し準備を進めました。
今回は一番年下のメンバーが非常に優秀で、あまり社内に居なかった私に代わり、多くの開発手法を取り入れてくれました。チームメンバーも彼の意見をよく聞き、レビューや相談も活発に行われ、良いチームに育ったと思います。
私も含め、みんなにとってこのプロジェクトが貴重な経験になったのは間違いありません。来年もみんなよろしくね!