12 min read

There is a moment in every programmer's development — regardless of language — when the question shifts. In your introductory course, the question was: Does it work? You wrote a program, compiled it, ran it, checked the output. If the output was...

Part II: Control Flow and Program Design

From "It Works" to "It's Well-Designed"

There is a moment in every programmer's development — regardless of language — when the question shifts. In your introductory course, the question was: Does it work? You wrote a program, compiled it, ran it, checked the output. If the output was correct, you were done. The program worked. You moved on.

That question never goes away. Programs must work. But in professional practice, a second question rises to equal importance: Is it well-designed? A program that produces correct output but is structured as a tangled web of paragraphs, GOTOs, and deeply nested conditionals is a program that works today and becomes a maintenance nightmare tomorrow. A program that produces the same correct output through clear, modular, logically structured code is a program that works today and can be understood, modified, and extended for years to come.

Part II is about that second question. It is about the transition from writing COBOL that compiles to writing COBOL that a professional team would be proud to put into production.

The Cost of Poor Design

At GlobalBank, Maria Chen keeps a mental catalog of what she calls "horror programs" — production COBOL that technically works but that no one wants to touch. There is ACCT-PROC-7, a 14,000-line monolith with 237 paragraphs, no copybooks, and a control flow that resembles a plate of spaghetti more than a structured program. There is LOAN-CALC-LEGACY, which uses GOTO statements to jump between sections in a pattern that one developer described as "a choose-your-own-adventure book written by someone who hated the reader." There is INT-BATCH-NIGHTLY, which contains a single PERFORM UNTIL loop that runs for 3,200 lines before reaching its exit point.

These programs work. They have worked for years, some for decades. They process transactions, calculate interest, generate reports. But every time a business rule changes — and in banking, business rules change constantly — someone must venture into these programs and modify them. That someone spends days reading code, tracing logic paths, trying to understand what the original programmer intended. That someone makes a change, tests it, and discovers that the change broke something in a seemingly unrelated part of the program because of a hidden dependency they could not see. That someone works overtime, misses a weekend, introduces a bug that reaches production and triggers an incident.

The cost of poor design is not theoretical. It is measured in hours, in errors, in developer burnout, and occasionally in dollars — real dollars lost to production incidents caused by code that no one could safely modify.

Derek Washington learned this lesson his first month at GlobalBank. Coming from Java, where IDE refactoring tools could restructure code semi-automatically, he was stunned by how much of COBOL maintenance was manual, painstaking, and dependent on the programmer's ability to read and understand existing code. "In Java," he told Maria, "if the code was messy, I could lean on my tools. In COBOL, I have to lean on the code itself being clear. The code is the documentation."

Maria nodded. "Now you understand why we care so much about how code is written, not just what it does."

Structured Programming and COBOL's Evolution

The structured programming movement of the 1970s — championed by Edsger Dijkstra, whose famous letter "Go To Statement Considered Harmful" became a rallying cry — transformed how programmers thought about control flow. The core insight was elegant in its simplicity: any program can be written using only three control structures — sequence, selection, and iteration. No GOTOs required.

COBOL's relationship with structured programming is more nuanced than the textbook version suggests. Early COBOL (COBOL-68 and before) relied heavily on GO TO and ALTER statements for control flow. Programs written in that era often reflected the hardware constraints and programming conventions of their time: memory was scarce, compilers were primitive, and the idea that program structure mattered for human comprehension had not yet taken hold.

The COBOL-74 and especially COBOL-85 standards transformed the language. COBOL-85 introduced scope terminators (END-IF, END-PERFORM, END-EVALUATE, and others) that made structured programming not just possible but natural in COBOL. The EVALUATE statement provided a powerful multi-branch selection mechanism. In-line PERFORM allowed iteration without the paragraph-jumping that had made earlier COBOL programs hard to follow. These features did not just add new syntax; they changed how COBOL programmers thought about program organization.

Today, no competent COBOL shop writes new code with GO TO statements (with rare, carefully justified exceptions). But the legacy of unstructured COBOL lives on in the millions of lines of pre-1985 code still running in production. Understanding structured programming principles is therefore doubly important for you: you need them to write good new code, and you need them to recognize — and carefully refactor — the unstructured patterns you will encounter in legacy code.

Design Patterns in COBOL

The phrase "design pattern" is often associated with object-oriented programming and the famous Gang of Four book. But COBOL has its own patterns — recurring solutions to recurring problems that experienced programmers recognize and apply. These patterns are less formally cataloged than their OO counterparts, but they are no less real and no less important.

In Part II, you will encounter several patterns that form the backbone of well-designed COBOL programs:

The Main-Control Pattern organizes a program around a controlling paragraph that reads like a table of contents. The main paragraph calls initialization, processing, and termination paragraphs in sequence, and the processing paragraph typically contains a PERFORM UNTIL loop that drives the program's core logic. This pattern is so fundamental that experienced COBOL programmers can glance at a program's PROCEDURE DIVISION and know within seconds whether it follows the pattern — and if it does not, they immediately become cautious.

The Read-Ahead Pattern solves the problem of detecting end-of-file correctly in sequential file processing. You will master this pattern until it becomes second nature, because getting file processing loops wrong is one of the most common sources of bugs in COBOL programs.

The Switch-Flag Pattern uses 88-level condition names to create readable, self-documenting boolean logic. Instead of testing whether WS-EOF-FLAG equals "Y", you test whether END-OF-FILE is true — a small change in syntax that makes a large difference in readability.

The Paragraph-Per-Function Pattern ensures that each paragraph in your program does one thing and does it well. Paragraphs are named for what they do (not numbered, not abbreviated, not cryptic), and they are short enough to fit on a single screen. This pattern directly supports the readability theme that runs throughout this textbook.

These are not arbitrary conventions. They are the distilled wisdom of decades of enterprise COBOL practice, and they exist because they make programs easier to read, easier to test, easier to modify, and less likely to harbor bugs.

What MedClaim Teaches About Control Flow

At MedClaim, the claims adjudication engine is a masterclass in control flow complexity. A single medical claim passes through dozens of decision points: Is the patient eligible? Is the provider in-network? Is the procedure covered under the patient's plan? Does the claim require prior authorization? Has the deductible been met? Is there a co-pay, and if so, how much? Are there coordination-of-benefits rules with another insurer?

Each of these questions involves its own sub-logic, its own edge cases, and its own history of regulatory changes that have added conditions and exceptions over the years. James Okafor, MedClaim's lead developer, estimates that the adjudication engine embodies over 4,000 distinct business rules — rules that interact with each other in ways that are not always obvious.

Writing code that correctly implements 4,000 interacting business rules is hard. Writing that code so that another programmer can understand it — so that when rule 2,847 changes because of a new state regulation, someone can find the relevant code, understand its context, modify it, and test the modification without breaking rules 2,846 and 2,848 — is the real challenge.

That challenge is fundamentally a control flow and program design challenge. It is about choosing the right control structures (EVALUATE is indispensable here), organizing code into manageable units, using copybooks to centralize shared definitions, and applying defensive programming techniques to catch invalid data before it corrupts downstream processing.

The MedClaim examples in Part II will show you control flow and design at scale — not in toy programs with three IF statements, but in realistic scenarios where the number of decision paths is large, the business rules are complex, and the consequences of errors are serious.

Defensive Programming: Building In the Safety Nets

One of the five themes of this textbook is defensive programming, and Part II is where that theme takes concrete form. Defensive programming in COBOL means:

Validating inputs before processing them. Every field that comes from an external source — a file, a database, a CICS transaction, a called subprogram — should be checked for validity before your program acts on it. Numeric fields should be tested with IF NUMERIC before arithmetic. Code fields should be tested against valid values. Lengths and ranges should be verified.

Using scope terminators consistently. END-IF, END-PERFORM, END-EVALUATE, and their siblings are not optional decorations. They are structural markers that prevent the ambiguity bugs that plagued pre-COBOL-85 programs. In this textbook, every IF has an END-IF, every PERFORM has an END-PERFORM, and every EVALUATE has an END-EVALUATE. No exceptions.

Handling the unexpected. EVALUATE statements should always include a WHEN OTHER clause. File operations should always check the FILE STATUS. Called programs should always validate their parameters. The question to ask yourself is not "What should happen when everything goes right?" but "What should happen when something goes wrong?"

Failing safely. When a COBOL program encounters a condition it cannot handle, it should not simply abend with a SOC7 or S0C4. It should write a meaningful error message, set an appropriate return code, close open files, and terminate in a controlled manner. A program that fails safely is a program that your operations team can diagnose and recover from at 2 AM without calling the developer.

Priya Kapoor at GlobalBank learned defensive programming the hard way. In her second month, she wrote a batch program that processed account updates. It worked perfectly in testing. In production, it encountered an account record with a non-numeric value in the balance field — a data quality issue that testing had not caught. The program abended with a SOC7, the batch job failed, and the nightly processing chain was delayed by two hours while the operations team diagnosed the problem and restarted the job with the bad record excluded.

The fix was simple: add an IF NUMERIC test before the arithmetic. But the lesson was larger: in production, you encounter data that your test cases did not imagine. Defensive programming is not pessimism; it is professionalism.

Copybooks: The Unsung Heroes

One topic in Part II that deserves special mention is copybooks. In your introductory course, you may have encountered the COPY statement briefly or not at all. In enterprise COBOL, copybooks are essential infrastructure.

A copybook is a reusable piece of COBOL source code — typically a data definition, but sometimes procedural code — that is stored separately and included in programs via the COPY statement at compile time. In a system like GBCS, where hundreds of programs reference the same record layouts, the same status codes, and the same common data structures, copybooks ensure consistency: change the copybook, recompile the affected programs, and every program uses the updated definition.

Copybooks are also a point of serious risk. At GlobalBank, Maria Chen estimates that modifying a widely-used copybook can trigger the recompilation and retesting of over 200 programs. This is why copybook changes go through a rigorous review process — and why understanding copybooks, their power, and their risks is a critical intermediate skill.

What Part II Covers

The five chapters in Part II build systematically from control structures to program-level design:

Chapter 6: Advanced Conditional Logic takes IF and EVALUATE beyond the basics. You will master nested conditions, complex compound conditions with AND/OR/NOT, the EVALUATE TRUE pattern, and the art of writing conditional logic that is both correct and readable.

Chapter 7: PERFORM Mastery explores COBOL's primary iteration and modularity mechanism in depth. In-line PERFORM, out-of-line PERFORM, PERFORM VARYING, PERFORM THRU (and why you should usually avoid it), and the patterns that make PERFORM the workhorse of structured COBOL.

Chapter 8: Program Design and Structured Programming steps back from individual statements to consider program-level organization. Top-down design, the main-control pattern, paragraph naming conventions, and the principles that make a COBOL program a coherent whole rather than a collection of loosely related paragraphs.

Chapter 9: Copybooks and the COPY Statement covers copybook design, the COPY ... REPLACING option, copybook management in team environments, and the organizational practices that keep copybooks from becoming a source of chaos.

Chapter 10: Defensive Programming Techniques brings together input validation, error handling, FILE STATUS checking, condition name design, and the defensive mindset that separates amateur code from professional code.

The Standard You Are Building Toward

By the end of Part II, you will write COBOL programs that a professional team would recognize as well-designed. Your programs will have clear control flow, consistent structure, meaningful paragraph names, proper scope terminators, and defensive checks at every point where data enters or leaves your program. You will understand why these things matter — not as arbitrary style rules, but as engineering practices that reduce errors, ease maintenance, and make the difference between code that survives in production and code that becomes one of Maria's "horror programs."

The standard is high. It should be. The systems you are learning to build and maintain handle billions of dollars, millions of medical claims, and the daily operations of institutions that societies depend on. Those systems deserve your best work.

Let us learn how to give it to them.