Tuesday, June 23, 2015

RPG Service Programs Best Practices

At my current client, I have been doing a lot of work using ILE and service programs. I have decided to start compiling a list of best practices that I have found work out well. Many of these are not my ideas, but have been gleaned from the discussions at midrange.com. This post will not function so much like a blog post, but more as a wiki page. I will return to update it as I have time, or learn new things.
Create a new binding directory for each Service Program
This service program specific binding directory will contain only the modules and service programs necessary to build this one service program. When you first create a service program, it will not be readily apparent that you need to do this. However, if you intend to maintain your service program, having a separate binding directory will allow you to add procedures without duplicate symbol errors. Consider an example of just a single common binding directory that contains all your service programs. You can (and should) use a common binding directory to build all the programs in your application, but if you try to use this binding directory to build your service programs you will find that both the existing service program (which is in your common binding directory), and the module you are using to build it have mostly the same exports which will produce duplicate symbol errors. You need a binding directory that does not contain the service program you are building to avoid these errors. Or you need to ignore them in some way. Save yourself the headache of trying to determine which errors you can safely ignore, and just avoid them all. Always create a service program specific binding directory for each service program in your application, even if it is empty. And name it the same as your service program, your builds will run far smoother for it.
 
Create a binding source with a single fixed signature for each Service Program
This is more about the signature than anything else since the binding source if you use it, must be specific to your service program. Name your binding source the same as your service program, and store it in QSRVSRC. Create a signature, and never change it. This has a few implications:
  1. You cannot reorder the exports in your binding source. Ever, unless you want to recompile the world!
  2. You cannot change the parameters in your procedures. Without taking extreme care, unless you want to recompile the world!
These are both true whether you use a fixed signature or generated signatures, so you might as well choose the simplicity of fixed signatures. An easy way to create a good signature (it is really an arbitrary string) is to create your service program once using EXPORT(*ALL), then use RTVBNDSRC to capture the signature and exports. Now all you have to do is add new exports to the end of your binder source. It is completely safe unless some knucklehead notices that the last few exports are not in alphabetical order and fixes that for you (See point 1 above).
 
Create a prototype file that contains only exported procedure prototypes
This file is named the same as the service program, and is stored in QPROTOSRC. While you can have procedures that are not exported from your service program, and in a multi-module service program, you may need prototypes for those procedures, keep those separate from the public exports. There is no need to show prototypes that can't be called from programs. Procedures exported from modules, but not exported from the service program can be stored in a separate source file, I suggest naming that the same as the service program with an underscore suffix.
While I wouldn't call it a best practice, I use a program I wrote to build my service programs It reinforces the rules above, and allows me to use multiple modules per service program without having to remember the names of those modules.