はじめに — つまずきに寄り添って
現場で「とりあえず動く」スクリプトを書いた経験は多いはずです。しかし時間が経つと、同じ処理が別の場所でコピペされ、テストがなく、変更がこわくなります。本記事では第135回(CSVクリーニング)から自然につながる実践的な手順で、そうした“一発スクリプト”を再利用可能でテストしやすいコンポーネントに変える方法を示します。
設計原則(短く実務視点で)
単一責務(Single Responsibility)
関数は一つの目的だけを持ちます。読み込み・変換・書き出しは別々にし、組み合わせは上位の関数で行います。
純粋関数と副作用の分離
データ変換は入力を受け取り出力を返す純粋関数にし、ファイルやログなどの副作用は別モジュールにまとめます。こうするとユニットテストが容易になります。
依存注入
外部リソース(ファイルパス、DB接続、設定)は引数で渡すか、IOアダプターを介して渡します。テスト時はモックやスタブに差し替えます。
パターン実例:CSVクリーナーのリファクタ(before / after)
まず典型的な一発スクリプト(before)です。
実装メモ: コード例は環境に合わせて調整してください。例: # before: csv_cleaner.py
問題点:読み込み・変換・書き出しが混在。テストが難しい。
リファクタ後は3つの責務に分けます:pure functions(transform)、io_adapter(読み書きラップ)、cli(エントリポイント)。
実装メモ: コード例は環境に合わせて調整してください。例: # package layout (例)
この構成の利点:transformは純粋関数なのでユニットテストが容易。io_adapterをモックすれば統合テストもしやすい。
ディレクトリとパッケージ構成(推奨)
小規模プロジェクトの最低限の構成例:
実装メモ: コード例は環境に合わせて調整してください。例: mycsv/
__init__.pyで外部に公開する関数を明記し、内部実装は隠すと保守性が高まります。
テスト設計:pytestでの例
pure functionは通常のユニットテスト、IOはtmp_pathやモックで扱います。例:
実装メモ: コード例は環境に合わせて調整してください。例: # tests/test_transform.py
実行コマンド例:
- pip install -e .[dev]
- pytest -q
CIと品質ゲート(最低ライン)
テスト・型チェック・lintを最低限組み込みます。簡単なGitHub Actionsジョブ例:
実装メモ: コード例は環境に合わせて調整してください。例: # .github/workflows/ci.yml
pyproject.toml の最低例:
実装メモ: コード例は環境に合わせて調整してください。例: [project]
実務的チェックリスト
| チェック項目 | 説明 | 判定基準 |
|---|---|---|
| 関数の責務は明確か | 一つの関数が複数のことをしていないかを確認 | 変換はpure、I/Oは別モジュール |
| グローバル状態はないか | モジュールレベルの可変変数が無いか | 無ければOK |
| I/Oは分離されているか | ファイルやDBアクセスが専用アダプターにあるか | モック可能であればOK |
| テストカバレッジの最低ライン | 重要な変換ロジックに対するユニットテストの有無 | 変換ロジックは100%を目指す(現実的最低は80%) |
| 後方互換性の扱い | API変更時の互換性維持方針があるか | 破壊的変更はバージョニングで管理 |
段階的リファクタ計画と落とし穴
段階的に置き換える手順:
- 1) transformをpure関数として切り出し、既存スクリプトから呼び出せるようにする
- 2) io_adapterを作成して既存I/Oを置換する(動作確認は並列運用で)
- 3) testsを追加、CIで確認してからマージ
注意点:
- 過度な抽象化は避け、複雑化してしまう場合はスコープを縮小する
- 既存運用中のスクリプトはブランチ戦略で段階的に切り替える(トグル可能にする)
- 大規模CSVはメモリに全ロードせずチャンク処理を使う。transformは行単位に保つと組み合わせやすい
まとめと次の実践課題
本稿のポイントは次の通りです。
- 関数は単一責務にし、変換ロジックは純粋関数にする
- 副作用(I/O)は別モジュールにまとめ、依存注入やモックでテストしやすくする
- パッケージ構成と最低限のテスト・CIを整えることで運用コストを下げる
手を動かす練習(3ステップ)
- リポジトリを作り、上記構成で最小限のファイルを作る(cli.py, transform.py, io_adapter.py)。
- transformのユニットテストを書いてpytestで実行する(pytest -q)。
- 簡単なGitHub Actionsワークフローを追加してPushでテストが回ることを確認する。
次回はこの基盤を使って、AIを組み合わせた自動データ正規化パイプラインに進みます。小さく始めて、確実に保守できる形にすることを優先してください。