Skip to main content

依存関係の継続的な更新:課題を前倒ししてプロセスを改善する

著者
Headshot of James Bowes

James Bowes

2021年5月24日

0 分で読めます

これは、課題を前倒しし、まず実行してから許しを請い、継続的に改善していった話です。Manifoldの初期、つまりSnykに加わるずっと前、私たちはデータベースやトランザクションメールサービスなど、開発者向けサービスの独立したマーケットプレイスを構築していました。コードの構成はごく一般的で、Reactのフロントエンドアプリと、データベースと通信する複数のGoマイクロサービスで成り立っていました。一般的な構成には、よくある問題もつきものです。その一つが、依存関係を最新の状態に保つことでした。チームのシニアメンバーとして、そして会社に長く在籍し、私たちの問題の多くを引き起こしてきた一人として、協力してくれる仲間とともに依存関係の問題を解決することにしました。

「依存関係の更新には多くの手間がかかる!」

長年にわたり、さまざまな言語やフレームワークで、依存関係の更新に対する不満は絶えません。新機能の開発や新たなバグの修正に更新が必要な場合(その場合でさえも)を除けば、この作業は骨が折れるうえ、見返りもほとんどありません。そもそも、更新があることをどうやって知るのでしょうか。その変更は役に立つのでしょうか。それともコンパイルが通らなくなったり、バグが発生したりするのでしょうか。数え切れないほどのファイルで関数のシグネチャを更新するとき、ほかの変更とどれだけ競合するのでしょうか。

作業が大変で見返りも少なければ、先延ばしにしたり、完全に放置したりするのは自然なことです。

「React Routerの更新に2スプリントかかります。」

やがて、童話に出てくるような状況に陥ります。夏の間ずっと依存関係の更新を先延ばしにしているうちに冬が訪れ、grasshopper-foodパッケージを少し更新する必要が出てきます。しかし、そのパッケージ自体の依存関係の更新がコードベースとあまりに多く競合していて、どれくらい時間がかかるのか見当もつきません。

Manifoldで特に深刻だったのは、Reactで使っていたルーターのバージョンアップでした。フロントエンドのコードベースで依存関係を最新の状態に保っていませんでした。ルーターライブラリの新しいメジャーバージョンに移行しようとしたとき、APIシグネチャの細かな変更が、依存関係全体にわたる無数の競合や非互換性と絡み合っていました。使い始めてから最初にアップグレードに取りかかるまでの間に、ほかのパッケージは何度も更新されていました。作業を分割するために、小刻みなリリースを探しながら、ルーターのアップグレードとほかの依存関係を切り分けるのは骨の折れる作業でした。

当初、アップグレードは大変すぎるように思えたため、私たちはまた先延ばしにしました。習慣を変えないまま待つ時間が長くなるほど、依存関係の更新が増えて問題は膨らんでいきます。待てば待つほど、ルーターのバージョン更新に必要な時間も増えていきました。

「課題を前倒しする。」

依存関係の更新は大変ですが、「課題を前倒しする」ために最適です。つまり、より頻繁かつ明示的に依存関係を更新します。依存関係は時間とともに更新されるため、更新頻度を上げれば、一度の変更が小さくなり、全体の作業量を減らせます(ある意味、これは古典的な継続的インテグレーションです)。変更が小さければ問題を明確に定義できます。「依存関係の更新は大変だ」と言う代わりに、「パッケージXをバージョンYに更新するには、この12個のファイルを変更する必要がある」と言えるようになります。残った手間に向き合うことで、自動化などの方法を通じて、その手間を減らせるようになります。

Manifoldでは、すべてのリポジトリで依存関係の自動更新を有効にしました。Snykのようなツールがそろっていたため、この変更の導入は難しくなく、一人でも実行でき、大きな効果をもたらせるものでした。リポジトリの所有者に「許可を求める」のではなく、まず実行してから「許しを請う」形で変更できました。合意形成も正式な決定も必要ありませんでした。実行者は、変更にかかるコスト(少ない労力)と期待できる効果(大きい)、そして元に戻す難しさ(低く、スイッチを切るだけ)を比較検討しました。

実行者は複数のリポジトリを変更し、ほかの開発者に変更内容とその理由、期待できるメリットを伝えました。そして、依存関係の更新を望まない人には、変更を元に戻す方法を尋ねてもらうか、自分で戻してもらうよう案内しました。

徐々にほかの開発者も、残りのリポジトリで依存関係の自動更新を有効にしました。やがて、更新が毎日届くようになりました。

「自動更新のプルリクエストのレビューに時間をかけすぎています。」

以前はめったに起きず、不安定で怖かったことが毎日起こるようになるのは、最初は奇妙に感じます。やがて誰もが慣れ、依存関係の更新という元の問題が、もはや問題ではなくなったことに安心するようになります。この段階に達すると、関心は移り、依存関係更新に伴う次の最も大きな苦痛が、新たな大問題になります。

自動でプルリクエストを作成する仕組みがあっても、依存関係を毎日更新すれば、依然として多くの手間がかかります。Manifoldでは、開発者が依存関係更新のプルリクエストのレビューや承認に時間をかけすぎていると感じることが、新たな大問題になりました。その結果、次の3つの行動が見られました。

  • プルリクエストを無視し、レビュー待ちのキューに放置する。

  • 自動更新を停止するよう求めたり、どんな方法でもいいから問題を解決するよう求めたりして、状況への不満を大きな声で訴える。

  • プルリクエストのレビューを(簡単に)続け、承認してマージする。

2つ目の行動(大きな声で不満を訴えること)が、依存関係更新プロセスを継続的に改善する原動力になりました。

プロセスの改善を実施したばかりの人は、次に何をすべきかある程度アイデアを持っている一方で、現状にも満足している可能性が高いものです。一方、変更に関わっていない開発者は距離を置いて見られるため、差し迫った問題点に気づきやすい傾向があります。問題を無視したり受け入れたりせず、声を上げた開発者のおかげで、私たちは前進を続けられました(新しいプロセス改善が不十分だと言われるのは、つらいこともありますが)。

私たちは、変更や改善を少しずつ進め、元に戻しやすくし、謙虚な姿勢で提案するよう努めました。依存関係更新のプルリクエストが大量に届くことへの不満が大きくなると、改善を実施した開発者は、自動更新を停止しても構わないと伝えつつ、代わりに負担を軽減する方法を実装するための時間がほしいと頼みました。コードベースの所有者が常に主導権を持っていましたが、改善が行われると信頼してくれていました。以前にも改善が実現するのを見ていたからです。

依存関係の自動更新を無効にしたり頻度を下げたりするのではなく、テストに合格した場合に自動で承認・マージする仕組みを用意しました。

「依存関係の自動更新によって、継続的デプロイのパイプラインにバックログが発生しています。」

改善を何度も重ねるうちに、感じている苦痛は、最初に前倒ししたときとはまったく違うものになります。そして、自分たちの成功によって生じる「うれしい悩み」を抱えるようになります。こうした問題の解決には、より多くの労力が必要になることもありますが、より広い範囲にメリットをもたらすことが少なくありません。

依存関係更新のプルリクエストを自動マージするようになると、Manifoldで更新頻度が高いサービスでは、次の問題が発生しました。私たちはすでにGitOpsパイプラインを運用しており、変更をPRからステージング、そして本番環境へと進めていました。変更をステージング環境にまとめて適用し、その同じ変更セットを本番環境にデプロイする仕組みです。本番環境へのデプロイにはサービス所有者の承認が必要で、ステージング環境に反映された後は変更セットを修正しませんでした。そのため、開発者による変更が、自動生成された依存関係更新のプルリクエストのバックログの後ろに滞留するようになりました。

実際には、これは依存関係の自動更新だけの問題ではありませんでした。大量の変更がデプロイパイプラインを詰まらせる兆候であり、開発者はビルドとテストの時間を短縮し、プラットフォームチームはデプロイツールが変更を検出して適用するまでの時間を短縮するきっかけになりました。

依存関係更新のプルリクエストについては、デプロイパイプラインにロジックを実装し、本番環境への昇格を自動承認するとともに、承認済みの変更を自動でマージして適用するようにしました(ほかのすべての変更についても、開発者の作業を一つ減らせるという副次的なメリットがありました)。一部の変更について人の介入なしで進めるプロセスに移行するには、テストを大いに信頼する必要がありました。既存のテストを改善するか、人による確認と同等に十分だと明確に認める必要がありました。

継続的な改善は効果を発揮する

依存関係の更新がスプリントの主要タスクになるのを防ぎたいという思いから始まった取り組みは、その実現だけでなく、デプロイの高速化、プルリクエストから本番環境までの完全自動化、そしてテストの有効性についての明確な合意をもたらしました。

依存関係更新の苦労はほとんど忘れ去られ、より大きな課題であるデプロイパイプラインの改善へと移りました。パイプラインの出来には満足していましたが、現状に満足することはありませんでした。継続的な改善とは、そういうものです。

続きを読む

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

illustration hero ai
Blog

AIハリケーンが到来

AIはソフトウェア開発とサイバー攻撃の双方を加速させています。リーダーは、エージェントとコードを開発の初期段階から保護し、実行時に制御を徹底するとともに、防御策を独立して検証しなければなりません。

feature insights context
Blog

予防は、本質的に解決済みの問題なのでしょうか?

エージェントが生成するコードの予防策はアーキテクチャ上解決されていますが、開発を遅らせることなくセキュリティを守る制御を選ぶことが、依然として課題です。