ソフトウェア開発において、リリース(出荷)をどの程度の頻度で行うかには、様々な考え方がある。 ここでは、話を単純にするために、古典的な3層ウェブアプリケーションを題材にして考えてみよう。
リリースをどう捉えるか
そもそも、リリースを通じてソフトウェアに変更を加えることには、リスクが伴なう。それまで「問題なく*1」動作していたソフトウェアに変更を加えることで、その状態が損なわれる可能性がある。事なかれ主義に走るのなら、うまく動いているソフトウェアにわざわざ手をいれる必要は無いのだ。
一方で、ソフトウェアのリリースを全くしないことには、新しい機能をユーザーに届けたり、これまで問題があった部分を修正することができないのも事実だ。「リリース」という行為に消極的になったり、その頻度を落とすということは、「ソフトウェアの変更を失敗する」というリスクからは自由になるが、その分ソフトウェアの成長も放棄してしまうと言える。*2 新しい価値を加えることが難しくなってしまったソフトウェアは、乱暴に言ってしまえばその時点で将来にわたっての評価も失う、ということなのかもしれない。
リリース頻度をどう捉えるか
近年は Google の DORA (DevOps Research and Assessment) による調査に基づいた書籍『LeanとDevOpsの科学』の提唱した "4 Keys" (デプロイ頻度、変更リードタイム、変更障害率、平均復旧所要時間) をきっかけに、「高頻度のデプロイを目指して開発組織を整備しよう」という風潮が強い。
しかし DORA が調査し提唱したのは「ハイパフォーマンスなソフトウェア開発チームを調査した結果、それらを特徴づける4つの指標が明らかになった」ということであって、「この4つの指標を改善すればソフトウェア開発のパフォーマンスが上がる」という因果を示しているわけではない。極端な例だが、価値をもたらさないリリースを頻繁に行えば「リリース頻度」という指標は改善するが、現実にはソフトウェア開発の本質的なパフォーマンスは向上しないだろう。
そうではなくて、「ハイパフォーマンスなソフトウェア開発組織」は「なぜ 4 Keys 指標が優秀なのか」を理解しようと試みてはどうだろうか。そうすれば、自分たちのソフトウェア開発に対するマインドセットをどう変えていけばよいのかが見えてくるのではないだろうか。
リリース頻度を高めるメリット
では、ソフトウェアのリリース頻度を高めると、どんなメリットがあるだろうか。以下に述べるものが全てではないが、パッと思いつくものを見てみよう。
リリースのリスクが軽減される
リリース周期が長く、例えば半年に1回しかリリースを行っていない組織を想像してみてほしい。これからリリースしようとするソフトウェアと、半年前にリリースしたソフトウェアには膨大なに差分が存在している。どれだけ膨大な差分なのかはコンテキストに依るだろうが、他の条件が固定であれば、1週間の間に生じる差分より少なくなるということはあり得ないだろう。
ソフトウェアに膨大な差分があるとは、どういうことだろうか。データベース・スキーマには後方互換性のない *3 変更が入っているだろうし、リリース作業の中でITインフラの構成も大きく変える必要があるかもしれない。今までとはユーザーインターフェイスも大きく変わるかもしれないし、外部に存在するAPIへの依存も新たに追加されているかもしれない。
「リリースしようとするソフトウェアと現行のソフトウェアの間の差分が大きい」ことは、そのリリースが「巨大で未知数な作業」となることを意味する。つまり、リリース周期が長く頻度が少ないということは、毎回のリリースが常にその「巨大で未知数な作業」にあたることを意味しているわけだ。想像するだけで、半年ごとのリリース作業が憂鬱になる。この状況を、どうにか回避したい。
逆に考えると、短い周期で定常的にリリース作業を行っていれば、そのリリースはほとんどが「既知な部分が多く、認知可能な程度の規模の作業」になる。なぜなら、リリース作業の周期が短いために変更差分が小さくなるのと、頻度が高くなるためにその作業に対する習熟度が高くなるからだ。逆説的だが、リスクの伴うリリースという作業を毎日行えるようになると、自ずとリリースそのものから生じるリスクは小さくなるのだ。
もちろん、データベース・スキーマに後方互換性がない変更を加えたり、新しいインフラストラクチャ要素を追加することは実務上避けられない。ただ、こういった「未知数な作業」は、それを認識して人間の認知リソースを集中透過させれば良い。それを実現するためには、「未知数な変更」と「既知な部分が多い変更」を分けて扱い、「既知な部分が多い変更」のリリースを圧倒的に得意になる必要がある。
価値提供の最大化
ある機能の開発が完了して、出荷されるのが「今すぐ」なのか、何も起こらなくても「6ヶ月間」待たされるのか、の違いを想像してみてほしい。6ヶ月後には、その機能の旬が過ぎていたり、その機能が欲しかった動機そのものをユーザーが忘れてしまっていたり、他の競合製品で事足りるようになってしまっているかもしれない。仮にそんな残念なことは起きずに、6ヶ月後に無事リリースできたとしても、「この6ヶ月間」は純然たる機会損失があったことに違いはない。
ここらへんは、id:i2key さんの以下のスライドが圧倒的にわかりやすい。一読することをおすすめする。 *4
ユーザーに届いた価値は、単純にある時点で観測される大小ではなく、それに時間を掛け算した「面積」なのだ。だから、機能をユーザーに届けるのは、他の条件が同じならば早ければ早いほど良い。そう考えると "4 Keys" には「変更を完了するリードタイム」が含まれているのにも納得できる。そして、短い変更リードタイムを実現するには、頻繁なリリースができる能力が欠かせない。
市場からの迅速なフィードバックが生むポジティブな循環
高頻度のリリースを実現することで、市場からのフィードバックを素早く得られるようになる。これはプロダクト開発において非常に重要な利点である。極端な話、どんな機能がユーザーにウケるのか、開発者が唸って考えるよりも、ユーザーの反応を見たほうが早い。その試行回数は多ければ多いほどよい。
また、高頻度のリリースがもたらすフィードバックは開発する側にもユーザーにもポジティブな感情をもたらす。ユーザーのフィードバックに基づいた迅速な改善は、当然ながら顧客満足度を向上させることができる。ユーザーは自分たちの声が製品に反映されていることを実感し、製品への愛着が深まる。他方、開発チームは自分たちの成果が迅速にユーザーの手に届き、フィードバックを得られることで、モチベーションが向上する。この好循環は信頼関係に基づくものだから、一朝一夕に得られるものではない。もし手に入れられたのなら、プロダクト開発を進めるうえでの大きな武器になる。
個人的な思い出を1つ語る。以前働いていた職場で esa.io を利用していたことがあった。細かな機能だったが要望を思いつき(コードブロックに "クリップボードへコピー" のボタンを付けてほしいというものだった)、問い合わせフォームに書いたことがあった。驚いたのは、その2,3日後には「コードブロック欄にクリップボードにコピーする」ボタンが表示されるような実装をした、とメールが送られてきたことだった。僕はその過程で esa.io のことはとても好きになったし、ユーザーを引き寄せ続けるためにはこの瞬発力が必要なんだな、と学ぶことになる出来事だったように思う。
高頻度のリリースをどう実現するか
無論、無策でただ高頻度のリリースを行ったからといって、これらのメリットを無条件に享受できるわけではなくて、様々なエンジニアリング・プラクティスの意図を理解して実践することが必須条件になる。本稿では簡単にしか触れられないが、高頻度のリリースを支えるプラクティスを見ていこう。
リリースプロセスの把握(バリューストリームマッピング)
すでにあるリリースプロセスを改善し、今よりも高頻度なリリースを実現しようとするのなら、まずは現状を把握する事から始める。ここでも、パフォーマンス改善の初手は「推測するな、計測せよ」なのである。その時に役に立つのがバリューストリームマッピングだ。
当然のことだが、プロダクトや組織が変われば、描かれるバリューストリームマッピングは驚くほど変わる。何を作っているか、誰が作っているか、誰に向けて作っているかなどによって、プロダクトに求められる品質特性や説明責任が異なる。それらが異なれば、どれくらい慎重にプロダクトをリリースするかも変わるし、結果としてソフトウェアのリリース頻度を高めることへのハードルも変わるだろう。
しかし、重要なのは「必ずリリースプロセスのどこかにムダがあるはずだ」という前提に立ち、その無駄を探し出すことだ。しかも、一時的に観測されたボトルネックを解消したとしてもシステムは完璧にはならず、必ず他の箇所に新しいボトルネックが生じる。それを絶え間なく特定し、つぶさに潰し続けてゆくことで、リリース頻度を向上させられるようになってゆく。
テストの自動化(継続的インテグレーション)
リリース作業はプロダクトにリスクもリターンももたらす。そんなリリースという作業からリスクを排除する方法として真っ先に思い浮かぶのがソフトウェアテストだ。
ソフトウェアテストには様々な粒度や形態があるが、リリースの頻度が上がり、その度にリリースしようとしているモジュールのテストが必要になるのなら、そのテストは「いかに自動化されているか」が大事になる。もちろん、リリースの内容によっては人の眼で確認しないと安心できないこともあるだろうが、毎回のリリースで必ず何らかの目視や確認作業が求められたり、一定規模のリグレッションテストの手動打鍵が必要になると、「一度のリリースで複数の機能をまとめてリリースして、これらの検証の回数を節約しようよ」という誘惑に駆られてしまう。その誘惑に駆られないように、繰り返し実行でき、信頼できる自動化されたテストを育てていかなければいけない。
ソフトウェアの自動テストという領域について学びを進めると、自動化されたソフトウェアテストのボリュームを表すフレームワークに「テスト自動化ピラミッド」というものが出てくる。しかし、近年ではこの黄金比を見直すような風潮も見られるようになってきた。世にあふれるプラクティスを盲信するのではなく、「自分たちのソフトウェアは出荷して大丈夫だ」という自信を得るという根本的な目的と、継続的なリリースを諦めない程度に短い時間で完了することのトレードオフを自分たちで模索する必要があると言えるだろう。
自動化されたリリースプロセス
当然ながら、テストまでが自動化されていても、ソフトウェアを実際の商用環境 *5 にデプロイする作業が自動化されていなければ元も子もない。定例的なデプロイ作業が手動であることは、ミスが直接の商用環境障害につながるというリスクであることを意味する。機械が得意なことを機械に任せるために、自動化されたリリースプロセスを実現できないか、検討しよう。
テストの自動化と同じように、すべてのリリース作業が必ずしも同じ手順で行われるとは限らない。しかし、突き詰めれば大半のリリース作業は定型化できる。定型化できる作業が自動化されていれば、人間のマインドシェアはイレギュラーな作業に集中させることができる。
自動化されたリリースプロセスは、できることならバージョン管理システム(VCS)と密接な関係となることが多い。自動化されたリリースプロセスにソフトウェアのどの断面を載せて出荷するかは、バージョン管理システム上で表現される *6 からだ。だから、GitHub における GitHub Actions にしろ、GitLab における GitLab CI にしろ、バージョン管理システムと高度に統合された処理自動化プラットフォームが併設されているのだ。
終わりに: 継続的デリバリー
知っている人には今更のこととなるが、ここに書かれた「高頻度のリリースをどう実現するか」のプラクティスを「継続的デリバリー」(Continuous Delivery, 略して CD)という。ちょうど最近、この話題を学ぶのに最適な書籍が出版された。タイトルはそのまま『入門 継続的デリバリー』だ。
この書籍では、「継続的デリバリー」の定義を以下のように設けている。
ソフトウェア開発において、チームがソフトウェアの変更を安全、迅速、かつ持続的にユーザーにリリースする手法であり、以下の2つを実践している。
- いつでも変更がリリース可能であることを立証している。
- リリースプロセスを自動化している。
(『入門 継続的デリバリー』p.7 より)
1つ目の「立証」は、前述した「テストの自動化」に当たる。そして「リリースプロセスの自動化」は、その文字通りの「自動化されたリリースプロセス」にあたっている。この書籍の中では、本稿で取り上げたトピック以外のことも含めて、現実的でチームを自信に満ち溢れさせるためのプロセスを網羅的に紹介している。まだ半分程度を駆け足で読んだ程度だけれども、ソフトウェア開発を通じて、その先の価値創出にフォーカスしたチームを目指す人々には必読の本になるような気がするのでオススメだ。
*1:この「問題なく」をどう定義するかが重要なのだが、いったん脇に置いておく。
*2:最も、ソフトウェアへの変更を止めることは、変更を失敗するというリスクは低減できても、新しいセキュリティ上の攻撃に対処できないとか、依存しているライブラリ・フレームワーク・プラットフォームなどのEOLに対処することができなくなる。内部由来のリスクには対処できても、外部環境に起因するリスクに対処できなくなるのだ
*3:新しいバージョンのソフトウェアやシステム、データベース・スキーマなどが、古いバージョンのデータや機能を正しく扱えなかったり、古いバージョンのソフトウェアと組み合わせて動作させられない状態を指す
*4:実はこれまでこのブログでは、何度もこの話を取り上げている。
*5:本番環境とも呼ぶね
*6:Gitでいえば main ブランチへのマージだったり、タグの作成だったりする