理想の開発チームに近づくために、この半年間チームとして取り組んだこと

この記事は Recruit Engineers Advent Calendar 2016 の23日目の記事です。

昨日はneko-nekoさんの「Terraformで始めるRolling deployment」でした。いやー、うちも試したい!

はじめに

2016年6月から11月末まで、とあるWebアプリケーション(以降、プロダクトA)の再構築プロジェクトに参画してました。(今も同じチームでエンハンス開発してます)

再構築の目的は、今後プロダクトAを事業としてさらにスケールさせるために、障壁となるシステム課題を解決・改善すること。

この記事では、再構築プロジェクトを進めながら、その後を見据えた理想の開発チームに少しでも近づくために、チームとして約半年間取り組んだことについて、ざっくり紹介してみようと思います。

なお、アーキテクチャや設計に関することは、話せば長くなるのでこの記事では言及しません。主にプロセス面や開発を支える仕組みについてのみ書きます。

先にまとめ

記事がすごく長くなってしまったので、取り組んだことの見出しを最初にまとめておきます。

  • 毎日朝会を開催する
  • 2週間ごとに振り返り会を開催する
  • テストコードを書く
  • マージする前にコードレビューする
  • 静的解析を導入する
  • 継続的インテグレーション
  • Infrastructure as Code

全部、目新しいことはなく、世の中では当たり前のことかもしれません。ただ、それができてなかった状態から、チームとしてできるようになったこと自体は、とても素晴らしいことだと思います。

「ふーん」と読み進めていただければ幸いです。

埋めたかったギャップ

再構築が完了したあとの新しいシステムの上で、開発チームがリーンスタートアップのような仮説検証サイクルを高速に回せている状態をゴールイメージとおき、プロジェクトがスタートしました。

上記の状態を実現する上で、解決しなければならかったギャップとして、大きく以下の2点がありました。

  • システム的な課題
    • システムの規模に対して技術的負債の蓄積量が多く、保守運用性が著しく低い(★再構築プロジェクトで解決したかったそもそもの課題)
    • 負債の解消・蓄積防止のための仕組みの欠如が課題
  • プロセス的な課題
    • 透明性や改善サイクルの欠如、属人化が課題

どちらか片方だけが解決しても、目指したい状態は実現できません。システムとプロセス、両輪がスムーズに回っている必要があります。

再構築が完了した後も、引き続き同じメンバーで開発を継続する予定だったので、プロジェクトで解決したかったそもそもの課題解決だけではなくプロセスや仕組み面の再構築についても並行して進める必要がありました。

開発チームの体制について

前提になるので、ざっくり紹介しておきます。(ビジネスサイドについては割愛)

プロジェクトには、時期にもよりますが6〜8人のエンジニアが開発チームとして参加していて、僕もそのうちの一人でした。再構築プロジェクトの開始タイミングで参画したメンバーもいれば、プロジェクト開始前から参画していたメンバーもいます。

いわゆるプロジェクトマネージャ(リーダ)的な役割は専任としてはおらず、一部の役割を僕が兼任していました。

では、それぞれの取り組みについて紹介します。

毎日朝会を開催する

解決・改善した課題は以下の通り。

  • 誰が何をやっているのかお互いに知らず、作業の属人化が激しい
  • 気がついたら同じ課題に複数のエンジニアが取り組んでいることがある
  • 全員がプロジェクトの進捗状況を把握できてない
  • 今日どこまでやれば帰っていいのかわからない

いわゆる普通の朝会です。以下を全員が順番に発表した後、全員でプロジェクトの進捗状況をバーンダウンチャートを使って確認していました。

  • 昨日やったことの共有
  • 発生した課題の共有
  • 今日やることの宣言

プロジェクトはウォータフォール型開発で進めていましたが、アジャイル開発じゃなくても朝会は有用で、上記課題に対して一定の効果がありました。再構築完了後はアジャイル開発を導入しようと考えていたので、助走してもよかったと思います。

ただ、発生した課題の共有については、時間の制約もあり全てが朝会の場で共有されるわけではなかったです。情報共有の課題については、別のアプローチも考える必要がありそうです。

2週間ごとに振り返り会を開催する

解決・改善した課題は以下の通り。

  • チームの活動を定期的に振り返る習慣が無い
  • 振り返り結果にもとづいた、改善のための打ち手をチームで議論する場がない
  • 改善の結果がどうだったかを振り返る習慣がない

いわゆる普通のKPTです。開発をすすめる中で課題やムダは毎日見つかるので、再構築完了を待たず、2週間ごとにKPTを実施するようにしました。この記事で書いている他の取り組みにも、KPTの場で議論した内容が反映されています。

最初は「ありえないレベル」の課題とそれに対する改善策をみんなで議論するシーンが多かったのですが、徐々に課題の質も変わっていき「より良いチームになるため」の課題が出てくるようになりました。朝会と同様に、こちらも再構築完了後を見据えた助走としてよかったと思います。

ただ、課題の質・難易度が上がってくるにつれて、改善策も一朝一夕とは行かないものが増えていきました。再構築中は開発の工数確保を優先していたため、挙がった改善策が実施されずないまま放置されることが多くなり、再構築完了後に解決する必要がある大きな課題となって残りました。

テストコードを書く

解決・改善した課題は以下の通り。

  • (再構築前のシステムに)テストコードが存在せず、改修時のデグレリスクが高い / リファクタリングが難しい
  • (再構築前のシステムに)後からテストコードを追加しようにも、テスタビリティが低い実装になっているため難しい

目指す状態である高速な開発サイクルを実現する上で、一定の品質を保つためには自動テストが必須であると判断。再構築のタイミングでは、最初からテスタビリティを意識した設計を行い、製造フェーズでテストコードの実装まで完了することを前提とした計画を立てました。

ある機能を実装する際は、機能のコードにだけではなく、テストコードも実装しないとタスクとしては完了ではない、ということにしました。テスト対象としては、

  • モデル、サービスクラス(ビジネスロジック)単体
  • コントローラより下のレイヤーを結合したもの

の2点を定義し、正しく仕様通りに動作するかを検証するためのテストコードを実装しました。

上記のテストコードがあったおかげで、プロジェクト後半にバグ修正や、リファクタリングを実施する際の工数・リスクがかなり軽減できました。

一方、今回フロントエンドや、フロントエンドとバックエンドの結合部分についてはテストコードを実装していませんでした。もちろん、手動でもテストは実施していたのですが、プロジェクト終盤やリリース後にその部分でバグが発生することが多かったように思います。そのあたりのテストを今後どうするかはについては、再構築完了後の宿題として残りました。

マージする前にコードレビューする

解決・改善した課題は以下の通り。

  • Pull Requestマージの条件についての認識がメンバー間で揃っておらず、場合によってはコードレビューなしでPull Requestがマージされることがあった
  • コードレビューが実施されないことがあるため、技術的負債の蓄積を防止できない
  • あとで技術的負債が発覚したとしても、影響範囲が大きくなっており、容易に解消できない

再構築前のチームではGitHub Flowを、再構築中のチームではgit-flowを採用。GitHub EnterpriseのPull Request機能を使ってブランチをマージしていました。

ですが、もともとブランチのマージ条件が曖昧だったこともあり、上記課題が発生。再構築プロジェクトの開始タイミングで、Pull Requestのマージ条件について全員で認識を合わせました。

具体的には、すべての修正に対し、以下の条件を満たさないとマージできないという形にしました。

  • 自分以外の誰か一人にコードレビューをしてもらいLGTMをもらうこと(具体的な観点は割愛)
  • 機能のコードだけではなく、上述のテストコードもPull Requestに含まれていること。
  • CIのビルドがパスしていること(後述)

開発の進捗が遅れていて余裕がないときも、上記のルールを守り続けることができており、これは再構築完了後の開発プロセスを見据えたうえで、大きな財産となりました。

一方で、レビュワーが特定のメンバーに偏る、レビュワーによってレビュー観点の抜け漏れや指摘の質に差がある、といった課題も発生し、一部は未解決のまま再構築完了後の宿題となりました。

静的解析を導入する

解決・改善した課題は以下の通り。

  • レビュワーにコードレビューの観点がずれる
  • 細かいところまでレビューすると、レビュワーの負担が非常に高い
  • レビュワーとの意見の相違が発生したときに、なかなか議論が着地しない

とりあえず、静的解析を導入しました。導入したのは以下のツールになります。

基本的にはツールデフォルトのルールを採用し、「これは流石に守れない」というケースがあった場合には、やむを得ずルールを無効化するという運用をとっています。

静的解析ツールによって、ある程度の観点についてはツールが自動的に指摘してくれるようになり、レビュワーの負担が大幅に軽減されました。

ルールを無効化する条件や、タイミングについては、個人の判断にゆだねていた部分があったのですが、これはまず間違いなくルールとして明文化すべきでした。ルールが無効化されている箇所は、可読性が悪く、保守性が低い実装になっていたり、バグが発生しやすい傾向になっているように思います。(これはあくまでも自分の肌感覚)

また、静的解析ツールが本質的ではない指摘をすることがある、という声も実際に挙がっており、使い方については再構築完了後も改善の余地がありそうです。

継続的インテグレーション

解決・改善した課題は以下の通り。

  • 不具合混入やデグレに気づくことができない
  • 気づくタイミングが遅く、気づいたときには影響範囲が大きくなっている

テストコードの実行、静的解析の実行をコミットをPushしたタイミングで自動的に実施てくれるCI環境を普通に構築しました。

Jenkins 2 + JenkinsfileをつかってPipelineを定義し、ビルド結果はGitHub Enterprise上にも表示。Pull Request時にコミットごとのビルド成功/失敗がひと目で分かるので、レビュワーの負荷も軽減できました。

Infrastructure as Code

解決・改善した課題は以下の通り。

  • インフラ環境の構築、運用、構成変更作業が特定のメンバーに属人化
  • インフラの構成情報が色んな場所に分散、どれが正しいの問題

以下のツールを使ってインフラの構成管理をコード化しました。

上記の技術さえ使えれば、インフラが今どのような構成になっているかを把握することができ、カンタンなメンテナンス・構成変更であれば、Jenkins上のビルドを実行すれば誰でも行なえます。

ただ、以下のように課題も多く、再構築完了後も取り組む必要がある宿題として残りました。

  • インフラの設計思想はコードに残りにくいが、それを知っておかないと大規模な構成変更はできない
  • 構成管理のコードにも技術的負債がたまる。気をつけないと、誰もメンテナンスできないコードになる
  • Packer / Terraform / Ansible 全部を上手く使える人は、まだチームにそれほど多くはない

まとめ

細かい点は他にもありますが、以上が、理想の開発チームに近づくために、この半年間チームとして取り組んだことでした。

一つ一つが抽象的で、具体的な部分は書いていない部分もありますが、それはまた別の機会に紹介できればと思います。

再構築プロジェクトは無事終了し、チームは今、スクラムによるエンハンス開発フェーズに入っています。開発のリードタイムをできるだけ短くするために、様々んな改善・ムダ取りに取り組もうとしています。

まだまだ課題は山積みですが、半年間で色んなことに取り組めたチームなので、これからもどんどん改善は進みそうです。

次はusaganikkiさんの番です!よろしくお願いします!