見出し画像

テスト保護されていない不慣れなシステムを安心して開発できるようにする

こんにちは、ネコ派メタラーです。ナビタイムジャパンでスポットデータ運用基盤の開発を担当しています。

以前は地点検索基盤の開発を担当していましたが、最近異動して新しいシステムと向き合うことになりました。本稿では、不慣れなシステムの開発を始めるにあたり、開発体験の改善に取り組んだ話をします。

はじめましてのバッチ処理

スポットデータ運用は多くのバッチ処理に支えられています。チームに所属して最初の仕事は、とあるバッチ処理の性能を改善することでした。

担当することになったバッチ処理は 300 行程度の Python コード 1 ファイルで構成されていました。仕様を丁寧に記述したドキュメントが存在し、すぐに挙動を理解することができました。一方コードについては、循環的複雑度は最大 18 とやや高く、1 ファイルに全て詰め込まれていることもあり、少し複雑なコードだと感じました。

このバッチ処理に自動テストはなく、動作確認のたびに稼働環境で動作させることになります。一度の実行で 1 〜2 時間かかりますので、頻繁に実行することはできません。加えて実行時にはデータベースにレコードを追加したり、社外の API サービスを呼び出したりと、外部に影響を与えます。基本的には定期実行に任せたいものであり、気軽に実行できるものではありませんでした。

このバッチの周辺システムまで熟知しているメンバーであれば、開発・リリース・テストまで無理なく進められるかもしれません。しかしチームに入って間もない私としては、コード改修・テスト実行ともに少なからずハードルを感じました。まずは開発体験を改善し、安心してコード改修・テスト・リリースできる状況を整えることにしました。

コード構成の改善

まず、コードを改修しやすくする方法を考えました。具体的には、不用意な不具合を防ぐため単体テストや静的解析によって保護される状況を目指しました。

プロジェクトのモダン化

テストフレームワークや静的解析ツールは開発用パッケージとして導入することが通例です。開発用パッケージを気軽に導入するためプロジェクト管理ツールを導入しました。今回のバッチは Python 製ですので、Poetry プロジェクトに変換しました。

早速、テストフレームワーク (pytest)、静的解析ツール (Flake8)、コードフォーマッタ (Black) を導入しました。後述しますが、特にテストできる環境を整備するまでは静的解析ツールが命綱になります。

壊さない程度にリファクタリング

次に、コードを改修しやすくするためにリファクタリングしました。ここでは挙動を変えないことを保証し続ける必要がありますが、自動テストが十分に整備されていない状況ではそれが難しいことに注意が必要です。

最初のリファクタリングは以下の変更のみを行うことにしました。

  • ファイル分割

  • コードの移動

  • 関数への切り出し

  • 命名の変更

これらの変更は、操作が単純でミスしづらいことに加え、静的解析で問題を検知しやすいという特徴があります。起きる不具合の大半は参照切れであり、未定義エラーとして現れるため、静的解析ツールで十分に検知可能です。このリファクタリングは静的解析ツールが新しいエラーを発生させないことを確認しながら進めました。

簡単なリファクタリングではありますが、コードが整理されて可読性が改善したことはもちろん、関数の切り出しによって機能が小さくなったことで単体テストを実装できるようになりました。

気軽に動かせるようにする

ここまでの工夫でコードを読み書きしやすくなりましたが、まだ自動テストが十分に整備されていないため、問題が起きていないかどうかシステムレベルでテストする必要があります。開発を続けるためにはシステムテスト実行のハードルを下げるべきだと考えました。

デバッグ機能を追加

実行時間を短くすること、および外部影響を小さくすることを目指し、部分的に実行するオプションを実装しました。実行時に環境変数を仕込むことで、全件数のうち一部だけを実行するようにしました。

# オプションを受け取る
limit = -1
if limit_str := os.getenv("LIMIT"):
    limit = int(limit_str)

input_stream = sys.stdin

# オプションが指定されたら入力を一部だけ切り取る
if limit >= 0:
    input_stream = itertools.islice(input_stream, limit)

この簡単な実装により、一度あたりの実行を 1 分程度に抑えたほか、外部 API のコール数も無視できる程度に削減できました。感覚的には、コミットごとにデプロイし、稼働環境でテスト実行できる状態になったと思います。

出力を可視化

処理の結果はデータベースに登録されるため、登録されたデータを閲覧できる簡単な Web ページを作成しました。当社のスポットデータを管理する Web システムがすでにありますので、間借りする形で実装しました。

実行結果をテーブルで可視化

テーブルビューを実装しただけですが、表現を柔軟に変えられるため、単に SQL を実行する以上に状況を把握することができました。データベースへの影響を簡単に確認できるほか、一覧性の高さから傾向把握が容易になり、改善アイデアも生まれやすくなりました。

改善した結果

初めて触ることになったバッチシステムでしたが、今回ご紹介した方法で開発体験を改善したことで、安心して開発することができるようになりました。実績として、開発初日に簡単な不具合修正をリリース、1 週間で大幅な業務効率改善をリリースするに至りました。

コードの循環的複雑度は 18 から 12 に少し改善しました。改修対象となった箇所から単体テストも記述しており、カバレッジも徐々に上がってきて、開発を継続する土台が整いつつあります。

本稿がシステム開発体験の改善に役立てれば幸いです。