ふくふくHukuhuku Inc.
EP.07Legacy Revival 13分公開: 2026-09-01

誰がこのシステムを呼んでいるか ── 外部連携の棚卸し

移行の計画が崩れる最大の原因は、想定していなかった連携先です。誰が、いつ、どこから呼んでいるのか。記録から機械的に洗い出す方法を扱います。

#レガシー#外部連携#移行#運用
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

移行計画が崩れる原因を並べると、上位に必ず入るのが想定していなかった連携先です。切り替えた直後に「あのバッチが動かない」「取引先からデータが届かない」と発覚します。

この回は、誰がこのシステムを呼んでいるかを機械的に洗い出す方法を扱います。設計書は信用しないところから始めます。

1. なぜ一覧が存在しないのか

長く動いているシステムに、正確な連携先一覧はまず存在しません。理由は構造的です。

  • 連携は少しずつ増える — 都度追加されるので、一覧の更新が追いつかない
  • 担当者が代わっている — 追加した人がもういない
  • 一時的なつもりだった — 恒久運用になったが、記録は残っていない
  • 相手側で作られた — こちらは知らないうちに参照されている
  • 設計書はあるが古い — 作った当時のもので、更新されていない

4番目が特に厄介です。データベースに直接接続されている、共有ディスク上のファイルを読まれている、といった形は、こちらの記録に残らないことがあります。しかもこの種の連携は、正式な手続きを経ていないことが多い。「昔、担当者同士の話で繋いだ」という経緯が、両社とも記録に残っていないわけです。

「たぶんこれで全部」は必ず外れる

関係者に聞いて作った一覧は、知っている人が知っていることだけの一覧です。実際の記録と突き合わせると、毎回いくつか出てきます。聞き取りと記録の両方をやってください。

2. 記録から復元する

確実なのは、実際のアクセス記録から復元することです。複数の経路を並行して調べます。

調べる場所と、その限界
調べる場所見つかるもの見落とすもの
アクセスログAPI・画面への呼び出し元直接DB接続
DBの接続記録どのアカウントがどこから接続したか共有アカウントだと区別できない
ファイアウォール設定許可されている通信の相手実際に使われているかは不明
認証情報の発行履歴誰に鍵を渡したか共有されている場合は追えない
ファイルの読み書き記録共有ディスク経由の連携記録が有効でないと取れない
定期実行の設定こちらから呼んでいる先相手側の定期実行は見えない
DB の接続元を集計する(PostgreSQL の例)
SQL
-- 現在の接続を見る(今この瞬間だけ。継続的に記録する必要がある)SELECT    client_addr        AS 接続元,    usename            AS ユーザー,    application_name   AS アプリ名,    COUNT(*)           AS 接続数,    MIN(backend_start) AS 最古の接続FROM pg_stat_activityWHERE client_addr IS NOT NULLGROUP BY 1, 2, 3ORDER BY 接続数 DESC;
-- 定期的にこれを記録しておくと、後から棚卸しに使えるCREATE TABLE IF NOT EXISTS connection_audit ASSELECT CURRENT_TIMESTAMP AS observed_at, client_addr, usename, application_nameFROM pg_stat_activity WHERE client_addr IS NOT NULL;

重要なのは、この記録を継続的に取っておくことです。移行を決めてから調べ始めると、その時点で動いていないもの(月次・年次の処理)を見落とします。最低でも1年ぶんの記録が欲しい。逆に言えば、移行の予定が無くても記録は取り始めておく価値があります。必要になった時点から始めると、1年待つことになります。

3. アクセスログから呼び出し元を出す

アクセス記録がある場合は、どこから、どの機能が、どれくらい呼ばれているかを集計します。移行の優先順位づけにも使えます。

アクセスログから連携先の一覧を作る
Python
import refrom collections import defaultdict
# 呼び出し元 → {エンドポイント: 回数} を集計するcallers = defaultdict(lambda: defaultdict(int))last_seen = {}
LINE = re.compile(    r'(?P<ip>\S+) \S+ \S+ \[(?P<ts>[^\]]+)\] "\S+ (?P<path>\S+)')
with open("access.log", encoding="utf-8", errors="replace") as f:    for line in f:        m = LINE.match(line)        if not m:            continue        ip, path = m.group("ip"), m.group("path")        # 末尾のID等を潰して、エンドポイント単位にまとめる        endpoint = re.sub(r"/\d+", "/{id}", path.split("?")[0])        callers[ip][endpoint] += 1        last_seen[ip] = m.group("ts")
print(f"{'呼び出し元':<18}{'種類':>6}{'総回数':>10}  最終アクセス")for ip, eps in sorted(callers.items(), key=lambda x: -sum(x[1].values())):    total = sum(eps.values())    print(f"{ip:<18}{len(eps):>6}{total:>10}  {last_seen[ip]}")

最終アクセスの日時が特に重要です。半年以上前で止まっているものは、使われていない可能性があります。ただし後述のとおり、年次処理の可能性があるので即断はできません。

4. 使われていないものを見分ける

記録が無い連携を「使われていない」と判断するのは危険です。頻度の低い処理ほど、記録に現れにくい

最終アクセスからの判断
最終アクセス判断確認方法
直近1週間現役移行対象として計画に入れる
1か月前現役(月次処理か)月次かどうかを確認
1年以内年次処理の可能性その時期に何があるか確認
1年以上前停止候補それでも通知期間を設ける
記録が存在しない判断保留そもそも記録が取れているかを確認

最下行が要注意です。記録が無いのは、使われていないからではなく、記録を取っていないからかもしれません。両者は結果が正反対なので、必ず切り分けてください。

止める前に「鳴らす」

止める判断をしたら、いきなり止めずに一時的に応答を止めて反応を見る方法があります。数分だけ止めて、問い合わせが来るかを確認する。来なければ本当に使われていない可能性が高い。業務影響が小さい時間帯に、短時間だけが条件です。

5. 相手を変えられないとき

連携先が社外だったり、相手側にも改修予算が必要だったりして、変更を依頼できないことがあります。この場合は、こちらが合わせるしかありません。

具体的には、旧システムと同じ形式で応答する層を新側に用意します。相手には何も変わっていないように見せ、内部だけを差し替える。EP.05 のストラングラーパターンを、外部連携に適用した形です。

  1. 1旧の形式を正確に把握する — 実際の通信内容を記録して確認
  2. 2同じ形式で応答する層を作る — 新システムの前に置く
  3. 3 で比較する — 新旧の応答が同じかを確認
  4. 4切り替える — 相手からは何も変わって見えない
  5. 5将来の廃止時期を合意しておく — 恒久化させない

5番目を必ず入れてください。互換層は一時的なもののはずが、恒久化しやすい。廃止時期を決めずに作ると、それ自体が次のレガシーになります。EP.11 で扱う「再レガシー化」の典型的な入口です。 を新側に移したあとも互換層が残っていると、新システムの中に旧システムの形が住み着くことになります。

6. 連携の形を固定する

棚卸しができたら、その形を壊さないための仕組みを入れます。 は、呼び出す側と呼ばれる側の「約束」を検証するテストです。

相手のシステム全体を動かさなくても、やり取りの形式が守られているかを確かめられます。移行中に応答の形が変わってしまう事故を、事前に検知できます。

応答の形式が変わっていないことを検証する
Python
REQUIRED_FIELDS = {"order_id", "status", "amount", "updated_at"}ALLOWED_STATUS = {"pending", "paid", "cancelled"}

def test_response_contract(client):    """外部に約束している形式が保たれているかを検証する。"""    res = client.get("/api/orders/12345")    assert res.status_code == 200
    body = res.json()
    # 1. 約束した項目が全部あるか(減らすと相手が壊れる)    missing = REQUIRED_FIELDS - set(body)    assert not missing, f"項目が欠けています: {missing}"
    # 2. 型が変わっていないか    assert isinstance(body["amount"], int), "金額が整数でなくなっています"
    # 3. 取りうる値が増えていないか(増やすと相手が知らない値を受け取る)    assert body["status"] in ALLOWED_STATUS, f"未知の状態: {body['status']}"

3番目の観点が見落とされがちです。項目を減らすと壊れるのは分かりやすいのですが、取りうる値を増やしても相手は壊れます。新システムで区分を細かくしたら、相手が知らない値が飛んでいった ── という事故は実際に起きます。相手の実装が「知らない値なら例外を投げる」形になっていると、こちらの改善が相手の障害になります。連携の変更は、増やす方向でも合意が要ると考えてください。

ここまでのまとめ

正確な連携先一覧は存在しないという前提で、記録から復元する。聞き取りと記録の両方をやる。判断には最低1年の記録が要る(年次処理を見落とさないため)。相手を変えられないなら互換層を作り、廃止時期も同時に合意する。棚卸し後は 契約テスト で形を固定し、項目を減らすことだけでなく、値を増やすことにも注意してください。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp