45395 - シコウサクゴ -

ジョブ数111→50への削減設計:「減らす勇気」と統合の判断基準

2026-04-12
AI駆動開発
AI駆動開発
launchd
ジョブ管理
運用設計
リファクタリング
Last updated:2026-04-12
10 Minutes
1811 Words

個人開発でデータパイプラインを運用していたら、いつの間にかジョブが111個に膨れ上がっていました。新機能を追加するたびに新しいジョブを作り、不要になっても消さず、似たようなジョブが乱立します。

「増やすのは簡単、減らすのは怖い」——このバイアスに正面から向き合い、111ジョブを約50に削減する設計を行った記録です。

問題:ジョブ数の指数関数的な増加

なぜジョブは増え続けるのか

1
Month 1: 10ジョブ — 管理は楽勝
2
Month 3: 30ジョブ — まだ全体を把握できる
3
Month 6: 60ジョブ — 「あのジョブ何だっけ?」が増える
4
Month 9: 90ジョブ — 依存関係が把握不能
5
Month 12: 111ジョブ — 管理基盤の見直しが必要に

増加の原因パターンを整理します。

パターン発生頻度
機能追加のたびに新ジョブ「通知を追加しよう」→ 通知専用ジョブ新設
試行錯誤の残骸「実験用に作ったジョブ」が本番に残る
粒度の不統一1つのジョブでやるべき処理を3つに分割
恐怖による温存「消して壊れたら怖い」で放置

111ジョブの運用コスト

  • 監視: 111個のログを毎朝チェックするのは不可能です
  • 障害対応: 「どのジョブが失敗した?」の切り分けに時間がかかります
  • リソース: macOS launchdで111プロセスの同時起動は現実的ではありません
  • 認知負荷: 全体像を把握できる人間がいません(自分すら含めて)

削減の判断基準:4つの問い

ジョブを1つずつ以下の4つの問いにかけます。

問い1: このジョブの出力を、誰が(何が)消費しているか?

1
ジョブA → output_a.parquet → ジョブBが読む → OK(消費者がいる)
2
ジョブC → output_c.csv → 誰も読んでいない → 削除候補

出力の消費者がいないジョブは、どんなに精巧でも無価値です。これが最も強力なフィルターになります。

問い2: 別のジョブと入出力が重複していないか?

1
ジョブD: APIからデータ取得 → 加工 → 保存
2
ジョブE: 同じAPIからデータ取得 → 別の加工 → 保存

入力源が同じジョブは統合候補です。取得部分を共通化して、加工だけを分岐させます。

問い3: 実行頻度は適切か?

1
ジョブF: 毎時実行 — でも入力データは1日1回しか更新されない
2
→ 日次に変更(23回/日の無駄な実行を削減)

過剰な頻度のジョブは、統合ではなく頻度調整で負荷を削減できます。

問い4: 独立したジョブである必要があるか?

1
ジョブG: データ取得(5秒で完了)
2
ジョブH: データ加工(ジョブGの出力を使う、3秒で完了)
3
ジョブI: レポート生成(ジョブHの出力を使う、2秒で完了)

3つで合計10秒です。依存関係が線形で、間に他のジョブが割り込む必要がないなら、1つのジョブにまとめて問題ありません。

統合の4パターン

パターン1: 直列統合

1
Before: ジョブA → ジョブB → ジョブC(線形依存)
2
After: ジョブABC(1つのジョブ内で順次実行)

適用条件: 依存が線形、間に他ジョブが割り込まない、合計実行時間が短い

パターン2: 入力源の共通化

1
Before: ジョブD(API取得→加工X), ジョブE(API取得→加工Y)
2
After: ジョブDE(API取得→加工X+加工Y)

適用条件: 同じ入力源を使うジョブ同士

パターン3: スケジュール統合

1
Before: ジョブF(毎時), ジョブG(毎時), ジョブH(毎時) — 全部同じ時刻に実行
2
After: 毎時バッチ(F→G→Hを順次実行)

適用条件: 同じスケジュールで動くジョブ群

パターン4: 削除(統合ではなく除去)

1
Before: ジョブI — 出力を誰も消費していない
2
After: (削除)

適用条件: 問い1で消費者がいないジョブ

実際の削減プロセス

Step 1: 全ジョブの棚卸し

まず111ジョブの一覧を作成し、以下を記録します。

1
| ジョブ名 | 実行頻度 | 入力 | 出力 | 消費者 | 最終実行 | 判定 |
2
|---------|---------|------|------|--------|---------|------|
3
| fetch_A | 毎時 | API_A | a.parquet | agg_A | 今日 | 維持 |
4
| fetch_B | 毎時 | API_A | b.parquet | なし | 30日前 | 削除 |
5
| agg_A | 日次 | a.parquet | summary.csv | report | 今日 | 統合候補 |

Step 2: 依存グラフの可視化

1
fetch_A ──→ process_A ──→ agg_A ──→ report_A
2
fetch_B ──→ (誰も使っていない)
3
fetch_C ──→ process_C ──→ agg_C ──→ report_A

グラフにすると「孤島」(どこにもつながっていないジョブ)と「直列チェーン」(統合候補)が一目でわかります。

Step 3: 段階的な実施

一気に削減すると事故のリスクが高いです。以下の順序で進めます。

  1. 孤島の削除(リスク: 最低)— 消費者がいないジョブを無効化
  2. 30日間の無効化期間 — 削除ではなく無効化。問題が出たら即時復旧
  3. 直列チェーンの統合(リスク: 低)— 出力形式は変えず、実行を1ジョブにまとめる
  4. 入力源の共通化(リスク: 中)— API呼び出しの削減、エラーハンドリングの統合
  5. 頻度の調整(リスク: 低)— 過剰な実行頻度の是正

Step 4: 削減前後の比較

1
Before: 111ジョブ
2
- 孤島(消費者なし): 18ジョブ → 削除
3
- 直列チェーン統合: 30ジョブ → 12ジョブに
4
- 入力源共通化: 16ジョブ → 8ジョブに
5
- 頻度過剰: 7ジョブ → 頻度調整のみ(数は変わらず)
6
7
After: 約55ジョブ(50%削減)

「減らす勇気」をどう持つか

心理的バリア

「このジョブを消して、何か壊れたらどうしよう」——これが最大の障壁です。

対策: 削除ではなく「無効化 → 待機 → 削除」

1
Day 0: ジョブを無効化(設定でOFF、コードは残す)
2
Day 30: 30日間問題なし → コードを削除

この「30日ルール」があるだけで、心理的ハードルは大幅に下がります。万一問題が出ても、無効化を解除するだけで即時復旧できます。

もう一つの対策: 消費者の明示

各ジョブの設定に「このジョブの出力を使っているジョブ」を明記します。

job_config.yaml
1
daily_fetch:
2
consumers:
3
- daily_aggregation
4
- weekly_report

消費者が明示されていれば、「消して大丈夫か?」の判断が格段に楽になります。

まとめ

判断基準問いアクション
消費者の有無出力を誰が使っているか?なし → 削除
入力の重複同じデータを取得していないか?重複 → 統合
頻度の妥当性入力更新より高頻度でないか?過剰 → 調整
独立性の必要性1ジョブにまとめられないか?線形依存 → 統合

ジョブの数は、システムの「健康」を示す指標ではありません。必要最小限のジョブで、必要な出力がすべて生成されている状態が健全です。

「このジョブ、本当に必要?」と問いかけ続ける仕組みを作ること。それが、ジョブ数の再膨張を防ぐ唯一の方法です。

Article title:ジョブ数111→50への削減設計:「減らす勇気」と統合の判断基準
Article author:45395
Release time:2026-04-12

記事へのご質問・ご感想をお聞かせください

フィードバックを送る