Skip to content

IBM i / AS400 Decoder

FieldValue
TypeSkill Resource
Source~/.copilot/skills/architecture/references/discovery/ibm-i-as400.md
DescriptionNot specified

Source Content

IBM i / AS400 Decoder

A field guide for reading an IBM i (AS/400) codebase when you have never seen one. Read this before phase 2 of the playbook when the platform family is IBM i.

Contents

What the platform actually is

IBM i is a business computer, not just an OS. Today’s name is IBM i running on Power Systems hardware; it was AS/400 (1988), then iSeries, then System i — the same lineage, and old-timers still say “the 400.” The OS was OS/400, then i5/OS, now IBM i. When someone says “we’re on the AS400,” they mean this stack.

Two facts explain almost everything that feels strange about it:

  1. The database is part of the operating system. DB2 for i is not a product you install; it is built in. Every “file” on the machine is a database object. There is no /home/app/data.csv — there is a physical file object in a library.
  2. The OS is object-based, not file-based. You do not navigate folders. You reference objects (each with a type like *PGM or *FILE) inside libraries. A “library list” is the search path.

The object model

Everything is an object of a specific type. These are the ones you will see in a discovery:

*LIB — library

A container of objects (roughly a schema/namespace). QSYS holds the OS; app code lives in app libraries. The library list (*LIBL) is the ordered search path the job uses to resolve unqualified names.

*FILE — file object

The catch-all for data and screens. Sub-typed by its DDS: a physical file (table), logical file (view/index), display file (screen), or printer file (report). “File” on IBM i almost never means a stream file — it means a database or device object.

*PGM — program

A runnable object, compiled from RPG, COBOL, CL, or C. *MODULE and *SRVPGM are the ILE building blocks that bind into programs (see ILE).

*DTAARA — data area

A tiny named persistent value (a counter, a flag, a next-check-number). Small but business-critical — see Where state hides.

Other objects worth knowing

ObjectWhat it isWhy discovery cares
*DTAQData queue (async message queue)Job-to-job integration; easy to miss
*JOBQJob queueWhere submitted batch jobs wait
*JOBDJob descriptionDefault library list + settings for a job
*OUTQOutput queueWhere spooled reports land
*MSGFMessage fileExternalized error/prompt text
*JRNJournalTransaction log / audit trail
*CMDCommandA custom green-screen command
*USRSPC / *USRIDXUser space/indexAd-hoc storage; rare but opaque when present

How source shows up in a git repo

On the live machine, source code lives as members inside source physical filesQRPGLESRC, QCLSRC, QDDSSRC, and so on. Each member has a member type (RPGLE, CLLE, PF, DSPF…) that tells the compiler what it is. That member type — not the filename — is authoritative on the box.

When a team exports to Git (commonly via IBM’s Rational Developer for i / the ibmi-bob build tool), members become IFS stream files with extensions that mirror the member type: PAYCALC.RPGLE, CUSTMAST.PF, ORDENT.DSPF. That mapping is your fastest inventory signal — see the cheat sheet. Trust the extension, but confirm by reading the first few specs, because teams rename inconsistently.

RPG: the four flavors

RPG is not one language. The flavor sets how the code reads, so identify it before you read a single line of logic. From oldest to newest:

RPG III / RPG/400 (OPM, fixed-format)

Column-dependent, uppercase, and driven by the RPG cycle (see below). Lives in QRPGSRC, extension .rpg or .rpg36/.rpg38. If you see code where columns matter and there is no explicit main read loop, this is it. Most “scary old” AS400 code is here.

RPG IV / ILE RPG (fixed-format)

The 1994 rewrite. Still column-oriented on the C/F/D specs but far richer: real subprocedures, data structures, pointers, ILE binding. Extension .rpgle. Column meanings differ from RPG III — do not read RPG IV columns with an RPG III ruler.

Fully free-form RPG (**FREE)

Modern RPG that reads like Pascal/C — no column rules. A **FREE on line 1, or /free … /end-free blocks inside older code. Extension .rpgle. The easiest flavor to read.

SQLRPGLE (embedded SQL)

Any RPG flavor with EXEC SQL … END-EXEC blocks — SQL statements embedded in RPG. Extension .sqlrpgle. Treat the SQL as first-class business logic; it often replaces native record-level I/O.

Reading fixed-format RPG

Fixed-format lines start with a specification letter in a fixed column. Learn the specs and you can skim any RPG program:

SpecColumn markerPurpose
HControlCompile options, program-wide settings
FFileDeclares files used (input/output/update), device
DDefinitionVariables, constants, data structures, prototypes
IInputInput record field layout (older programs)
CCalculationThe actual logic — the main event
OOutputOutput record layout (older programs)
PProcedureSubprocedure boundaries (RPG IV)

The opcodes you must recognize on C specs (and in free-form):

OpcodeMeaningDiscovery note
CHAINRandom read by key”Look up one record” — the workhorse
SETLL / SETGTPosition a cursor before readingPrecedes READE/READ loops
READ / READE / READPSequential read / read-equal / read-priorLoop over records
WRITE / UPDATE / DELETECreate / modify / remove a recordThe CRUD verbs
EVAL / Z-ADD / MOVE/MOVELAssignment (modern / numeric / legacy)MOVE has surprising truncation rules
IF/ELSE/ENDIF, DOW/DOU, SELECT/WHENControl flowStandard
EXSR / BEGSR/ENDSRCall / define an internal subroutineRules cluster inside subroutines
CALL/CALLP / KLIST/KFLDCall a program / build a composite keyCross-program calls; multi-field keys

The RPG cycle and indicators

These two features trip up every newcomer. Flag them explicitly in the onboarding doc.

The RPG cycle is an implicit program-wide loop that older RPG (III especially) runs for you: it reads the primary file, processes a record, and repeats until end-of-file — without any loop you can see. So a program can “process every customer” with no visible READ loop. When *INLR (the Last Record indicator) is set on, the cycle ends and the program closes down. If you cannot find the loop, the loop is the cycle.

Indicators are 99 numbered on/off flags (*IN01*IN99) plus named ones (*INLR, *INOF). They carry meaning by convention, not by name: *IN60 might mean “record not found” in one program and “print totals” in another. On display files they map to function keys and error states. Because a flag named *IN43 tells you nothing, indicator-heavy code is where business meaning is most hidden — trace every indicator to where it is set and tested.

DDS

DDS (Data Description Specifications) is the fixed-format language that defines files. Four sub-types matter, and each is a different thing:

PF — physical file (a table)

Defines the actual stored records: a record format (R), fields with types and lengths, and key fields (K). This is your table. Example decode: a field line PRICE 9 2P is a 9-digit, 2-decimal packed number. Field types: A char, P packed decimal, S zoned, B binary, L date, T time, Z timestamp. See references/data-model-extraction.md for the full decode.

LF — logical file (a view or index)

A key-sequenced and/or filtered access path over one or more PFs. It can re-order (different key), subset columns, SELECT/OMIT rows, or join multiple PFs (a join logical file). Logical files are how the old world did indexes and views — treat each one as a query the business cared about enough to persist.

DSPF — display file (a green screen)

Defines 5250 screens: record formats, field positions (row/column), function-key keywords (CAxx/CFxx), attributes (DSPATR), edit codes (EDTCDE), and subfiles (SFL/SFLCTL) — the scrollable list control. A DSPF is the closest thing to “the UI”; each record format is roughly one screen or panel. See references/mermaid-legacy-patterns.md for turning these into user-journey diagrams.

PRTF — printer file (a report)

Defines report layouts for spooled output. Often the only specification of what a report contains and how totals are computed, so read PRTFs when reverse-engineering reporting requirements.

CL (Control Language)

CL is the job-control / scripting language — the glue that runs programs, sets up files, and drives batch. Two flavors: CLP (OPM) and CLLE (ILE); read the same. Extensions .cl, .clp, .clle. Commands you will see constantly:

  • PGM / ENDPGM — program boundaries.
  • DCL — declare a variable; DCLF — declare a file (usually a display file for a prompt screen).
  • CALL — run another program (RPG/CL/COBOL), passing parameters.
  • SBMJOBsubmit a job to batch (to a *JOBQ). This is the fork point between interactive and batch — trace every SBMJOB.
  • OVRDBFoverride a file at runtime (redirect a program’s file to a different member/library). A huge source of “the code says X but production does Y.”
  • MONMSG — monitor for a message/error and handle it (the CL equivalent of try/catch).
  • RTVDTAARA / CHGDTAARA — read/write a data area (see below).
  • CHGVAR, IF/ELSE, DOWHILE, GOTO — assignment and control flow.

DB2 for i

The integrated relational database. Two worlds coexist in the same tables:

  • DDS-created files — older tables defined by a PF (as above). Field names are ≤10 chars, uppercase.
  • SQL DDLCREATE TABLE/CREATE INDEX/CREATE VIEW in .sql members. Modern code uses these; they can carry long names and constraints.

Both compile to the same kind of object, and a “file” and a “table” are the same thing seen two ways. Access is either native record-level I/O (RPG CHAIN/READ) or SQL (EXEC SQL, or set-based .sql). The catalog lives in QSYS2 (QSYS2.SYSTABLES, SYSCOLUMNS, SYSKEYS) — useful if you get a live connection to validate the reverse-engineered model. Watch for system naming (LIB/FILE) vs SQL naming (LIB.FILE); it changes how joins and qualifiers read.

Where state hides

On IBM i, important state lives outside the tables in three places newcomers miss:

Data areas (*DTAARA)

Named persistent scalars — the next invoice number, a “batch is running” flag, the current processing date, a company-wide config value. Read/written by RTVDTAARA/CHGDTAARA (CL) or IN/OUT (RPG). A single data area can gate an entire nightly run. The LDA (Local Data Area) is a per-job scratchpad passed between chained programs.

Data queues (*DTAQ)

FIFO/keyed async queues used to pass work between jobs — a lightweight message bus. If job A SNDDTAQs and job B RCVDTAQs, that is an integration point with no network in sight. Easy to miss and important for the dependency map.

Journals (*JRN)

The transaction log. Journaling is required for commitment control (transactions) and is the audit trail. If tables are journaled, you have a change history you can mine; if a rebuild needs audit parity, the journal defines the bar.

ILE

ILE (Integrated Language Environment) is the modern (RPG IV-era) compilation model. It matters for the call graph:

  • *MODULE — a compiled unit of one source member; not runnable alone.
  • *PGM — one or more modules bound together into a runnable program (an entry point).
  • *SRVPGM — a service program: a bound collection of reusable procedures (like a shared library / DLL). Programs call its exported procedures via a binding directory.
  • Activation group — the runtime scope for a program’s resources (variables, open files, commit cycle). *NEW vs *CALLER vs named groups changes whether state and transactions are shared — relevant when reasoning about isolation.

For the call graph, a CALLP to a procedure may resolve into a *SRVPGM you must open separately; do not assume every callee is a standalone program.

File-extension cheat sheet

In an exported Git repo, classify by extension first (confirm by reading the header):

Extension(s)Member typeIt is a…
.rpg, .rpg36, .rpg38RPG / RPG36/38RPG III (fixed, cycle-driven) program
.rpgleRPGLERPG IV / free-form program
.sqlrpgleSQLRPGLERPG IV with embedded SQL
.clp, .clCLPOPM Control Language
.clle, .cllesrcCLLEILE Control Language
.pf, .pf.ddsPFPhysical file (table)
.lf, .lf.ddsLFLogical file (view/index/join)
.dspf, .dspf.ddsDSPFDisplay file (green screen)
.prtf, .prtf.ddsPRTFPrinter file (report)
.sql, .table, .viewSQLSQL DDL/DML
.cbl, .cblleCBL/CBLLECOBOL (see references/mainframe-cobol.md)
.cmdCMDCustom command definition
.bnd, .bnddirBNDBinder source / binding directory

When an extension is missing or wrong, the first non-comment line usually reveals the type: **FREE/H/F/D specs → RPG; PGM → CL; A in column 6 with R record formats → DDS; IDENTIFICATION DIVISION → COBOL.