technical: Exits: My Five Best Practices
Over the past few months I've been working with a client on an exit: a hook we can use to tailor processing to our needs. This particular user had a problem: they'd lost the source code to the exit. They had some older source, but it didn't match what was running. So, we've been trying to recreate the current processing, and do some other fixes so it works with the latest software.
This is not the first time I've seen exit source code lost. Nor is this the first time I've seen clients get into trouble with exits. I've seen exits that are slow, use too much CPU, don't quite work the way they're supposed to, or cause problems when something goes wrong. There are ways to avoid these problems, including following what I consider to be best practices for implementing and maintaining exits. In this article, I'm going to talk about my five most important practices when using exits.
1. Don't Write Exits
Yep, the very first rule is to avoid exits unless we absolutely need them. Exits must be developed, tested, and then continually managed and maintained. Often, they are written in Assembler, and may use system services and other features not needed by (or available to) normal programs. The consequences of an error or a crash in an exit can be severe.
When I first started as a systems programmer, exits were the norm. z/OS (well, MVS in those days) just didn't do everything we wanted. So, we wrote exits.
Today, exits may still be required, but they're less essential than they were. So, if we're thinking about writing an exit, a good first step is to look for other solutions, and use an exit as a last choice.
Let's take an example: memory limits. Many sites want to limit or control how much memory address spaces can use: the REGION, REGIONX or MEMLIMIT JCL parameters. Today, most use the SMF IEFUSI exit to do this.
But this is not the only way. Let's look at some options:
- We can set a default memory for TSO users using the JES2 TSUCLASS initialization statement. We can also set a REGION or MEMLIMIT parameter in the TSO logon procedure. RACF also provides features to limit the REGION size for a TSO user.
- The sys1.parmlib member SMFPRMxx has a MEMLIMIT parameter that can be used to specify a default MEMLIMIT for all jobs (it defaults to 2GBytes).
- If we have some software that manages the JCL parameters a user can use, then we could use this to police any REGION, REGIONX or MEMLIMIT parameters specified. For example, Compuware Thruput Manager can do this.
- A new sys1.parmlib member called SMFLIMxx has recently been available that can be used to define these limits.
This last option is interesting. A new sys1.parmlib member is now available that eliminates the need for IEFUSI exit to limit the storage requested by different address spaces. An excellent example of why exits should be reviewed regularly (more on this shortly).
If we do need an exit, we could try to use samples provided unchanged. These samples are usually maintained by the vendor: no need for us to maintain the code. All we have to do is compile or assemble and install the exit. For example, IBM supply sample IEFACTRT source in SYS1.SAMPLIB that many sites use unchanged.
In some cases, we may be lucky to go one step further, and not create an exit module at all. For example, CICS uses a user exit when it is dynamically routing transactions to other CICS regions: a dynamic transaction routing exit. CICS also supplies a sample exit that will do this for us, working with CPSM. Even better, it supplies this sample exit as a load module. So, we don't have to code the exit at all: just use the module.
2. Manage the Source
Most sites I see now use SCM software like CA Endevor or IBM SCLM to manage their source code. But I'm regularly surprised by sites that don't have the source for their exits in this SCM. Often this code has been written by systems programmers who don't follow the normal procedures for source code management.
And this is amazing. An error in an application program can be bad: affecting processing or data. But an error in an exit can wipe out an entire system.
More interestingly, exits often need to be reviewed and recompiled. We talk more about this later.
So, it makes sense that exit source should be better managed, not worse.
Bottom line: use some code of source control software for exit code and compile/assemble jobs. We talk about using SMP/E as this source control software in our partner article.
3. Review Regularly
Exits are closely tied in with the systems that provide the exit. For example, z/OS exits are closely tied with z/OS.
So, z/OS exits should be reviewed with every new z/OS release. Similarly, CICS exits should be reviewed with every new CICS release. Control block mapping macros may have changed, the information passed to the exit may have changed or there may be other release-dependent issues. In some cases, as we've seen above for the IEFUSI exit, the exit may no longer be relevant.
In some cases, I'd go as far as to suggest that exits be recompiled or assembled every time the software providing the exit changes.
Bottom line, review the exit every time the software that provides the exit point is modified.
4. Bullet-Proof, or At Least Bullet Resistant
In many cases exits get control as authorized. Or in other words: they are very powerful. But with this power comes responsibility: an authorized program can do a lot of damage if it crashes or does something it shouldn't.
This is why exits should have rock-solid error handling and recovery. Think ESTAEs and FRRs for Assembler exits, or other error handling code. Think messages to the console if things are unexpected (but be careful how many messages). And think of handling situations where things aren't what is expected.
What's more, the exit should be rigorously tested, and tested, and tested. ALL error handling logic should be tested.
5. Don't Forget About Performance
Exits can be called a lot of times. And the performance impact of exits can be hidden: particularly if they're operating deep into the operating system.
So, performance issues with exits can be critical. Let's take our IEFUSI exit we talked about above. This is called every time a new address space starts: every new job, TSO user, UNIX logon or started task. So it's called a lot. For z/OS assembler exits, I actually get a little obsessed with performance, looking for instructions or ways to make it even a little more efficient.
Exits can be great. They allow us to tailor software to do exactly what we want. However, exits come at a cost, and can cause a lot of damage if not coded correctly or carefully. What's more, exit source may not be managed as well as other application source, or even lost. So, care, attention and thorough management of exits is essential.