Not every repetition has a known count. A program might retry a connection until it succeeds, prompt an operator until valid input arrives, or drain a work queue until nothing remains. The number of passes is not known in advance; a business condition decides when to stop. RPGLE’s DOU operation code expresses exactly that kind of repetition: it repeats a controlled group until an expression becomes true, checking that expression only after the body has already run.12
This tutorial focuses on that single, post-test form. It assumes you already understand counted DO repetition and does not compare DOU against DOW beyond the minimum contrast needed to place the timing correctly.
Quick Summary
- Write a post-test group as
dou indicator-expression; ... enddo;.1 - The expression is evaluated after the body executes, at
ENDDO. Repetition continues while the expression is false and stops once it becomes true.1 - Because the check happens after the body, a
DOUgroup always executes at least once, even if the condition was already true before the loop was entered.1 ENDDOmarks the end of the group and is where RPG re-evaluates the condition and decides whether to repeat.2- Use
DOUwhen the business rule is “do the work, then check if you’re done” — not when a numeric range or a check-first condition governs repetition. - The loop can only stop if something the body does changes the outcome of the condition. A condition that never changes produces an infinite loop.
- Trace the first pass by hand, especially when the condition might already be true before entry, to confirm the guaranteed execution is intentional.
Reader Prerequisites
You should already be comfortable with counted repetition from RPGLE DO Loops Explained (ARTICLE-068), including the index, limit, body, and ENDDO vocabulary. This tutorial reuses that vocabulary but replaces the numeric range with a condition.
The wider learning sequence is collected in RPGLE for Beginners: A Practical IBM i Learning Path (ARTICLE-046).
Learning Outcomes
After reading, you should be able to:
- State what
DOUmeans and when its condition is evaluated relative to the body. - Explain why a
DOUgroup always executes at least once. - Trace a counter-based
DOUloop and a state/sentinel-drivenDOUloop. - Recognize the infinite-loop risk created by a condition whose state the body never updates.
- Choose
DOUonly when “run first, then check” actually matches the business rule.
Why Post-Test Repetition Exists
Some tasks are naturally check-first: read records while more remain, keep going while a balance is positive. Others are naturally do-first: attempt a connection, then decide whether to retry; process a batch, then check whether anything was left over. For that second family, phrasing the loop as “check first” would be backwards — there is nothing meaningful to check before the first attempt has even happened.
DOU matches that do-first shape directly. The body is not guarded by an entry test the way a pre-test loop is guarded; it runs, and only afterward does the program ask whether it should run again.1 This makes DOU a natural fit for retry logic, prompt-and-validate sequences, and any task where “try once, then decide” is the actual business rule.

Anatomy of an RPGLE DOU Loop
Here is the smallest complete post-test example:
dcl-s attemptLimit int(10) inz(5);
dcl-s attemptCount int(10) inz(0);
dou attemptCount >= attemptLimit;
attemptCount += 1;
dsply ('Attempt ' + %char(attemptCount));
enddo;
The parts have distinct responsibilities:
| Part | Example | Responsibility |
|---|---|---|
| Until-condition | attemptCount >= attemptLimit | Indicator-valued expression evaluated after the body |
| Body | attemptCount += 1; dsply ... | Work performed once per pass, including the state the condition depends on |
| Terminator | enddo; | Closes the group; RPG evaluates the condition here and decides whether to repeat |
IBM documents DOU as preceding a group of operations executed at least once and possibly more than once, with ENDDO as its matching end operation that re-evaluates the expression.12 The variable names above and the recommendation to keep the condition’s state visible in the body are teaching and readability guidance, not compiler requirements.

How the Loop Progresses
Trace basic-dou-loop.rpgle one state at a time, starting with attemptCount at 0 and attemptLimit at 5:
| Moment | attemptCount | What happens |
|---|---|---|
Enter DOU | 0 | No condition check yet — the body runs unconditionally |
| First body execution | 0 → 1 | Increment, then display “Attempt 1” |
First ENDDO check | 1 | 1 >= 5 is false; repeat |
| … | … | Passes 2 through 4 follow the same pattern |
| Fifth body execution | 4 → 5 | Increment, then display “Attempt 5” |
Fifth ENDDO check | 5 | 5 >= 5 is true; continue after the group |
The critical detail is the very first row: RPG does not check attemptCount >= attemptLimit before the first pass. It cannot — DOU‘s condition lives at ENDDO, after the body, not at the top of the group.12 This is conceptual source-level behavior; the diagram and trace do not claim anything about compiler internals or generated machine instructions.
Counter-Based DOU Example
basic-dou-loop.rpgle is the example traced above. It stops as soon as attemptCount reaches attemptLimit, giving exactly five displayed attempts. The pattern — initialize a counter, do work, advance the counter, check against a limit — looks similar to counted DO, but the stopping question is phrased as a condition rather than a range, and the check happens after the work instead of controlling entry to it.
The Post-Test Boundary: When the Condition Starts True
The most important boundary case for DOU is not an off-by-one count; it is what happens when the until-condition is already true before the loop is ever entered. dou-condition-and-boundary.rpgle makes this visible:
dcl-s remainingItems int(10) inz(0);
dcl-s iterationsRun int(10) inz(0);
dou remainingItems <= 0;
iterationsRun += 1;
dsply ('Iteration ' + %char(iterationsRun) + ' remaining ' + %char(remainingItems));
enddo;
dsply ('Total iterations ' + %char(iterationsRun));
remainingItems starts at 0, which already satisfies remainingItems <= 0. A reader who expects a check-first loop might predict zero iterations. The intended, documentation-based result is iterationsRun = 1: the body runs once, because DOU never tests the condition before that first pass — only at the ENDDO that follows it.1 Confirming this boundary by hand, with a deliberately “already done” starting value, is the fastest way to catch a mistaken assumption that DOU behaves like a pre-test loop.
State/Sentinel-Driven DOU Example
Not every DOU condition is a simple counter comparison. readable-dou-processing.rpgle models a small connection-retry sequence where the stopping question combines a success flag with a maximum-attempts safeguard:
dou connectionEstablished or connectionAttemptNumber >= maxConnectionAttempts;
connectionAttemptNumber += 1;
connectionEstablished = (connectionAttemptNumber = maxConnectionAttempts);
dsply ('Connection attempt ' + %char(connectionAttemptNumber));
enddo;
With maxConnectionAttempts set to 3, the loop tries at least once and at most three times: it stops early if connectionEstablished turns on, and it stops regardless once the attempt count reaches the safeguard. This is a common shape for real retry logic — a state flag for “are we actually done” combined with a count-based safeguard so the loop cannot run forever even if the flag never turns on. Both variables are updated inside the body, before ENDDO evaluates them; that update is what makes the stopping question answerable at all.
All three members in this tutorial are documentation-reviewed; not compiled or run on IBM i in this workspace. Their traced values follow from initialized values and documented DOU/ENDDO behavior, not captured runtime output.
Common Mistakes
Forgetting to update the state the condition depends on. If nothing in the body changes the value(s) tested at ENDDO, the condition can never become true, and the loop repeats forever. This is the single most consequential DOU mistake — always identify which variable(s) the until-condition reads and confirm the body writes to at least one of them on every pass.
Assuming DOU checks before the first pass. As dou-condition-and-boundary.rpgle shows, the body runs once even when the condition is already true beforehand. Code that depends on skipping the body under that condition belongs in a pre-test structure, not DOU.
Using DOU when a numeric range is already known. If the real stopping rule is “repeat exactly N times,” counted DO states that directly; reaching for a condition to express a known count adds an extra place for the count to drift.
Relying on a single flag with no safeguard. A pure success flag with no maximum-attempts guard can loop forever if success never arrives for a legitimate reason, such as a permanently unreachable resource. Combining a state flag with a bounded count, as in the retry example, keeps the loop safely terminating.
Hiding the condition’s state far from the loop. If the variables tested at ENDDO are updated deep inside a long body, a reader cannot quickly confirm the loop terminates.
Confusing an attempt count with a success count. connectionAttemptNumber counts tries, not successes. Naming each state variable for exactly what it counts avoids reusing one variable for two different questions.
Readability Guidance
The following are editorial recommendations, not compiler requirements:
- Name every variable the until-condition reads for what it represents (
attemptCount,remainingItems,connectionEstablished) rather than reusing a generic flag across unrelated loops. - Keep the statement that updates the condition’s state close to the top or bottom of the body, not buried in unrelated work.
- When a condition combines a state flag with a safeguard count, write both in one line so a reader sees the full stopping rule at a glance.
- Comment the intended maximum number of passes when a safeguard count exists, so a future reader knows it is a safety limit, not an arbitrary number.
- Extract a named procedure when the work needed to decide “are we done” grows complex.
IBM defines what DOU and ENDDO do.12 These naming and layout choices are maintainability guidance from this tutorial.
Choosing the Right Loop Structure
Choose a structure by asking what governs completion and when the check belongs:
| Question | Appropriate direction |
|---|---|
| “Repeat for these numeric positions” | Counted DO — ARTICLE-068 |
| “Run at least once, then stop when a condition becomes true” | DOU — this article |
| “Run only while a condition is true, and skip entirely if it starts false” | DOW — ARTICLE-070 |
DOW differs from DOU in exactly one respect that matters here: DOW tests its condition before the body, so it can skip the body entirely on the first pass. DOU cannot skip the first pass, because its condition lives at ENDDO.1 ARTICLE-070 is the canonical planned article for that pre-test form and is intentionally not linked yet, since this repository has no publishable destination for it. Detailed DOW timing, LEAVE, ITER, nested loops, arrays, and file-reading loops remain outside this tutorial’s scope.
FAQ
Does a DOU loop check its condition before the first pass, or can it run zero times?
No to both. The condition sits at ENDDO, after the body, so it is evaluated only once the body has already run; in the normal case documented here, that means the body always executes at least once.1
What happens if the condition’s state never changes?
The condition never becomes true, and the loop repeats indefinitely. Confirm that some statement in the body writes to every variable the until-condition reads.
Is DOU the right choice for reading records until end of file?
Only if “read first, then check for end of file” matches your actual sequence. Many read-loop patterns are pre-test by nature; verify the intended timing before choosing DOU over a pre-test structure.
Can the until-condition combine more than one thing, like a flag and a count?
Yes. readable-dou-processing.rpgle combines a success flag with a maximum-attempts count using or, which is ordinary indicator-expression logic, not a special DOU feature.1
Were these examples tested on IBM i?
No. They are documentation-reviewed against IBM i 7.5 and 7.4 references, but were not compiled or run on IBM i in this workspace.
Key Takeaways
- A post-test RPGLE
DOUgroup repeats a body until an expression becomes true, checked atENDDO, after the body runs.12 - The body always executes at least once, even if the condition was already true before the loop began.
- The loop can only terminate if the body updates the state the condition depends on.
- Combine a state flag with a bounded safeguard count when “success” alone might never arrive.
- Choose
DOfor known numeric ranges andDOWwhen the check must happen before the first pass. - Treat descriptive naming, visible state updates, and hand-tracing as editorial guidance rather than IBM compiler requirements.
Continue Your Learning
- Pillar: RPGLE for Beginners: A Practical IBM i Learning Path (ARTICLE-046)
- Prerequisite: RPGLE DO Loops Explained (ARTICLE-068)
- Current: RPGLE DOU Loops Explained (ARTICLE-069)
- Recommended next: RPGLE DOW Loops Explained (ARTICLE-070)
- Then: RPGLE LEAVE, ITER, and Loop Control (ARTICLE-071)
ARTICLE-070 and ARTICLE-071 are named from the canonical registry but remain unlinked until publishable repository routes exist.
IBM Evidence Used
The IBM i 7.5 DOU reference is the primary source for post-test timing, the guaranteed first execution, and condition evaluation at ENDDO.1 The matching ENDDO reference supports how the group closes and re-evaluates the condition.2 IBM’s structured-programming overview supplies the broader grouping context shared with counted DO.3
The IBM i 7.4 DOU and ENDDO topics were cross-checked for the same operation model.45 No publication date is assigned to any IBM Docs page. Recommendations about naming, state-update discipline, safeguard counts, and hand-tracing are editorial guidance; they are not represented as IBM compiler rules.
References
IBM, DOU (Do Until), IBM i 7.5, accessed 2026-09-13. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩
IBM, ENDDO (End Do), IBM i 7.5, accessed 2026-09-13. ↩↩↩↩↩↩↩
IBM, Structured Programming Operations, IBM i 7.5, accessed 2026-09-13. ↩
IBM, DOU (Do Until), IBM i 7.4, accessed 2026-09-13. ↩
IBM, ENDDO (End Do), IBM i 7.4, accessed 2026-09-13. ↩
References
- DOU (Do Until) — IBM
- ENDDO (End Do) — IBM
- Structured Programming Operations — IBM
- DOU (Do Until) — IBM
- ENDDO (End Do) — IBM