Start with the valid index range
An off-by-one error happens when a count or boundary differs by one from what the program needs. In a zero-indexed array, the first element is at index 0. If an array contains n elements, its valid indexes run from 0 through n - 1. The value n is the array’s length, but it is not an index of an element. Keeping that distinction in view resolves many loop bugs.
The examples here use JavaScript syntax supported by ECMAScript 2015 and later. You can try them in a browser’s developer console or a Node.js environment that supports that syntax; no packages are needed. Consider const colors = ['red', 'blue', 'green'];. Its length is 3, while its valid indexes are 0, 1, and 2. Before examining a loop, write down those values. Then ask whether the loop should visit every element, stop before one, or include some other endpoint.
Check whether the loop includes its endpoint
A common mistake is writing for (let i = 0; i <= colors.length; i++) when the goal is to visit every color. The condition allows i to reach 3. On that last iteration, colors[i] is undefined because colors[3] is beyond the array. In JavaScript, reading that missing array element usually does not throw an error by itself. A later operation, such as calling a method on the value, may fail instead. That delay can make the original boundary mistake harder to notice.
For this task, use for (let i = 0; i < colors.length; i++). The loop begins at 0 and continues while i is less than 3, so it visits 0, 1, and 2. The opposite mistake is also possible: for (let i = 0; i < colors.length - 1; i++) stops after index 1 and skips 'green'. Do not change <= to < automatically whenever you see it. First state which indexes the task requires, then choose a condition that produces exactly those indexes.
Trace a small input by hand
When a loop looks plausible but its result is wrong, trace it with an array short enough to inspect without guessing. Record the starting value of the index, the condition checked before each iteration, the element accessed, and the index after the update. For the incorrect <= loop above, the sequence of index values that enters the body is 0, 1, 2, 3. Comparing that sequence with the valid range immediately exposes the extra iteration.
Also inspect where an index changes inside the loop body. For example, if the loop header increments i and the body increments i again, it may skip elements even though the condition itself is correct. If an index is calculated from another value, trace the calculation rather than only the loop variable. A useful debugging question is: “What is the first value that should not enter this loop?” If that value still passes the condition, the upper bound needs attention.
Test the boundaries, not just a typical array
Try the same loop with an empty array, a one-element array, and a short array with distinct values. An empty array tests whether the body runs when there is nothing to process. A one-element array tests whether the only valid index, 0, is visited exactly once. Distinct values help reveal a skipped or repeated element; identical values can conceal either problem.
Check the expected result before running each test. For a loop meant to visit every element, an empty array should produce zero visits, a one-element array should produce one visit, and ['red', 'blue', 'green'] should produce the values in that order. If you are counting visits, start the count at 0 and increment it once per visit. These tests are small, but they cover the places where a mistaken <, <=, starting index, or update is most likely to show up.
Make the boundary easier to see next time
Use an index-based loop when you need the index, but consider a direct value loop when you do not. In JavaScript, for (const color of colors) visits each value without requiring you to write an index boundary. That does not solve every loop problem, but it removes one opportunity for an indexing mistake.
When an index is necessary, name the intended range in a nearby comment or in your own notes: “start at 0; stop before length.” If the task genuinely needs to include an endpoint, state that explicitly too. Finally, keep a small boundary test when the loop’s output matters. The most reliable fix is not merely changing one character in a condition; it is confirming that the values entering the loop match the values the task requires.



