What keeps you from being as productive as you need to be? Illiterate users who don't follow instructions? Executives who should have hired a magician instead of a programmer? Phone calls from salesmen who promise you a free beer mug, then don't send it if you don't place an order with them?
While I can blame lack of productivity on other people, the primary reason I'm not as productive as I should be is that my work habits are not what they should be. For instance, I have been guilty on more than one occasion of solving a problem that I or someone else had already solved. Since each new day has challenges enough of its own, there's no reason I should be approaching the task of writing a file maintenance program as if it were a problem never before faced by mankind.
Programmers need to reuse code. We need to reuse object code, and we need to reuse source code. I'm not talking about spending twenty minutes trying to find that general ledger program (or was it an accounts payable program?) I wrote eight or ten months ago (or was it a year and a half?) so I can haphazardly paste bits and pieces of subfile wizardry into my current project. I mean developing a disciplined approach to building applications.
Reusing object code is preferable to reusing source code, but in the world of business data processing, reusing source code is more practical. Most programs involve files, which include record formats and field names. These things vary from program to program. It would be impractical, if not impossible, to write one generic file maintenance program that would permit modification to any file in the system. It makes more sense to produce one well-written bug-free program that you can clone and modify to make other file maintenance programs.
There's more to reusing source code than pressing the F15 key while editing with SEU. That's what I want to explore in the following paragraphs. I'll talk about reusing object code in a future article.
We should reuse code to be more productive. Most programs needed by AS/400 installations are variations on old themes. A new system requires file maintenance programs, data entry programs, inquiry programs, report programs with control breaks, communications programs, and so on. If we begin a new system from scratch, we redo much of the work that has already been done in other systems. If, instead, we begin with a library of debugged source members that we modify and enhance as needed, we can have an operational system sooner.
Another good reason to reuse code is to improve the quality and consistency of our code. Instead of a dozen programmers inventing a dozen ways to center text within a field, let's invent one good way and let everybody in the shop use it.
The general method for reusing code might be called "copy, paste, and edit." While editing one source member, we view and copy all or part of a second source member. Nothing is wrong with this method, but it breaks down at the "edit" point. Once we start deleting a line here, adding a line there, changing the names of variables, and otherwise cannibalizing working code, what we end up with is not a copy of the original. The code we started with was bug-free in the source member from which we copied it, but that doesn't mean it ended up bug-free in the source member we copied it to.
Another way programmers reuse source code is through compiler directives, such as the /COPY directive of RPG and Auto Report, and the COPY verb of COBOL. This can be a tremendous boon to productivity, as anyone who's familiar with C's preprocessor directives can testify.
/COPY is a good way to reuse sections of code that are widely used throughout a system. For example, suppose your shop has the convention that the current time appears in U.S. format (hh:mm AM) in page headings of reports. You could write an appropriate routine, like the one in 1, and include a /COPY directive in the *INZSR subroutine of all the programs that need it. If your shop standard changes or if you find a bug in the time routine, you can change the routine and recompile the programs that /COPY it.
/COPY is a good way to reuse sections of code that are widely used throughout a system. For example, suppose your shop has the convention that the current time appears in U.S. format (hh:mm AM) in page headings of reports. You could write an appropriate routine, like the one in Figure 1, and include a /COPY directive in the *INZSR subroutine of all the programs that need it. If your shop standard changes or if you find a bug in the time routine, you can change the routine and recompile the programs that /COPY it.
/COPY is also handy for defining data structures. 2 shows the type of /COPY member you might build to define a plant information data area in a data structure. If you later add another field to the plant data area, you can change the copy member and recompile programs as necessary. You can define any data structure, including local data area, print control, file control, and program status data structures. If you'd like to see an example of the source you'd use to create a copy member for the program status data structure, see "Error Recovery in RPG Programs" (MC, November 1994).
/COPY is also handy for defining data structures. Figure 2 shows the type of /COPY member you might build to define a plant information data area in a data structure. If you later add another field to the plant data area, you can change the copy member and recompile programs as necessary. You can define any data structure, including local data area, print control, file control, and program status data structures. If you'd like to see an example of the source you'd use to create a copy member for the program status data structure, see "Error Recovery in RPG Programs" (MC, November 1994).
This technique is of limited value in most AS/400 shops, however. For one thing, RPG's /COPY has a take-it-or-leave-it attitude. It copies a source member exactly as it is, no substitutions allowed. If the code contains something that differs from program to program, such as a file name, /COPY will not do the job. The Auto Report /COPY will make limited substitutions to F and I specs, but that's unnecessary if you use externally described files. COBOL's COPY verb allows substitutions, but most midrange shops don't use COBOL. Another problem is that there is no /COPY facility for DDS or CL, although there is a copy facility in User Interface Manager (UIM). So, we fall back on "copy, paste, edit, and debug."
Plenty of products on the market address the deficiencies in these methods. Program generators, macro preprocessors, enhanced editors, and CASE products all work with repositories of reusable source code, called templates, into which they make substitutions. The end result is bug-free, compilable source code. These products are good, but they are not in widespread use in AS/400 shops for several reasons. First, the price of many of these products is more than many shops are willing to pay. Second, some products have a steep learning curve that programmers must surmount before becoming productive. Third, the repository of templates is limited. Fourth, the generated code of some products cannot be manually modified (or at least not easily) to handle the deficiencies of the templates.
It is possible to reuse source code successfully with nothing more than SEU, because the successful reuse of code depends more on attitude and discipline than on anything else.
To successfully reuse source code, data processing shops should commit themselves as a whole to the reuse of code. That is, the entire shop, not just individual programmers, must adopt a philosophy of code reuse.
Programmers should cooperate to develop templates of debugged code that reflect the standards of their organizations. All programmers in a shop should use the same templates. This can be a problem, as programmers often disagree on the best way to write code. Egotism won't help our employers survive in the marketplace, however. Using the same templates ensures that programs have the same look and feel, regardless of who wrote them. Users get frustrated when one program works one way, but another program of the same type works another way. Using common templates also makes it easier for programmers to work on one another's programs.
Templates should be organized so that they're easy to find. It's good to put all template code into a library of its own. The source files in the template library can be the IBM standard ones (such as QRPGSRC or QCLSRC) or they can be named according to some other shop standard. The members within the source files need to follow some naming convention so that they are easy to find, if not remember. I like to give related members similar names. If a file maintenance RPG template is MAINTRG, the related display file might be MAINTDF, and the driver CL could be MAINTCL. Whatever convention, every person in the shop should know it and adhere to it.
Programmers should not fall prey to the NIH ("not invented here") syndrome. The work of other programmers is published monthly in Midrange Computing. Programmers can pick the brains of programmers from other shops at user group meetings or through BBSs and the Internet. Be on the lookout for code that you can use. Whatever the problem, someone else has probably solved it already.
There should be enough templates to handle the common tasks of the shop, yet not so many that templates are hard to keep up with. In other words, don't fill up the template library with junk.
A template should accomplish only one task, and it should do it well. A task could be an entire program. For example, you might have DDS and RPG source code templates for file maintenance jobs. Or a task might be an RPG subroutine that builds a load-all subfile. Or it could be a few lines of CL that right-adjust one character variable into another.
Templates must work correctly after being inserted into new programs. You'll have to change variable names, record format names, and such, but avoid changing the logic of the routine. A good way to accomplish this is to load template code with tokens that won't compile. (If you use RPG, you must copy such routines in manually, since /COPY won't let you make substitutions into the copied code.) 3 contains a routine to clear a subfile. The standard in the shop that produced this template is that SFLDSP and SFLEND are conditioned to one indicator, SFLDSPCTL is conditioned to another indicator, and SFLCLR is conditioned to both indicators being off. (See the portion of the display file and RPG templates in3 and 4.) The tokens &1, &2,
Templates must work correctly after being inserted into new programs. You'll have to change variable names, record format names, and such, but avoid changing the logic of the routine. A good way to accomplish this is to load template code with tokens that won't compile. (If you use RPG, you must copy such routines in manually, since /COPY won't let you make substitutions into the copied code.) Figure 3 contains a routine to clear a subfile. The standard in the shop that produced this template is that SFLDSP and SFLEND are conditioned to one indicator, SFLDSPCTL is conditioned to another indicator, and SFLCLR is conditioned to both indicators being off. (See the portion of the display file and RPG templates in Figures 3 and 4.) The tokens &1, &2,
Management should consider the reuse of code when evaluating the productivity and performance of a programmer. If a programmer writes a twenty-line program that includes six /COPY statements, he should be evaluated as if he wrote the copied code from scratch. Not only does this encourage programmers to reuse code, but it can be great for morale. Imagine going home at the end of a day having produced eleven programs consisting of 9,500 lines of bug-free code.
A good way to start a collection of templates is to select some typical jobs in your shop that work the way you want them to. Select a file maintenance program, for example, make sure it's fully debugged, and convert it into a template for future file maintenance programs. Once code reuse becomes the norm in your shop, consider investing in a CASE product, a program generator, or an intelligent source code editor. If management won't spring for them, put in some extra hours writing an application that generates code by reading a template file and replacing tokens with actual values you fill in at run-time. Then you'll have your own program generator.
I encourage you to read the chapter entitled "Software Reusability" in Edward Yourdon's book, Decline and Fall of the American Programmer. Yourdon covers much of what I've talked about and more-and all of it in greater detail.
Let me offer a few more reasons why I think this topic is important.
We should reuse code because life is too short not to. It's not good to spend extra hours at the office when we should be living life with our families and friends. Life is not about spending time with machines, but about spending time with people.
We should reuse code to help our employers stay in business. Every hour spent working on yet another file maintenance program is an hour we can't spend implementing barcoding or electronic data interchange (EDI). Reusing code can help us develop the new systems needed to remain competitive.
We should reuse code for our own mental health. Programmers generally enjoy tackling something new. Writing yet another batch posting program is a task we shove off on the junior programmers (if there are any.) If we get in the habit of solving a problem once and reusing that solution as needed, we'll have time to tackle those challenging projects we enjoy. Maybe a little more enjoyment at work will get rid of some of that stress that's killing so many of us.
We can't do anything about some things; they're just part of the job. But we do have control over other things, especially our work habits. If we are disciplined in good work habits, such as reusing source code, we will be more productive.
Ted Holt is an associate technical editor for Midrange Computing. He can be reached by E-mail at
REFERENCE
Yourdon, Edward. Decline and Fall of the American Programmer. Englewood Cliffs, New Jersey: Prentice Hall, 1993.
Reusing Source Code
Figure 1: /COPY Member to Retrieve Current Time
*. 1 .. + .. 2 .. + .. 3 .. + .. 4 .. + .. 5 .. + .. 6 .. + .. 7 .. * Build current time in USA format in variable #TIME C TIME #T 60 C MOVEL#T #THM 40 Hours & minutes C MOVEL#THM #THN 20 Hours, numeric C MOVE #THM #TMA 2 Minutes, alpha C #THN IFLT 12 Convert from . C MOVE 'AM' #AMPM 2 . 24 hour to 12 C ELSE . hour clock C MOVE 'PM' #AMPM C SUB 12 #THN C ENDIF C #THN IFEQ *ZERO C Z-ADD12 #THN C ENDIF C MOVE #THN #THA 2 Hours, alpha C #THN IFLT 10 C MOVEL' ' #THA Suppress zero C ENDIF C #THA CAT ':':0 #TIME 8 P C CAT #TMA:0 #TIME C CAT #AMPM:1 #TIME *. 1 .. + .. 2 .. + .. 3 .. + .. 4 .. + .. 5 .. + .. 6 .. + .. 7 ..
Reusing Source Code
Figure 2: /COPY Member to Define a Data Structure
*. 1 .. + .. 2 .. + .. 3 .. + .. 4 .. + .. 5 .. + .. 6 .. + .. 7 .. * Plant information data structure IPLANT DS 80 I 1 30P@DIVN I* Division number I 1 70P@NBR I* Plant number I 8 27 P@CITY I* City where plant is located I 28 30 P@PROD I* Corporate code of product manufactured at plant *. 1 .. + .. 2 .. + .. 3 .. + .. 4 .. + .. 5 .. + .. 6 .. + .. 7 ..
Reusing Source Code
Figure 3: Template to Clear a Subfile
C* RPG routine to clear a subfile C* Substitutions: C* &1 : Indicator conditioning SFLDSPCTL C* &2 : Indicator conditioning SFLDSP C*: Subfile control record name C* : Relative record field assigned to the subfile C SETOF &1&2 C WRITE C MOVE *ZERO
Reusing Source Code
Figure 4: Portion of Display File Template
... A RSFLCTL( ) ... A &2 SFLDSP A &1 SFLDSPCTL A N&1N&2 SFLCLR A &2 SFLEND(*MORE)
LATEST COMMENTS
MC Press Online