RPG IV Style, Standards, and Best Practices

by Bryan Meyers

The classic comedy skit "The 2,000 Year-Old Man" featured straight man Carl Reiner interviewing Mel Brooks as a recently discovered man from the ancient past, with a New York Yiddish accent. As Brooks shared his memories with Reiner, he also marveled at the changes and achievements of the last 2,000 years ("Mankind's greatest achievement: Saran Wrap.") and dispensed advice for a long life ("I never run for a bus.").

When looking at today's RPG programs, a classic RPG programmer might feel the same uneasy awe as the 2,000 year-old man. Recent releases have introduced enough changes into the RPG language that even if you're already coding RPG IV, you might need a refresher course to exploit the newest features; a current RPG IV program bears little or no resemblance to the original RPG that crawled onto dry land in the 1960s.

As fast as the language is changing, its best practices and style guidelines are proving to be moving targets. The goals in any discussion of standards and practices should be to accelerate the application development process, facilitate program maintenance, and reduce errors. These goals are accomplished when you write programs with code reuse and standards uppermost in your mind.

This article presents some updated suggestions for how to write RPG IV programs that are easy to read, understand, and maintain. Many of these guidelines require V5R1; if a suggestion requires a newer release, I'll note the requirement.

These guidelines are based on those originally set forth in "The Essential RPG IV Style Guide" (NEWS/400, June 1998), "RPG IV Style Revisited" (iSeries NEWS, February 2002), and "RPG IV Style, Standards, and Best Practices" (iSeries NEWS, March 2005).The guidelines are arranged under these headings:

También publicado en español, ServerNEWS, Junio-Julio 2005.
 

Free the Factor 2

Repeat after me: "RPG IV is a free-format language; I will use no fixed-format calculations." This is the prime commandment regarding RPG IV style.

The advantages of free-format are well proven. It's easier to read, document, and maintain than fixed-format code, and its syntax is consistent with other more modern computer languages.

In many cases, if you use the free-format specification, good standards will result automatically, because the new specification doesn't allow much of the obsolete baggage and poor practices that fixed-format C-specs allowed. Also, beginning with V5R2, many of RPG's new features are supported only in free-format.

Avoid mixing fixed-form style and free-form style in your C-specs. The result is inconsistent and difficult to read. Take full advantage of the more natural order and expanded space afforded by the free-form specification. When you're coding loops and groups, you'll find that the code looks and feels better in free-form. Use free-form expressions wherever possible: for arithmetic, string, and logical expressions; for assignment and comparison; to set indicators (if you need them). But don't completely abandon columnar alignment as a tool to aid readability in expressions. Especially when an expression must continue onto subsequent lines, align the expression to make it easier to understand:

/Free
 TotalPay = (RegHours * Rate)       +
            (OvtHours * Rate * 1.5) +
            (DblHours * Rate * 2);
/End-free
Unfortunately, embedded SQL statements still require a fixed-format entry. Until IBM makes right this glaring design gaffe, you can alleviate the eyesore somewhat by indenting the RPG code to match the SQL code:
 ...
           PgmAccount = 'G5X67';
 /End-free
C/Exec SQL Select FirstName, LastName, City, State
C+           Into :InFirstName, :InLastName, :InCity, :InState
C+           From MMaster
C+           Where Account = :PgmAccount
C/End-exec
 /Free
           If InState = 'TX';
 ...
(To beginning of article)

Embrace ILE

The second RPG IV style commandment is, "RPG IV and ILE are inextricably joined together and must never be torn asunder."

I silently groan whenever I hear an RPG programmer (or worse, a manager or consultant) tell me, "I use RPG IV, but I don't do any of that binding stuff." The RPG IV syntax, along with the Integrated Language Environment (ILE), encourages a modular approach to application programming. Modularity offers a way to organize an application, facilitate program maintenance, hide complex logic, and efficiently reuse code wherever it applies.

Isolate reusable code in a procedure. Instead of monolithic, all-purpose monsters, write smaller, single-function compile units and bind them to the programs that need them. Eliminate duplicate code, even little bits of it; your coding mantra should be, "Once and only once."

Relegate mysterious code to a well-documented, well-named procedure. Despite your best efforts, on extremely rare occasions you simply will not be able to make the meaning of a chunk of code clear without extensive comments. By separating such heavily documented, well-tested code into a procedure, you'll save future maintenance programmers the trouble of deciphering and dealing with the code unnecessarily.

Use binding directories consistently. A binding directory is an object that can help organize the "pieces" required to create a program. Using a binding directory to list often-reused modules or service programs will prevent tedium and errors by allowing you to refer to the binding directory or directories instead of explicitly listing the required components when you bind a program with the CRTPGM (Create Program) or CRTSRVPGM (Create Service Program) command. To use binding directories effectively, you need a consistent strategy for them. Perhaps the most useful strategy would be to have a generic binding directory that refers to reusable code that crosses applications, along with an application specific binding directory for that code that relates only to one application.

Package often-reused procedures in service programs. The service program is an elegant means of reusing procedures without physically copying them into each program that needs them. As a general rule of thumb, if a procedure will appear in more than just one or two programs, you should package that procedure in a service program.

Use binder language to control a service program signature. It's tempting to let the binder handle the service program's signature by specifying EXPORT(*ALL) on the CRTSRVPGM command. But that practice will lead to maintenance nightmares in the long run. Binder language source will let you explicitly control the signature of a service program, and more importantly, will allow the service program to maintain multiple signatures. This feature means that you can make dramatic changes to a service program - adding, changing, and removing procedures - without ever touching the programs that use the service program.

Use RPG IV's prototyping capabilities to define parameters and procedure interfaces. Prototypes (PR definitions) and procedure interfaces (PI definitions) offer many advantages when you're passing data between modules and programs. For example, they avoid runtime errors by giving the compiler the ability to check the data type and number of parameters. Prototypes also let you code literals and expressions as parameters, declare parameter lists (even the *ENTRY PLIST) in the D-specs, and pass parameters by value and by read-only reference, as well as by reference.

Store prototypes in /COPY members. For each module, code a /COPY member containing the procedure prototype for each exported procedure in that module. Then include a reference to that /COPY module in each module that refers to the procedures in the called module. This practice saves you from typing the prototypes each time you need them and reduces errors. As an alternative to having many small /COPY members, consider having a master /COPY member that contains prototypes for all or most of your reusable procedures.

You might also explore RPG's conditional compilation directives that will let you use the prototype from the actual procedure source instead of using a universal /COPY member. This technique would avoid introducing unused prototypes into the compile process (although there's no runtime penalty for having unused prototypes during the compile).

Include constant declarations for a module in the same /COPY member as the prototypes for that module. If you then reference the /COPY member in any module that refers to the called module, you've effectively "globalized" the declaration of those constants.

Use IMPORT and EXPORT only for global data items. The IMPORT and EXPORT keywords let you share data among the procedures in a program without explicitly passing the data as parameters. In other words, they provide a "hidden interface" between procedures. Limit use of these keywords to data items that are truly global in the program - usually values that are set once and then never changed.

(To beginning of article)

Centralize Declarations

With RPG IV, we finally have an area of the program source in which to declare all variables and constants associated with the program. Use the D-specs to organize all your declarations in one place. Within the D-specs, organize your definitions in a predictable order, by definition type:
  • Prototype for main procedure
  • Procedure interface for main procedure
  • Other prototype definitions
  • Named constants
  • Data structures
  • Standalone variables
Within each definition type, alphabetize the declarations.

Instead of using literals, declare them as named constants. This practice helps document your code and makes it easier to maintain. One obvious exception to this rule is the allowable use of 0 and 1 when they make perfect sense in the context of a statement. For example, if you're going to initialize an accumulator field or increment a counter, it's fine to use a hard-coded 0 or 1 in the source.

Indent data item names to improve readability and document data structures. Unlike many other RPG entries, the name of a defined item need not be left-justified in the D-specs. Take advantage of this feature to help document your code:
D ErrMsgDS        DS
D   ErrPrefix                    3
D   ErrMsgID                     4
D   ErrMajor                     2    OVERLAY(ErrMsgID:1)
D   ErrMinor                     2    OVERLAY(ErrMsgID:3)
To improve readability, always leave column 7 blank. (The same rule applies to H-specifications - leave column 7 blank.)

Use length notation instead of positional notation in data structure declarations. D-specs let you code fields either with specific from and to positions or simply with the length of the field. To avoid confusion and to better document the field, use length notation consistently. For example, code
D RtnCode         DS
D  PackedNbr                    15P 5
instead of
D RtnCode         DS
D  PackedNbr              1      8P 5
Use positional notation only when the actual position in a data structure is important. For example, when coding the program status data structure, the file information data structure, or the return data structure from an API, you'd use positional notation if your program ignored certain positions leading up to a field or between fields. Using positional notation is preferable to using unnecessary "filler" variables with length notation. Even with positional notation, though, consider overlaying the positionally declared variable with another variable that is declared with length notation to better document the variable:
D APIRtn          DS
D  Pos145               145    152
D   PackNbr                     15P 5 OVERLAY(Pos145)
When defining overlapping fields, use the OVERLAY keyword instead of positional notation. Keyword OVERLAY explicitly ties the declaration of a "child" variable to that of its "parent." Not only does OVERLAY document this relationship, but if the parent moves elsewhere within the program code, the child will follow.

Avoid compile-time arrays. As you decompose a program into its individual procedures, it helps to have all related pieces of code physically and logically self-contained. The traditional compile time array coding separates the array definition from the array data by perhaps thousands of code lines in the program. A better solution would be to code the array inside a data structure:
D DaysCTData    DS
D                           9    Inz('Sunday')
D                           9    Inz('Monday')
D                           9    Inz('Tuesday')
D                           9    Inz('Wednesday')
D                           9    Inz('Thursday')
D                           9    Inz('Friday')
D                           9    Inz('Saturday')
D   Days                    9    Dim(7)
D                                Overlay(DaysCTData)
Avoid multiple occurrence data structures. V5R2's array data structure is a better construct because it uses standard array notation in your program; it doesn't limit you to processing a single occurrence in the same line; and it allows you to deal with those instances where nested (multiple dimension) arrays are useful.

Use QUALIFIED data structures. Qualified data structures force you to qualify their subfield names with the name of the data structure (e.g., Customer.Name, Customer.Address). This feature not only provides good documentation for the data item, telling you where a data item originates, but it also allows you to have identically named subfields in different data structures (e.g., Before.Name, After.Name). To define a qualified data structure, use the QUALIFIED keyword in its definition.

The LIKEDS and LIKEREC (V5R2) keywords are also useful when a data structure "inherits" its subfields from another data structure or a record format. Also, at V5R2, you can control the subfields that will appear in a LIKEREC data structure or an externally described data structure, specifying *ALL, *INPUT, *OUPUT, or *KEY fields.

(To beginning of article)

Expand Naming Conventions

Perhaps the most important aspect of programming style deals with the names you give to data items (e.g., variables, named constants) and routines. Establish naming conventions that go beyond the traditional six characters, to fully identify variables and other identifiers. Those extra characters can make the difference between program "code" and a program "description."

When naming an item, be sure the name fully and accurately describes the item. The name should be unambiguous, easy to read, and obvious. Although you should exploit RPG IV's allowance for long names, don't make your names too long to be useful. Name lengths of 10 to 14 characters are usually sufficient, and longer names may not be practical in many specifications.

When naming a data item, describe the item. When naming a procedure with a return value, name the procedure after the return value; or use a "get/set" naming convention if the procedure retrieves or assigns a data value.

For subroutines or procedures without a return value, use a verb/object syntax (similar to a CL command) to describe the process. Maintain a dictionary of names, verbs, and objects, and use the dictionary to standardize your naming conventions.

When coding an RPG symbolic name, use mixed case to clarify the named item's meaning and use. RPG IV lets you type your source code in upper and lowercase characters. Use this feature to clarify named data. For RPG-reserved words and operations, consider using all uppercase characters.

Avoid using special characters (e.g., @, #, $) when naming items. Some of these characters may cause compile errors in some character sets. Although RPG IV allows an underscore (_) within a name, you can easily avoid using this "noise" character if you use mixed case intelligently.

(To beginning of article)

Write Indicatorless Code

Historically, indicators have been an identifying characteristic of the RPG syntax, but with RPG IV they are fast becoming relics of an earlier era. Reducing a program's use of indicators may well be the single most important thing you can do to improve the program's readability.

Eliminate numbered indicators from your RPG source. RPG IV completely eliminates the need for conditioning indicators and resulting indicators and does not support them in free-form specifications. The indicator data structure (INDDS keyword) and a number of built-in functions (BIFs) render obsolete the predefined numbered indicators. Remember that you can indicate file exception conditions with error-handling BIFs (e.g., %EOF, %ERROR, %FOUND) and an E operation extender to avoid using indicators.

If you must use indicators, name them. RPG IV supports a Boolean data type (N) that serves the same purpose as an indicator. You can use the INDDS keyword with a display-file specification to associate a data structure with the indicators for a display or printer file; you can then assign meaningful names to the indicators.

Include a description of any indicators you use. Even after you eliminate numbered indicators, a handful of predefined indicators may remain (e.g., L0-L9 level break indicators, or the U1-U8 external indicators). It's especially important to document these indicators, because their purpose isn't usually obvious by reading the program. The preface is a good place to list them.

(To beginning of article)

Use Structured Programming Techniques

Give those who follow you a fighting chance to understand how your program works by implementing structured programming techniques at all times. The so-called "structured" operation codes - IF, DOU, DOW, and WHEN - support free-format expressions, making them more readable than their fixed format ancestors. In general, if an opcode offers a free-format alternative, use it. This rule applies to the DO opcode as well; the free-format FOR operation is usually a better choice. By the way, you'd never use indicators to condition structured opcodes, would you? Good!

Don't use GOTO, CABxx, or COMP. Instead, substitute either a structured alternative, such as nested IF statements, or status variables to skip code or to direct a program to a specific location. To perform loops, use DOU, DOW, and FOR. Never code your loops by comparing and branching with COMP (or even IF) and GOTO. Employ ITER to repeat a loop iteration, and use LEAVE or LEAVESR for premature exits from loops or subroutines, respectively.

Perform multipath comparisons with SELECT/WHEN/ OTHER/ENDSL. Deeply nested IFxx/ELSE/ENDIF code blocks are hard to read and result in an unwieldy accumulation of ENDIFs at the end of the group. ELSEIF improves the situation somewhat, but SELECT/WHEN/OTHER is usually a better and more versatile construct. The same advice goes for the obsolete CASxx opcode; use SELECT/WHEN/OTHER instead.

(To beginning of article)

Manipulate Character Strings

IBM has greatly enhanced RPG IV's ability to easily manipulate character strings. Many of the tricks you had to use with earlier versions of RPG are now obsolete. Modernize your source by exploiting these new features.

Use a named constant to declare a string constant instead of storing it in an array or table. Declaring a string (such as a CL command string) as a named constant lets you refer to it directly instead of forcing you to refer to the string through its array name and index. Use a named constant to declare any value that you don't expect to change during program execution.

Avoid using arrays and data structures to manipulate character strings and text. Use the new string manipulation opcodes and/or BIFs instead.

Use EVAL's free-form assignment expressions whenever possible for string manipulation. When used with character strings, EVAL is usually equivalent to a MOVEL(P) opcode; EVALR is equivalent to MOVE(P). When you don't want the result to be padded with blanks, use %SUBST or %REPLACE functions.

Use variable-length fields to simplify string handling. Use variable-length fields as CONST or VALUE parameters to every string-handling subprocedure, as well as for work fields. Not only does the code look better (eliminating the %TRIM function, for example), but it's also faster than using fixed-length fields. For example, use this code:
D QualName        S             33    VARYING
D Library         S             10    VARYING
D File            S             10    VARYING
D Member          S             10    VARYING

 /FREE
    QualName = Library + '/' + File + '(' + Member + ')';
 /END-FREE
instead of this:
D QualName        S             33
D Library         S             10
D File            S             10
D Member          S             10

 /FREE
    QualName = %TRIM(Library) + '/' + %TRIM(File)
               + '(' + %TRIM(Member) + ')';
 /END-FREE
(To beginning of article)

Use Comments Judiciously

Good programming style can serve a documentary purpose in helping others understand your source code. If you practice good code construction techniques, you'll find that "less is more" when it comes to commenting the source. Too many comments are as bad as too few. Here are some specific commenting guidelines.

Use // comments exclusively. RPG IV now uses comments that begin with double slash characters (//) instead of the traditional asterisk (*) in column 7. In free format code, which does not allow the asterisk format, the comment can begin anywhere from columns 8-80, and can even be on the same line with existing executable statements.

The new comment form can also replace asterisk comments in fixed format specifications, but the comment must be on a line all by itself. For the sake of consistency, use the new form exclusively, even in fixed format specifications:
  //  Prototypes
D DayofWeek       PR             1  0
D  VarDate                        D

  //  Standalone variables
D DayNbr          S              5  0

 /Free
  // Calculate total pay for employee
  Chain(ne) EmployeeID Employees;
  If %Found;               // If employee active, calculate total pay
      Eval(h) TotalPay = (RegHours * Rate) + (OvtHours * Rate * 1.5);
  Endif;
 /End-free
Use comments to clarify - not echo - your code. Comments that merely repeat the code add to a program's bulk but not to its value. In general, you should use comments for just three purposes:
  • to provide a brief program or procedure summary
  • to give a title to a subroutine, procedure, or other section of code
  • to explain a technique that isn't readily apparent by reading the source
Always include a brief summary at the beginning of a program or procedure. This prologue should include the following information:
  • the program or procedure title
  • a brief description of the program's or procedure's purpose
  • a chronology of changes that includes the date, programmer name, and purpose of each change
  • a summary of indicator usage
  • a description of the procedure interface (the return value and parameters)
  • an example of how to call the procedure
Use consistent "marker line" comments to divide major sections of code. For example, you should definitely section off with lines of dashes (-) the declarations, the main procedure, each subroutine, and any subprocedures. Identify each section for easy reference.

Use blank lines to group related source lines and make them stand out. In general, you should use completely blank lines instead of blank comment lines to group lines of code, unless you're building a block of comments. Use only one blank line, though; multiple consecutive blank lines make your program hard to read.

Avoid right-hand "end-line" comments in columns 81-100. Right-hand comments tend simply to echo the code, can be lost during program maintenance, and can easily become "out of synch" with the line they comment. Especially now that comments can be inline with code, don't use right-hand comments.

(To beginning of article)

Avoid Obsolescence

RPG is an old language. After nearly 40 years, many of its original, obsolete features are still available. Don't use them.

Eliminate obsolete operation codes. How do you identify an obsolete opcode? Simple answer: If the free-form specification doesn't support it, the operation code is obsolete. In introducing the free-form specification, IBM took advantage of the opportunity to simplify RPG by paring the number of supported operation codes in half to about 60. These tables summarize the preferred substitutions for those opcodes that you shouldn't use any more.

Arithmetic Operations
ADD Use + operator
DIV Use / operator or %DIV
MULT Use * operator
MVR Use %REM
SQRT Use %SQRT
SUB Use – operator
XFOOT Use %XFOOT
Z-ADD Use EVAL
Z-SUB Use EVAL

String Operations
CAT Use + operator
CHECK Use %CHECK
CHECKR Use %CHECKR
SCAN Use %SCAN
SUBST Use %SUBST
XLATE Use %XLATE

Assignment Operations
MOVE Use EVALR, %SUBST, conversion functions
MOVEA Use %SUBST, %SUBARR (V5R3), conversion functions
MOVEL Use %SUBST, conversion functions
SETOFF Use assignment expressions
SETON Use assignment expressions

Conditional Operations
ANDxx Use AND
CABxx Use logical expressions
CASxx Use SELECT/WHEN/OTHER
COMP Use logical expressions
DO Use FOR
DOUxx Use DOU
DOWxx Use DOW
IFxx Use IF
ORxx Use OR
WHENxx Use WHEN

Call Operations
CALL Use CALLP
CALLB Use CALLP
PARM Use PR/PI definitions
PLIST Use PR/PI definitions

Date Operations
ADDDUR Use + operator, date functions
EXTRCT Use %SUBDT
SUBDUR Use – operator, %DIFF, date functions
TIME Use %DATE, %TIME, %TIMESTAMP

Definition/Allocation/Array Operations
ALLOC Use %ALLOC
DEFINE Use D-specs with LIKE
KFLD Use %KDS data structure (V5R2)
KLIST Use %KDS data structure (V5R2)
LOOKUP Use %LOOKUP or %TLOOKUP
OCCUR Use array data structure (V5R2)
REALLOC Use %REALLOC
SHTDN Use %SHTDN

Bit Operations
BITOFF Use %BITAND, %BITNOT (V5R2)
BITON Use %BITOR (V5R2)
MHHZO Use %BITAND, %BITOR (V5R2)

MHLZO

Use %BITAND, %BITOR (V5R2)
MLHZO Use %BITAND, %BITOR (V5R2)
MLLZO Use %BITAND, %BITOR (V5R2)
TESTB Use %BITAND (V5R2)
TESTN Use MONITOR, ON-ERROR
TESTZ Use %BITAND (V5R2)

Branch Operations
GOTO No alternative
TAG No alternative

If a function offers the same function as an opcode, use the function instead of the opcode. With some opcodes, you can substitute a BIF for the opcode and use the function within an expression. The functions are preferable if they offer the same capability as the opcodes.

Avoid program-described files. Instead, use externally defined files whenever possible.

Use native date data types to operate on dates. Get rid of the clever date and time routines that you have gathered and jealously guarded over the years. The RPG IV date-related BIFs are more efficient, clearer, and more modern. Even if your database includes dates in "legacy" formats, you can use the date functions (e.g., %DATE, %DIFF, %SUBDT, %DAYS) to manipulate them.

Don't sequence program line numbers in columns 1-5. Chances are you'll never again drop that deck of punched cards, so the program sequence area is unnecessary. In RPG IV, these columns are commentary only. You may use them to identify changed lines in a program or structured indentation levels, but be aware that these columns may be subject to the same hazards as right-hand comments.

(To beginning of article)

Miscellaneous Guidelines

Here's an assortment of other style guidelines that can help you improve your RPG IV code.

Avoid programming tricks. Such maneuvers aren't so clever to someone who doesn't know the trick. If you think you must add comments to explain how a block of code works, consider rewriting the code to clarify its purpose, or hiding the complexity of the code in a procedure. Use of the obscure "bit-twiddling" opcodes (BITON, BITOFF, MxxZO, TESTB, and TESTZ) may be a sign that your source needs updating.

In all specifications that support keywords, observe a one-keyword-per-line limit. Instead of spreading multiple keywords and values across the entire specification, your program will be easier to read and let you more easily add or delete specifications if you limit each line to one keyword, or at least to closely related keywords (e.g., DATFMT and TIMFMT).

Begin all H-spec keywords in column 8, leaving column 7 blank. Separating the keyword from the required H in column 6 improves readability.

(To beginning of article)

Final Advice

Sometimes good style and efficient runtime performance don't mix. Wherever you face a conflict between the two, choose good style. Hard-to-read programs are hard to debug, hard to maintain, and hard to get right. Program correctness must always win out over speed. Keep in mind these admonitions from Brian Kernighan and P.J. Plauger's The Elements of Programming Style (McGraw-Hill; 2nd edition, 1978):
  • Make it right before you make it faster.
  • Keep it right when you make it faster.
  • Make it clear before you make it faster.
  • Don't sacrifice clarity for small gains in efficiency.

(c) Copyright 2005, Bryan Meyers
(To beginning of article)


It is your responsibility to ensure procedures, techniques, and code used from this website are accurate and appropriate for your installation. No warranty or support is implied or expressed.
E-mail to Bryan Meyers
Privacy and Email Policy
 
AS/400®, eServer, i5/OS, IBM i, Integrated Language Environment®, iSeries, OS/400®, RPG/400®, System i5, VisualAge®, and WebSphere® are trademarks of IBM Corporation. Note: IBM® System i is the latest member of the family of eServer iSeries. This site might refer to System i as System i5, iSeries, or AS/400.
 

If you are having trouble navigating the menu at the top of this page, you may use the following scrollable site map instead:
 
Powered by Google
enskill.com iSeries Training Onsite classes
DVD training
Books
Online e-Learning
Home
Resources
Contact
(c)2002-2008
Self-paced DVD Training
DVD: RPG IV for RPG Programmers
DVD: RPG IV Modules, Procedures, and Service Programs
 
In association with Amazon.com
Programming in RPG IV
RPG IV Jump Start
Power Tips for RPG IV
Control Language Programming for the AS/400
VisualAge for RPG by Example
 
International visitors: click hereInternational visitors:
Click here!