Rampでは、機械学習ワークフローのモノレポが、便利な共有スペースからチームが「ちょっとした沼」と呼ぶ状態にまで成長していた。固定されていない推移的依存関係を持つrequirements.txtがマシン間でずれ、フォーマットは不統一で、自動テストはなく、型注釈は欠けているか微妙に間違っていた。エンジニアは何かを壊すことを恐れてコードに触れることに警戒心を抱くようになり、当初の目標であった速度は這うように遅くなっていた。

リポジトリを書き直すのではなく、チームは4つのツールをその場で導入した。ロックされ再現可能な依存関係のためのpoetry、フォーマットとリンティングのためのruff、完全なカバレッジを追うのではなく影響力の大きい共有ユーティリティをテストするためのpytest、段階的な型チェックのためのmypyである。稼働中のコードベースで導入を可能にした秘訣は、まずすべてを修正することではなかった。自動フォーマッタをリポジトリ全体に対して一度だけ単一のコミットで実行し、そのコミットのハッシュを.git-blame-ignore-revsに追加してblame履歴を汚さないようにした。

リンティングと型付けについては、CIを有効にする前に数百の既存エラーを修正するのではなく、既存のすべての違反に対して「# noqa」と「# type: ignore」のコメントを機械生成し、新規のものに対してのみ厳格な適用を有効にした。ruffの「RUF100」ルールとmypyの「warn_unused_ignores」は、基になるコードが実際に修正されて不要になった抑制コメントを検出したため、無視リストは縮小するだけで、静かに腐敗することはなかった。

その結果、コードベースは命令によって「クリーン」になったわけではないが、初日から将来に向けて適用されるようになり、開発者が自然に古いファイルに触れるにつれて負債は縮小していった。

古い違反を祖父条項で免除し、新規コードのみを適用する

完全な書き直しや一斉クリーンアップは、適用を開始するまでに数週間を消費するため、チームは遡及的にではなく将来に向けて適用した。

初期の抑制コメントを機械生成することで、無制限の手動クリーンアップを一度きりの自動化可能なステップに変えた。

未使用の抑制コメントを検出することで、古い違反を修正すると今では無意味な無視コメントが表面化しCIがそれを検出するため、負債は減少するのみとなった。

リフォーマットを単一のコミットにまとめ、git blameから除外することで、実際の理由で行に最後に触れた人物を確認する能力を維持した。

基準を適用する前にクリーンなコードベースは必要ない。既存のすべての違反を一度黙らせ、新規コードのみに厳格に適用すれば、人々が古いファイルに触れるにつれてバックログは縮小する。

チームは、コードベースが将来に向けて品質が維持される状態に達したと報告した。poetryのロックファイルは開発者のマシンとCI間で一貫した環境を提供し、ruffはマージ前に重複した辞書キーなどの実際のバグを検出し、mypyはユニットテストがカバーしていない呼び出し箇所全体でのシグネチャ変更のリグレッションを検出した。投稿は、目標をツール自体ではなく、大規模な変更を行う自信の回復として位置づけ、チームがこの負担のより多くを貢献者からツールへ移すことを依然として検討していると述べている。

出典をたどる

出典

  1. Increasing velocity by modernizing a Python codebase Ramp Engineering · builders.ramp.com ·