Avoid loop-the-loops in software

Charles Stover
3 min readJun 19, 2023

I recently reviewed a pull request for a software test suite, and I wanted to call out this particular code anti-pattern, which I refer to as a “loop-the-loop” on account of the reader’s need to jump back and forth between lines of the code module.

I share code snippets below to exemplify a loop-the-loop, the mental gymnastics required to comprehend a loop-the-loop, and how a refactor can improve the code’s readability.

There’s little reason to abstract a value that you aren’t referencing multiple times. Consider keeping your values alongside the code that references them. Turning string literals into constants just requires the reader to jump around to determine what the constant contains.

10: const PAGE_TITLE = ‘Hello world’;
20: const PAGE_TITLE_SELECTOR = ‘some > scary > selector’;
30: expect(cy.get(PAGE_TITLE_SELECTOR).innerText).toBe(PAGE_TITLE);

In the above example, the line 30 is doing 3 things:

  • selecting an element
  • grabbing its inner text
  • comparing it to a string constant

When there is an error on line 30, the investigation steps are as follows:

  • Is the selector wrong? Check line 20.
  • If it was wrong, update it. Done. ✅
  • If it was right, go back to line 30.
  • Is the value wrong? Check line 10.
  • If it was wrong, update it. Done. ✅
  • If it was right, go back to line 30.
  • Update the innerText property. Done. ✅

In a worst case scenario, the engineer had to check the following lines in order: 30, 20, 30, 10, 30.

Here’s a minimal refactor in which our code only performs 2 actions per segment:

10: const PAGE_TITLE_SELECTOR = 'some > scary > selector';
20: private get pageTitle(): string {
21: return cy.get(PAGE_TITLE_SELECTOR).innerText;
22: }
30: expect(home.pageTitle).toBe('Hello world');

In the above example, line 20 is doing 2 things:

  • selecting an element
  • grabbing its inner text

--

--