sap-unused-code について
sap-unused-code は、SCMON/SUSG の実行データと静的参照を組み合わせ、対象を分類する SAP カスタムコード退役審査用スキルです。パッケージ、名前空間、オブジェクト一覧、または目的による範囲指定が必要で、コードの削除は行いません。
主な機能
- 01実行データと静的参照の併用
- 02オブジェクトを4分類で判定
- 03SCMON または SUSG データの利用
- 04明示的な監査範囲の指定
- 05オブジェクトごとの実行数表示
活用シーン
- 01特定パッケージのカスタムコード退役を評価
- 02名前空間内で実行記録のないオブジェクトを確認
- 03静的呼び出し元も含めて削除候補を審査
MCP 対応
このスキルは ARC-1 を介して SAP にアクセスし、クエリ、検索、読み取り、参照ナビゲーション機能を使って分析します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ arc-mcp/ arc-1 --skill sap-unused-codeインストール後、Claude Code で/sap-unused-codeを入力して読み込まれたか確認します。
詳細
SAP 未使用コードの検出
実行時に一度も呼び出されていない Z/Y カスタムオブジェクトを特定します。これは、信頼性の高いカスタムコード廃止プロジェクトの前提条件です。SAP のコールモニター(SCMON)の実行時使用データと、静的な使用箇所検索を組み合わせ、各オブジェクトを UNUSED / LIKELY_UNUSED / USED / INDETERMINATE に分類します。
⚠ 必須:スコープと目的
このスキルは、フィルターが指定されていない場合は実行されません。 ECC システム全体を対象に「未使用コードをすべて一覧表示する」レポートを作成すると、数万行が返され、役に立ちません。ユーザーは、次のいずれかを少なくとも 1 つ指定する必要があります。
- パッケージ(例:
Z_FI_CUSTOM)— このパッケージ内のオブジェクトのみを分析します - 名前空間プレフィックス(例:
Z_SALES_*、/ACME/*)— パターンに一致するオブジェクトのみを対象にします - オブジェクトリスト — チェックする名前をカンマ区切りで指定します
- 目的の説明 — 例:「過去 90 日間に実行されていない FI-GL の Z レポートを見つける」—目的で絞り込みます
いずれも指定されていない場合は、質問してください。確認の質問例:
監査するパッケージまたは名前空間を教えてください。例:「Z_REPORTING パッケージ」、「Z_FI_ で始まるすべての Z*」、または「削除を予定しているこの 15 個のレポート」。
見た目以上に難しい理由(計画を提案する前にお読みください)
SAP には、関連する監視システムが 2 つあります。どちらを扱っているかを理解してください。
| システム | 概要 | データの保存場所 | 遅延 | データの保持期間 |
|---|---|---|---|---|
| SCMON(ABAP Call Monitor) | 呼び出しごとの生カウンター | SCMON_DATA + SCMON_PROG + SCMON_SUB + SCMON_RDATA テーブル | 有効時はリアルタイム | 約 7 日間のローリング保存 |
| SUSG(Usage Statistics Generator) | SCMON の日次集計 | SUSG_DATA + SUSG_PROG + SUSG_RDATA + SUSG_SUB + SUSG_ADMIN テーブル(または CDS ビュー SUSG_I_DATA、SUSG_I_RDATA、SUSG_I_ODATA) | バッチスケジュールに依存(デフォルトは毎日約 02:30) | 数か月から数年間 |
重要な制約: レコード数のしきい値に達すると、SCMON は自動的に無効になります(SCMON_CONF_SET_MAX_NUM_SLICES)。集計対象期間に SCMON が無効だった場合、SUSG は集計を拒否します。開発/テストシステムでは、このためパイプラインが頻繁に停止します。
3 つの選択肢(システムの状態に応じて選択)
オプション A(推奨)— ARC-1 経由で SCMON テーブルを直接結合
SUSG の集計が実行されたかどうかにかかわらず、SCMON が一度でも有効になったシステムならすぐに利用できます。
-- "Which programs actually ran?"
SELECT p~PROGNAME, p~OBJECT, p~OBJ_NAME, SUM( d~COUNTER ) AS EXECS
FROM SCMON_DATA AS d INNER JOIN SCMON_PROG AS p ON d~TRIGID = p~PROGID
WHERE <filter>
GROUP BY p~PROGNAME, p~OBJECT, p~OBJ_NAME
ORDER BY EXECS DESCENDING
-- "Which transactions/reports triggered anything?"
SELECT s~ROOTNAME, s~ROOTTYPE, SUM( d~COUNTER ) AS EXECS
FROM SCMON_DATA AS d INNER JOIN SCMON_SUB AS s ON d~SUBID = s~SUBID
GROUP BY s~ROOTNAME, s~ROOTTYPE
列に関する注意(SAP の命名は逆になっています):
SCMON_DATA.TRIGID→SCMON_PROG.PROGID(名前とは異なり、実行されたプログラム)SCMON_DATA.SUBID→SCMON_SUB.SUBID(トリガーの起点。開始元となったトランザクション/レポート)
メリット: バッチを待つ必要がなく、スライスをローテーションする手間もなく、今日すぐに実行できます。
デメリット: 保持期間が短く(約 7 日間)、「過去 6 か月間に実行されたか」といった質問には役立ちません。
オプション B — CDS ビュー経由の集計済み SUSG
顧客が SUSG を適切にスケジュールしている場合に使用します(通常、本番システムでは設定されています)。
-- Same question, longer window
SELECT PROGNAME, OBJ_TYPE, OBJ_NAME, SUM( COUNTER ) AS EXECS, MAX( LAST_USED ) AS LAST_SEEN
FROM SUSG_I_DATA
WHERE <filter>
GROUP BY PROGNAME, OBJ_TYPE, OBJ_NAME
事前結合済みの CDS ビュー:SUSG_I_DATA、SUSG_I_RDATA、SUSG_I_ODATA。いずれも @AccessControl.authorizationCheck: #NOT_REQUIRED です。
メリット: より長い履歴(数か月分)があり、事前に結合されているため SQL が簡潔で、公式にサポートされているデータです。
デメリット: SUSG バッチの実行が必要です(実行されるまでは SUSG の行数は 0)。対象期間中に SCMON が無効になっていた場合は、何も機能しません。
オプション C — SUSG XML の手動エクスポート(最終手段のみ)
顧客がすでにトランザクション SUSG を実行し、5 つの XML ファイル(ADMIN0001.xml、PROG0001.xml、DATA*.xml、RDATA*.xml、SUB0001.xml)をエクスポートしている場合、このスキルでローカルに解析できます。これは Kiro の方法であり、最後の手段にしてください。SAP GUI での人手による操作と、オフラインでのファイル受け渡しが必要です。
このスキルには XML パーサーは含まれていません。ユーザーがオプション C を希望する場合は、Kiro の parse-susg.py を使用するか、ローカルの Python スクリプトを作成するよう伝えてください。ARC-1 では対応できません。
スマートデフォルト(黙って適用し、質問しない)
| 設定 | デフォルト | 根拠 |
|---|---|---|
| オプションの選択 | まず SCMON の稼働状況を確認し、ライブデータがあれば A、なければ B を使用します | ほとんどのシステムには、どちらか一方があります |
| 出力の分類 | UNUSED / LIKELY_UNUSED / USED / INDETERMINATE | Kiro の 4 種類の分類です。優先順位付けに役立ちます |
| 使用箇所の確認 | はい。実行時に確認されなかったオブジェクトが対象です | 静的参照も有効なシグナルです |
| レポートの詳細度 | オブジェクトごとに件数を表示し、上位 20 件と概要を示します | ご要望があれば全件を表示できます |
前提条件
事前に確認してください。不足している項目があれば、処理を停止してユーザーに伝えてください。
- ARC-1 サーバーで
SAP_ALLOW_FREE_SQL=trueが設定されていること(設定されていない場合、FreeSQL を実行するSAPQueryはブロックされます)。必要に応じて管理者に確認してください。 - ユーザーが
SCMON_DATA、SCMON_PROG、SCMON_SUB、SCMON_RDATAに対するS_TABU_NAM権限を持っていること(オプション B の場合は、同等の SUSG テーブルが対象です)。 - SCMON が過去に一度でも有効化されていること(オプション A)、または SUSG の集計が実行済みであること(オプション B)。次のクエリで確認します。
SAPQuery(sql="SELECT COUNT(*) AS CNT FROM SCMON_DATA")
SAPQuery(sql="SELECT COUNT(*) AS CNT FROM SUSG_I_DATA")
どちらも 0 行の場合、データ収集が行われていません。次のセクションをユーザーに案内して、処理を停止してください。
顧客に SCMON/SUSG データがまだない場合
次のように伝えてください。
- SAP GUI でトランザクション
SCMONを実行し、モニターを有効化して、N 日間稼働させます(最低 7 日間が一般的です)。 - トランザクション
SCMONLではスライスの状態を、SCMONDでは生データを確認できます。 - データを永続的に保持するには、
SCMON_COLLECTを日次ジョブとしてスケジュールし、その後にSUSG_COLLECT_FROM_SCMONを実行します。 - データ収集が始まったら、このスキルを再実行します。
ステップ 1:使用するオプションを確認する
上記の 2 つの COUNT クエリを実行します。判定表は次のとおりです。
| SCMON_DATA の件数 | SUSG_I_DATA の件数 | 使用するオプション |
|---|---|---|
| > 0 | > 0 | A(データが新しく、リアルタイムです)。ただし、より長期間の履歴には B も使用できることを伝えます |
| > 0 | 0 | A(SUSG による集計がまだ行われていません) |
| 0 | > 0 | B(SCMON のデータは期限切れですが、SUSG に履歴があります) |
| 0 | 0 | 停止します。 SCMON を有効化するようユーザーに伝え、推測で進めないでください。 |
ステップ 2:対象範囲を列挙する
ユーザーが指定したフィルター入力に基づいて処理します。
2a. パッケージ
SAPRead(type="DEVC", name="<package>")
実行時の型(PROG、CLAS、FUGR、FUNC)のうち、Z/Y で始まるオブジェクトだけを残します。(テーブル、CDS ビュー、インターフェースは、SCMON/SUSG が直接捕捉する「実行時に呼び出される」オブジェクトではありません。)
2b. 名前空間/プレフィックス
SAPSearch では objectType フィルターを 1 つだけ指定できるため、実行時の型ごとに呼び出して結果を統合します。
SAPSearch(query="<prefix>*", objectType="PROG")
SAPSearch(query="<prefix>*", objectType="CLAS")
SAPSearch(query="<prefix>*", objectType="FUGR")
2c. オブジェクトリスト
指定された名前をそのまま使用します。
これを セット S(候補リスト)と呼びます。
ステップ 3:実行時の使用状況をクエリする
オプション A(SCMON の直接結合)
セット S から WHERE 句を作成します。SAP FreeSQL にはクエリ長の制限があるため、|S| > 100 の場合は分割します。
SELECT p~PROGNAME, p~OBJECT, p~OBJ_NAME, SUM( d~COUNTER ) AS EXECS
FROM SCMON_DATA AS d INNER JOIN SCMON_PROG AS p ON d~TRIGID = p~PROGID
WHERE p~OBJ_NAME IN ( 'ZCL_FOO', 'ZR_BAR', ... ) -- or p~OBJ_NAME LIKE 'Z_FI_%'
GROUP BY p~PROGNAME, p~OBJECT, p~OBJ_NAME
トリガー側も確認します(Z オブジェクトの中には、内部呼び出し元としてではなく、トランザクション/RFC としてのみ実行されるものがあります)。
SELECT s~ROOTNAME, s~ROOTTYPE, SUM( d~COUNTER ) AS EXECS
FROM SCMON_DATA AS d INNER JOIN SCMON_SUB AS s ON d~SUBID = s~SUBID
WHERE s~ROOTNAME IN ( ... )
結果を統合します。セット U(実行時に使用されたオブジェクトとその件数)を作成します。
オプション B(SUSG)
SELECT PROGNAME, OBJ_TYPE, OBJ_NAME, SUM( COUNTER ) AS EXECS, MAX( LAST_USED ) AS LAST_SEEN
FROM SUSG_I_DATA
WHERE <scope filter>
GROUP BY PROGNAME, OBJ_TYPE, OBJ_NAME
セット U を作成し、LAST_SEEN タイムスタンプも含めます。
ステップ 4:U に含まれないオブジェクトの静的な使用箇所を確認する
S に含まれていて U に含まれていない各オブジェクトについて、次を実行します。
SAPNavigate(action="references", type="<type>", name="<name>")
(references は ARC-1 の使用箇所検索アクションで、内部で SAP の使用箇所検索 API を呼び出します。)
セット W(ソース内から参照されている静的参照オブジェクト。観測期間中に実行されていなくても、何らかのコードから呼び出されています)を作成します。
ステップ 5:分類する
最初に一致した分類を適用します。
| オブジェクトの該当先 | 分類 | 意味 |
|---|---|---|
| U(実行時に 1 回以上実行) | USED | 本番環境で実行されています。保持してください |
| W(ソース内で参照されているが、U には含まれない) | LIKELY_UNUSED | 静的な呼び出し元はありますが、観測期間中に実行されたものはありません |
| U にも W にも該当しない | UNUSED | 削除候補として有力です |
| S には含まれるが、システム上にオブジェクトが存在しない(例:SAPSearch で解決できなかった) | INDETERMINATE | 手動で確認してください |
ステップ 6:レポートを出力する
6a. 見出し
Unused Code Audit — <scope>
Data source: SCMON (2,843 rows, window: 2026-04-12 → 2026-04-19) ← Option A
— or —
Data source: SUSG (Aggregated 2026-01-01 → 2026-04-19, 128 days) ← Option B
Scope: 42 objects
USED: 18 (43%) — safe to keep
LIKELY_UNUSED: 12 (29%) — still has static callers; investigate
UNUSED: 10 (24%) — deletion candidates
INDETERMINATE: 2 (5%) — manual review
6b. UNUSED テーブル(削除候補)
Object Type Package Last change (TR)
ZR_OLD_FI_POSTING PROG Z_FI_CUSTOM 2024-02-15 (MARIAN / A4HK900042)
ZCL_LEGACY_HELPER CLAS Z_FI_CUSTOM 2023-11-03 (MARIAN / A4HK900031)
...
「最終変更」は SAPRead(type="VERSIONS") から取得します。古く、かつ未使用のオブジェクトが最も安全に削除できます。
6c. 呼び出し元のヒントを含む LIKELY_UNUSED テーブル
LIKELY_UNUSED オブジェクトごとに、参照しているオブジェクトを示します。
ZCL_OLD_SALES_UTILS (CLAS)
Called by: ZR_SALES_JOB (PROG — also UNUSED — cascade delete)
ZCL_ACTIVE_HANDLER (CLAS — USED at runtime) ← blocks deletion
呼び出し元がすべて UNUSED の LIKELY_UNUSED オブジェクトは、依存関係をたどって削除できます。アクティブな呼び出し元が 1 つでもあれば、削除はできません。
6d. 次のステップの選択肢
- 「静的参照グラフをツリー形式で表示しますか?」(→
SAPNavigate where-usedを連続実行) - 「未使用オブジェクトに取りかかる前に、使用中のオブジェクトをクリーンコアチェックしますか?」(→ sap-clean-core-atc)
- 「使用中のオブジェクトのドキュメントを表示しますか?」(→ sap-object-documenter)
- 「これをレビュー済みの S/4HANA 移行ドシエにして、HTML/CSV/グラフ形式で出力しますか?」(→ sap-migration-dossier)
- 「UNUSED オブジェクトの削除を始めますか?」→ 手動で行ってください。このスキルは何も削除しません。ユーザーが
SAPWriteまたは移送を通じて削除します。
エラー処理
| 症状 | 根本原因 | 対処方法 |
|---|---|---|
SCMON_DATA の件数 = 0、SUSG_I_DATA の件数 = 0 | モニタリングが一度も有効化されていない | 停止し、SCMON を有効化するようユーザーに案内します(上記の手順を参照) |
SCMON にデータがあるが、すべて SLICEID=1 で SUSG は 0 | スライスがまだ切り替わっていない。SUSG は未クローズのスライスを集計できない | オプション A(直接)を使用して SUSG をスキップするか、SA38 から SCMON_COLLECT_ALL を実行してから SUSG_COLLECT_FROM_SCMON を実行します |
SUSG_LOG に「SCMON not active」と表示される | SCMON が自動的に無効化された(レコード数のしきい値に到達) | SCMON_CONF_SET_MAX_NUM_SLICES でしきい値を引き上げ、SCMON を再有効化します。集計サイクルが 1 回失われたことは受け入れてください |
SAP_ALLOW_FREE_SQL=false | 管理者ポリシー | 制限の緩和を依頼するか、SAPRead(type="TABLE_CONTENTS", name="SCMON_DATA", maxRows=…) に切り替えます(SAP_ALLOW_DATA_PREVIEW=false の場合もブロックされます。どちらの場合も結合はできません)。レポートの品質は大きく低下します |
SCMON_*/SUSG_* テーブルで 403 | S_TABU_NAM 認可が不足している | ユーザーに認可の割り当てを依頼し、申請に対象テーブルを記載します |
| 対象範囲から 500 件を超える候補が返される | ユーザーのフィルター範囲が広すぎる | 拒否し、対象を絞るようユーザーに依頼します(500 件のオブジェクト削除リストは実行に移せません) |
注意事項
ランタイムデータからはわからないこと
- カバレッジの欠落:年末に毎年実行されるレポートは、1 週間分の SCMON データには現れません。観測期間とオブジェクトの想定使用頻度を常に考慮してください。
- デッドコード検出 ≠ コード削除:UNUSED オブジェクトは、緊急時/災害復旧用のコード、試験期間中のみ使用するコード、または観測期間中に実行されなかったスケジュールジョブから呼び出されるコードである可能性があります。削除前に業務担当者に確認してください。
- 動的呼び出し:
CALL FUNCTION IN BACKGROUND、動的なプログラム名を指定するSUBMIT、または RTTI 経由の CALL METHOD では、SCMON が正しい PROGNAME に帰属させられない場合があります。誤って UNUSED と判定されるケースを完全になくすことはできません。
このスキルで行わないこと
- 削除は行いません。 レポートのみを作成します。削除は人が判断し、できれば移送と CTS を通じて行う必要があります。
- システム間の統合は行いません(DEV + QAS + PRD の使用状況)。各システムからのエクスポートが必要です。このスキルは一度に 1 つのシステムのみを読み取ります。
- 非推奨だが使用中のコードに対する置き換え案の提示は行いません。 その場合は sap-clean-core-atc を使用してください。
- XML のインポートは行いません(オプション C)。顧客が SUSG の XML ダンプのみを持っている場合は、オフラインパーサーが必要です。
このスキルを使用する場面
- カスタムコードの廃止プロジェクトの対象範囲を定めるとき
- 移行前:使用していないものは移行しない
- プロジェクト後の整理:ロールバックされた機能向けに作成したコードを削除するとき
- デッドコードの一覧が必要なライセンス/コンプライアンスレビュー
- sap-clean-core-atc と組み合わせる場合: 使用中のレベル A コードを移行し、使用中のレベル C/D コードを監査し、UNUSED コードを完全に削除します