Dependency Mapping
| Field | Value |
|---|---|
| Type | Skill Resource |
| Source | ~/.copilot/skills/architecture/references/discovery/dependency-mapping.md |
| Description | Not specified |
Source Content
Dependency Mapping
How to map what connects to what in a legacy system — which programs call which, which programs read and write which files, what order the batch jobs run in, and where the system touches the outside world. This is phase 4 of the discovery playbook. It is the map that tells you what you can safely change, and what will break if you do.
Contents
- Two engineers, one broken feed
- The four maps
- Program call graph
- File-usage / CRUD matrix
- Batch-job DAG
- Integration inventory
- The OVRDBF trap: code says X, prod does Y
- Output artifacts
Two engineers, one broken feed
Jack looked at RPTGEN, a program that wrote a work file every night, and saw nothing else reading it in the online app. “Dead code — safe to retire,” he wrote, and the team pulled it from the nightly cycle. The next morning, a state agency’s wage-reconciliation feed arrived empty: a downstream job Jack never opened had read that work file, formatted it, and pushed it over SFTP to the agency. The output had no consumer in the code he looked at — its consumer was a batch step two jobs later and a vendor no one had documented.
Jill built the file-usage matrix first. Before calling anything safe to retire, she listed every program against every file and marked who created, read, updated, and deleted each one. RPTGEN’s work file showed a second reader — the feed job — right there in the matrix, three columns over.
She traced that reader to an SFTP push in the integration inventory and flagged the whole chain as load-bearing. Nothing broke, because the matrix made the hidden consumer visible before anyone touched the code.
The four maps
Dependency mapping is four artifacts, not one. Each answers a different question, and a modernization decision needs all four — a program can be call-graph-isolated but file-coupled, or file-isolated but wired into a scheduler dependency you cannot see in any source file.
| Map | The question it answers | Built from |
|---|---|---|
| Program call graph | What calls what? | CALL/CALLP/LINK/XCTL statements across programs |
| File-usage / CRUD matrix | Who touches which file, and how? | File I/O verbs per program, mapped program × file |
| Batch-job DAG | What runs when, and in what order? | JCL steps and scheduler dependencies |
| Integration inventory | Where does the system touch the outside world? | Feeds, extracts, queues, spool output, screen-scraping |
What to notice: the four maps overlap but none is a substitute for another — the batch DAG catches scheduler edges no source file mentions, and the integration inventory catches consumers that live entirely outside the codebase.
Program call graph
The call graph is the edges between programs: program A calls program B, which transfers to program C. Build it by finding every cross-program call and recording the caller, the callee, and whether control returns.
The call constructs, per platform
Per platform, the constructs to grep for:
RPG (IBM i)
CALL and CALLP invoke another program, passing parameters. A CALLP may resolve into a procedure exported by a service program (*SRVPGM) rather than a standalone program — you must open the binding directory to find the real target, so do not assume every callee is its own *PGM. KLIST/KFLD build composite keys but are not calls. See the IBM i decoder for CALL/CALLP and ILE service-program binding.
COBOL
CALL 'PROGNAME' is the cross-program edge — static (literal name) or dynamic (a variable name resolved at runtime). Dynamic calls are the trap: the callee is a data value, so grep for the CALL and then trace what the variable holds. See the COBOL decoder for CALL syntax.
CICS
LINK calls a program and returns (like a subroutine, with its own COMMAREA); XCTL transfers with no return — control never comes back, so draw it as a flow edge, not a call/return. RETURN TRANSID sets the next transaction, another kind of edge. TRANSID routing (from the CSD) maps a transaction code to its initial program. See the CICS decoder for LINK, XCTL, and TRANSID routing.
The trap across all three: dynamic and bound calls hide the real target. A COBOL dynamic CALL, an RPG CALLP into a *SRVPGM, and a CICS XCTL to a program named in a variable all need a second step to resolve — mark them and chase the actual callee.
File-usage / CRUD matrix
The CRUD matrix is a grid: programs down the side, files across the top, each cell marked with which operations that program performs on that file — Create, Read, Update, Delete. It is the single most useful artifact for a retirement decision, because it shows every producer and every consumer of every file in one view.
Map the I/O verbs to CRUD per platform:
| Operation | RPG (IBM i) | COBOL batch | CICS |
|---|---|---|---|
| Create | WRITE | WRITE | WRITE |
| Read | CHAIN, READ, READE, SETLL+read | READ | READ, STARTBR+READNEXT |
| Update | UPDATE | REWRITE | REWRITE |
| Delete | DELETE | DELETE | DELETE |
Note the naming trap: COBOL and CICS spell update REWRITE, not UPDATE — a WRITE is always a create. Embedded SQL (EXEC SQL in either language) uses INSERT/SELECT/UPDATE/DELETE, which map cleanly. The decoders list each verb: the IBM i decoder, the COBOL decoder, the CICS decoder.
A worked fragment of the matrix:
| Program | CLAIMS | WAGE | PAYMENT | CLAIM-WORK |
|---|---|---|---|---|
CHKELIG | R U | R | - | - |
CALCBEN | R U | R | C | - |
RPTGEN | R | - | R | C |
WAGEFEED | - | - | - | R |
What to notice: reading down the CLAIM-WORK column reveals RPTGEN creates it and WAGEFEED reads it — the exact hidden producer-consumer pair Jack missed. A file with a writer but no reader is a retirement candidate; a file with a reader but no writer in your inventory means a producer lives outside it — chase it.
Batch-job DAG
The batch-job DAG is the order the nightly work runs in: which jobs run first, which wait on which, and the timing windows they must finish inside. This is a directed acyclic graph — job B runs when job A ends OK — and it is the artifact a migration most often gets wrong, because the true order lives in the scheduler, not the JCL.
The two layers of the DAG
Build it in two layers:
JCL — the steps inside a job
A JCL job is made of steps; each EXEC PGM= step runs one program, and its DD statements name the datasets it reads and writes. COND and IF-THEN-ELSE gate steps on a prior step’s return code. This gives you the flow within one job — see the COBOL decoder for JCL steps, DD, and DISP.
The scheduler — the order across jobs
The real nightly cycle — which job triggers which, and the “must finish before 6 AM” windows — lives in a scheduler: Control-M, CA-7, or IBM TWS/OPC. The scheduler export holds dependencies that no single JCL member mentions, so a call graph built from JCL alone will miss cross-job edges. If you can get the scheduler definitions, they are worth more than any one program for understanding operational risk.
On IBM i, the equivalents are SBMJOB (submit a job to a job queue — the fork point into batch) and data-area gates that turn a run on or off; data queues (*DTAQ) pass work between jobs with no network in sight, so a SNDDTAQ/RCVDTAQ pair is a batch dependency edge. See the IBM i decoder for SBMJOB and data queues.
Draw the DAG left to right with timing on the edges — the Mermaid legacy patterns reference has the batch-DAG template. What to preserve in a migration: the order, the dependencies, and every hard time window.
Integration inventory
The integration inventory is every place the system touches something outside itself — the edges that leave the codebase entirely. These are the dependencies most likely to be undocumented and most likely to break a downstream consumer nobody remembers, exactly like Jack’s wage feed. Walk this checklist against the whole system:
- SFTP / FTP feeds — files pushed to or pulled from another party on a schedule.
- Vendor extracts — files generated for an external vendor or agency (the classic hidden consumer).
- Nightly file exchanges — batch files swapped with partner systems as part of the cycle.
- Report / spool output — spooled reports (
*OUTQon IBM i) that a person or a downstream job collects; extrapartition TDQs on CICS that map to real datasets. - Database links — connections to another database (DB2 federation, ODBC/JDBC, a remote DB2 subsystem).
- Message queues / data queues — MQ, IBM i data queues (
*DTAQ), CICS transient data queues (TDQ) that trigger a program. - Screen-scraping — an external tool driving the 5250/3270 green screen as an integration of last resort; brittle and almost never documented.
For each hit, record the direction (inbound/outbound), the party on the other end, the trigger (schedule or event), and the file or message layout. An integration you cannot name the counterparty for is an open question, not a finished entry — flag it [VERIFY].
The OVRDBF trap: code says X, prod does Y
The most dangerous gap in any dependency map is the file a program appears to use versus the file it actually uses in production. On IBM i, OVRDBF (override with database file) redirects a program’s file at runtime to a different member or library — so the source says it reads CUSTMAST, but a CL program ran OVRDBF FILE(CUSTMAST) TOFILE(ARCHLIB/CUSTMAST) first, and in production it reads the archive copy.
The mainframe equivalent is the JCL DD statement: the program’s internal file name is bound to a real dataset (DSN=) only at job-run time, so the same program reads PROD.CLAIMS in one job and TEST.CLAIMS in another. The dataset is not in the COBOL — it is in the JCL that runs it.
The rule: build the CRUD matrix from the runtime binding, not the source-level file name. Trace every OVRDBF in the CL and every DD statement in the JCL, and record the file a program reads as it actually runs. See the IBM i decoder for OVRDBF and the COBOL decoder for DD and DISP.
Output artifacts
Each map has a canonical shape, and each diagram gets a “what to notice” line per the Mermaid legacy patterns reference:
| Map | Artifact | How to draw it |
|---|---|---|
| Program call graph | A flowchart of programs with call/transfer edges | Mermaid flowchart — call-graph pattern |
| File-usage / CRUD matrix | A program × file table marked C/R/U/D | Markdown table (per technical-writing’s Markdown rules) |
| Batch-job DAG | A left-to-right job graph with timing on edges | Mermaid flowchart — batch-DAG pattern |
| Integration inventory | A context diagram plus a directional table | Mermaid flowchart — integration-context pattern |
Feed these four artifacts into the modernization disposition (modernization-strategy.md): a subsystem’s coupling — how many call edges cross into it, how many files it shares, how many jobs depend on it — is a direct input to whether you retire, rehost, refactor, rebuild, or replace it.