毎週のように依存パッケージを上げ続ける努力

皆さんこんにちは。fluctにてfluct SSPという広告配信システムの管理画面を中心にクライアントサイドの開発を行っております、大関です。

依存パッケージの更新、どうしてますか?

今や数多くの言語でパッケージマネージャが提供されており、みなさんも日常的にコミュニティによるパッケージエコシステムを活用していることと思います。

ですが、この依存パッケージの更新については、どのようにしていますか? セキュリティfixなどを除き、以下のようなことになっていることが多いのではないでしょうか?

  • チームの「いい人」が頑張って更新し続ける
    • その人の謎の情熱が消えると更新されなくなってしまう
  • たまに気がついたら頑張る
    • 「いい人」が頑張るタイプの亜種
    • 気が付かなかったら更新されない
  • 更新はリスクなので塩漬けにする
    • プロダクトは定期的に作り直す前提
    • CIでテストを回し続けているのに更新しないなんて……とモヤモヤも溜まったりする

日々の運用としては、これで問題ないように見えますが、継続的にサービスを運営する観点から言えば、古いバージョンのパッケージに依存し続けることは以下の様なリスクがあります。

  • コミュニティドリブンのライブラリ・フレームワークは往々にして最新版しかサポートされない
    • バグは最新版で修正されるがバックポートはされるかは別問題
    • 長期メンテナンス版があっても、長期メンテナンス版の最新版を使わないかぎりサポートされない
    • 古いバージョンのドキュメントはアーカイブされずに消えることがある
    • 最新版だと使える機能が使えないことによるフラストレーションの増加
    • 最新版だと直っているバグなのに回避策を考える努力
  • いざ更新しなければならなくなった時の変更の規模は、最新版から離れれば離れるほどに大きくなる

これらを考慮すると、経験則・慣習に依るものではありますが、更新されないパッケージはリスクとなる、と私は考えています。

ですが、チームの「いい人」に任せているだけでは継続しない。自分が「いい人」になっても、自分が燃え尽きたら、それ以降に継続する保証はありません。では、どうすればよいのでしょうか?

対策: 持ち回りで継続的に更新する

依存パッケージの更新にあたって問題となるのは以下の点です.

  • 依存パッケージの更新がたまにしか行われない場合、更新自体のリスクが大きくなる
    • そもそも作業コストが大きくなる
    • 実際に更新する段になって課題を初めて知るので、作業コストが往々にして想定から膨らみやすい
  • 更新が個人の熱量に依存してしまう

つまり、

  • 小刻みに更新し、変化に対応することで、時間の堆積に伴うリスクを減らす
    • 仮に更新できなくても、何が必要かを継続的に収集する
  • チームの定例作業に組み込んで属人性を減らす

ことができれば、更新のコストは軽減できます(CIや自動テストの導入は今や必須なので敢えて言いません)。

そこで私達のチームでは、各チームメンバー交代の当番制で、依存パッケージの更新を行うことをpackage gardeningと呼び、毎週実施することにしました(ネーミングは炭坑の庭師 - steps to phantasienで紹介されていた、当時のChromium Projectの依存先のWebKitのバージョンアップ作業の名前がカッコいいので拝借しました)。

手順

具体的な手順は以下の通りです。これを毎週の始めに作業します。 (npm経由の場合を挙げます)。

1. 初期化

  1. リポジトリから最新のmasterをpullする
  2. セットアップ処理を走らせて、依存のインストール込みで、最新のmasterでの環境を一度再現する
    • rm -rd ./node_modules/ && npm install

2. 古いパッケージの洗い出し

  1. npm outdatedで更新を確認する
  2. 1の結果のうち、「package.jsonに記述されているもの」の更新を確認する
    • release noteやchangelogを見に行く
    • なければコミットログをざっと確認する

3. 更新

  1. ロックファイルを削除する
    • 一度 rm npm-shrinkwrap.jsonする
  2. package.json側の指定を、パッケージごとに更新する
    • 「自分たちは、このバージョンまでは更新内容を確認し、使っている」意志の表明として更新しています
  3. 一通り更新が終わったら、依存パッケージのディレクトリを削除し、再生成する
    • rm -rd ./node_modules/ && npm install
    • 間接依存しているパッケージも込みで依存関係を作り直すため
  4. ロックファイルを再生成し、リポジトリにコミットする
    • 開発用パッケージ込みでロックしたいので npm shrinkwrap --dev します

4. 更新の後始末を行う

  • パッケージの更新の結果、ビルドが通らない場合
    • 直す
    • または更新を諦めて, 専用のissueで対応する
  • パッケージの更新の結果、テストが落ちる場合
    • 直す
    • または更新を諦めて, 専用のissueで対応する
  • パッケージの破壊的変更が多すぎる
    • 頑張って追従する
    • または更新を諦めて, 専用のissueで対応する
  • コードの改善・リファクタリングに使えそうな新機能はissueを作る
    • 基本的には、必須ではなく「気がついたらfileしよう」にしている
    • lintなどコードの健全性に関係するものは、ほぼ必須で回っている

5. pull requestの作成

  • 更新のあった依存パッケージの変更をまとめてpull requestする

6. code reviewで問題がなければmasterにmergeする

マージしましょう

7. チームメンバーへの周知

周知して、次の週に。 お疲れ様でした。

以上を1サイクルとして、毎週回します。

f:id:saneyuki_s:20160624135226p:plain

補足

どのくらい大変な作業なのか?

コードベースや作業に慣れるまでは、0.5~1営業日まるまるかかることも有りましたが、慣れてくると平均して1~2時間程度で1サイクルが終わるようになっています。

パッケージの更新量ってどんなもの?

量としては平均5~10パッケージ程度です。 毎週継続的に更新を続けるので、minorやpatch verの更新が殆どになっています。 これにより、一度に多くの更新を行うのを避けることが出来るようになりました。

パッケージの追加・削除は当番以外できないのか?

さすがに追加・削除にまで当番制を敷くと、開発の上で必要なパッケージの追加・更新ができない

greenkeeper.ioを使わないのはなぜか?(2016年6月27日 16:50 UTC+9に追記)

はてなブックマークのコメントで「greenkeeper.ioを用いていないのは何故か?」という質問が何件か有りましたので、それについて回答します。

もちろん、人力に依存する(努力している)箇所を減らしていきたいのは事実です。 ですが、

  • 「まとめ」にて挙げるように、副次効果としてチームメンバーのコードベースへの理解のトレーニング効果が明らかになったため、現在のところ、それも重視している
  • 更新した結果のパッチ・pull requestの導入の有無をreviewするreviewerをローテーションの効果は薄いと判断した
    • そもそもreviewer役をやる時点で、依存パッケージについて理解が一定度あるという前提なので、ローテーションしてもトレーニングとしては薄い
  • pull requestの作成も確かに手間ではあるが、行っている内容は、バージョンアップの結果として、どのような影響が起こるのかの予測・確認、および既存のコードベースに対して加えることの可能なリファクタリングなどの機会の発見も含んでいるので、更新通知そのものに対しては強い切望があったわけではない
  • npm系パッケージに限らずやっていきたいので、特定のパッケージマネージャの更新のみbot化するのは今のところ避けておきたい
    • 言語が増えるごとに依存するサービスを増やすことになると逆に不便なので避けておきたい
      • monolithic repository構成で、web applicationのserverとclientが別のサブディレクトリに入っている状況でも、依存パッケージを記述したマニフェストファイルを解釈してくれるかがツールごとに別だったりすると更に不便なので慎重にいきたい

という理由にて「導入していない」のが現状での判断になっています(将来的に、チームの練度や人数、スタンスの変化によって導入する判断を下すことはもちろんありえます)。

まとめ

実際に、毎週の更新を行うことで、依存パッケージの更新という作業のリスクが大きく軽減することができました。

開始当初の目的では意図していなかった副次効果として、「サーバー・クライアントなどの専門性・分業はあれども、自分たちの開発しているプロダクトが何に依存して成り立っているのかを認識できる」という副次効果も有りました。

また、依存パッケージの数に対してチームとして責任を持つようになるので、「無闇矢鱈に便利そうなパッケージを追加してしまう」ことに対して慎重になるという効果があったと考えています。

お知らせ

弊社には AJITO という社内バーがあり、毎夜のようにエンジニアが現れ、酒を嗜み、軽食をつまみつつ、エンジニアリング談義を行っています( #ajitingで既にご存知の方も多いと思います)。「私も一緒に(こういう)エンジニアリング談義をしたい!」という方がいらっしゃいましたら、是非とも弊社エンジニア or @tech_voyageに、お声掛けいただければと思います。

また、fluctを含むVOYAGE GROUPアドテクユニットでは、一緒に働いてくださる仲間を募集しております。VOYAGE GROUPや広告配信に興味を持たれましたら、お気軽にご連絡ください。

f:id:saneyuki_s:20160624132725j:plain