Legacy Application Modernization: The Source Inventory Sets the Bid

  • August 23, 2026
  • Ty Woods
  • 9 min read

Two vendors can quote the same AS/400 library and land far apart. Neither one padded the number.

The gap is what each of them could see. One counted your programs. The other asked how many of those programs still have matching source on the machine. That second question moves a price more than any line count does.

Legacy application modernization services price risk, and risk lives in the uncounted

A vendor walking into an AS400 shop gets a program count and a description of what the system does for the business. That is enough to write a proposal. It falls short of a bid anyone will hold to.

Everything a vendor cannot measure becomes contingency. Contingency has to sit somewhere, and it sits in your number. A shop that hands over a real inventory converts guesswork into scope, and the price drops for a reason both sides can point at.

The inventory takes a few hours of operator time. Every command below ships with the operating system. You already own the tooling that produces the document a vendor needs.

Four counts decide the number

Ask any vendor selling legacy application modernization services what they need before quoting, and the answer reduces to four counts.

  • How many objects live in the libraries in scope
  • How many source members exist for those objects
  • How many program objects have no source at all
  • How many dependencies nobody can resolve

The first two are inventory. The last two are the ones that price the job, because each one names work that cannot be estimated from a line count.

Draw the library boundary before you count anything

Scope arrives as a business phrase. Somebody says "the order system" or "everything the warehouse touches," and both descriptions cover a set of libraries nobody has written down.

Start from the job description of your nightly batch. The library list that job runs under is the working boundary, and it usually holds two or three libraries that never come up in planning meetings. One of them is the utility library a contractor built in the nineties, and half your programs call into it.

The reference file settles the argument. DSPPGMREF records the library name beside every object a program touches, so a quick sort of that column shows you exactly which libraries your code reaches into. Anything on that list belongs in the count or belongs in a written exclusion. A modernization project that discovers a fourth library in month three has already lost its schedule.

Count the objects with DSPOBJD

Display Object Description writes the whole library to a database file:

DSPOBJD OBJ(PRODLIB/*ALL) OBJTYPE(*ALL) OUTPUT(*OUTFILE) OUTFILE(QGPL/OBJDOUT)

The system-supplied outfile format is QADSPOBJ. It carries the object name, the library, the object type and the creation date for every object in the library.

On a current IBM i release, the SQL service does the same job and returns more. The QSYS2.OBJECT_STATISTICS table function reports OBJNAME, OBJTYPE, OBJCREATED, LAST_USED_TIMESTAMP and DAYS_USED_COUNT. Run it against each library in scope and you have a spreadsheet in one statement.

Read the last-used column carefully. A program that has not run in eleven months is a candidate for the dead-code pile. A program that has not run in eleven months might also be the annual close job that everyone depends on every January. Last-used dates narrow the list of things to ask about. They never answer the question by themselves, and a vendor who treats them as proof will scope the wrong programs out of the project.

Count the source with DSPFD

Display File Description lists the members inside a source physical file:

DSPFD FILE(PRODLIB/QRPGLESRC) TYPE(*MBRLIST) OUTPUT(*OUTFILE) OUTFILE(QGPL/MBRDOUT)

That produces outfile QAFDMBRL with record format QWHFDML, an alphabetical list of every member in the file. Repeat it for QDDSSRC, QCLSRC, QRPGSRC and any other source file the shop uses. Older shops often have four or five, and one of them usually holds the members nobody remembers.

Now join the two lists on name. Objects with a matching member are the easy half of the project. Objects without one are the half that sets your budget.

The programs with no source

Every long-lived AS/400 installation has them. A program object sits in production, runs the nightly job correctly, and no member anywhere on the machine compiles into it. Somebody patched the source on a workstation in 2004 and never checked it back in. The library came off a tape that held objects and skipped the source file.

These objects cost more per unit than anything else in a modernization project, because the work is reconstruction. A team has to read the compiled behavior, interview the people who use the output, and write a specification before anyone writes a line of replacement code.

Counting them is the whole point of the exercise. Twelve orphaned programs is a workstream. Two hundred is a different project with a different plan, and finding that out during discovery costs far less than finding it out in month four.

Pull the references with DSPPGMREF

Display Program References reads compiled objects. IBM's command description states its scope plainly: it "provides a list of the system objects referred to by the specified programs." It accepts *PGM, *SRVPGM, *MODULE and *SQLPKG objects, and it writes to outfile QADSPPGM with record format QWHDRPPR.

DSPPGMREF PGM(PRODLIB/*ALL) OBJTYPE(*ALL) OUTPUT(*OUTFILE) OUTFILE(QGPL/PGMREF)

That single file tells a vendor which programs touch which files, which programs call which other programs, and how each file gets used. It is the closest thing the platform gives you to a free dependency list.

Two documented limits belong in your notes next to it, because both of them inflate what the file appears to say.

IBM records the reference information at program creation time. Its own description warns that "the referenced object names and libraries listed may be different than the actual names of the objects." A program built against a file that has since been renamed still reports the old name.

The second limit matters more. Entries get added when somebody updates an ILE program with UPDPGM or UPDSRVPGM, and IBM states that "entries are never removed." A file dropped from a program five years ago still shows up as a dependency today. The list is an upper bound on what your programs touch. Treat it that way when you hand it over, and say so in writing, because a vendor who assumes the list is current will scope files that no longer exist.

What the inventory still cannot tell you

Run all three commands and you have counts, names and an over-reported reference list. One class of dependency stays invisible in every one of those files.

Copy members do not appear. The ILE RPG compiler directives insert records from other source members while the program compiles, and the merged text becomes part of the object. The copy member is source, and DSPPGMREF reports objects, so nothing in QADSPPGM records which members a program pulled in.

That gap is the one that breaks change estimates. A field definition sitting in a single copy member can be shared by four hundred programs. The inventory shows you four hundred program objects and gives you no way to group them. Our RPG code analysis work starts at exactly this boundary, because resolving those includes takes a parser that reads source the way the compiler reads it.

Say this out loud during vendor selection. Ask each vendor how they resolve copy members and what they do with the ones they cannot find. A vendor who answers with DSPPGMREF has told you the size of their map.

Turn the inventory into a bid you can defend

A vendor holding your three outfiles can split the proposal into two parts that behave differently.

The known part gets a fixed price. Programs with source and resolved references convert on a rate the vendor can commit to, because the volume is measured and the work repeats.

Name the target language in the same document. AS/Forward converts to whatever language you specify, and Python is one common answer among several. Free-format RPG is another, and shops that intend to stay on IBM i often choose it. The choice changes the review burden per program, so a vendor quoting without it is quoting a range.

The unknown part gets a named allowance. Orphaned objects, unresolved copy members and programs flagged for manual review each carry a unit price and a count. The count is visible to both sides on day one, and it shrinks as discovery proceeds.

That structure changes the conversation during the project. A change order stops being an argument about whether the vendor underbid and becomes arithmetic against a number both parties agreed to. Modernization cost estimates fail for one reason more than any other, and it is the absence of this split.

Where AS/Forward reads the same libraries

AS/Forward ingests source directly from the IBM i and builds the graph the outfiles cannot produce. Its proprietary ingestion layer resolves copy members and interconnections first, then generates plain-English descriptions of what each program does. Golden Path Digital holds one US patent pending on that approach.

Three outputs matter for a bid. The tool flags unresolved copy members by name, so the allowance line has a real count behind it. It marks each program with a readiness status based on parser confidence, which sorts the automatic conversions from the ones a developer has to review. It maps the blast radius of a single field change across the whole library, which is the number that scopes a data migration.

The models run wherever your security review puts them, including entirely inside your own network on your own hardware. Source code stays where it sits today. Read more on the AS/Forward page.

What to send before the first vendor call

Send four things and the first conversation gets past discovery.

  • The DSPOBJD outfile for every library in scope
  • The DSPFD member lists for every source file
  • The DSPPGMREF outfile, with a note that entries are never removed
  • Your IBM i release level and the target language you want the code in

Add one sentence naming the business deadline that started the project. An auditor's finding and a hardware service date point at different scopes, and a vendor who knows which one you are facing will sequence the work differently.

The shops that get clean bids are the ones that measured their own mess first and handed somebody the measurement. Talk to us about running the inventory against your libraries.

Leave a Reply

Your email address will not be published. Required fields are marked *