Ugly Code vs Clean Code: A/B Comparison of Legacy/Test-Driven Implementations

room: Columbus KL — time: Monday 16:00-16:45, Monday 16:45-17:30
Level: Practicing

The instructors wish, when they were first learning test-driving, refactoring, and OO, that they had had a side-by-side comparison between code Heaven and code Hell. Such an object lesson would have made the value and benefits of agile programming practices so much more plain, so much sooner. Alas for us, but hurray for you! In this workshop you will be able to compare and work with two very different implementations of the same problem domain: one of them fabulously ugly, and the other of them — well — a lot better. This is a close-repeat of a successful session we gave at Agile 08.

Process/Mechanics

The Experience: Catastrophe & Epiphany

We’ll provide attendees with a unique opportunity to experience a worst-case and better-case implementation of a 10 x 10 TicTacToe game. All programming will be in pairs or small groups. All code is Java; solid Java programming ability is a prerequisite for this workshop.

In this very hands-on tutorial, programmers will be asked to make an innocent-looking requirements tweak in Ugly Code Base A, the downright ornery one that is a single 1200-line “class” with a non-trivial cyclomatic complexity, no-unit tests, rampant duplication, muddled flow of control, a great wash of globals, and no real hope.

We’ll get everyone set up with Ugly Code Base A in Eclipse as quickly as we can. We want to get to coding with very little introductory preamble. (For instructions on how to checkout the code from subversion, go here: http://code.google.com/p/ugly-and-clean-tictactoe/source/checkout ) We’ll summarize the problem domain, and show Ugly Code Base A running as a playable Java Applet.

Duration: 20 minutes.


We’ll then subject everyone to a seemingly innocent little requirements change in Ugly Code Base A. We’ll enforce the deadline ruthlessly. There will be wailing and gnashing of teeth.

Duration: 20 minutes.


We’ll next have a retrospective on the first exercise, with brief synopses from different teams. We’ll all share the pain. We’ll then have a summary of agile/XP programmer practices that can prevent this kind of heartache entirely.

Duration: 15 minutes


Next, everyone will be given Cleaner Code Base B (again, with simple-enough design, good-enough unit tests, etc). They’ll attempt the same requirements tweak. We predict a happier outcome.

Duration: 20 minutes


We’ll retrospect: (Whoa, what just happened? How in the world can Code Base A be so completely intractable for this requirement compared to Code Base B?)

We’ll celebrate all the Aha! moments. We’ll all take one last deep, relaxing breath.

We’ll close with Q & A.

Duration: 15 minutes


Notes: How Ugly is “Ugly”?

Ugly Code Base A (The Legacy TicTacToe game) is chock full of methods that look like this:

public int someWierdMethodName(

int playerMark, int x, int type) { int j, k, l; int position = 0, position2 = 0;

for (l = 0; l < 6; l++) {
    for (j = 0; j < SQUARES_PER_SIDE; j++) /* horiz & vert */
    {
        resetAllMarksAlongAxesForFirstHalfOfBoard();

        position = checkFor5AlongHorizAxis(playerMark, x, j, l, position);

        if (marksByAxisByPlayerForChecking[0] == 3 && marksByAxisByPlayerForChecking[1] == 2) {
            if (type == SETFLAGS_MODE) {
                tempTableForChecks[tempRowForChecks[0]] = OCCUPIED;
                tempTableForChecks[tempRowForChecks[1]] = OCCUPIED;
            }
            if (type == CLEAN_MODE) return tempRowForChecks[0];
        }

        if (marksByAxisByPlayerForChecking[0] == 4 && marksByAxisByPlayerForChecking[1] == 1 && type == CHECK_MODE) 
                                return position;

        position = checkFor5AlongVertAxis(playerMark, x, j, l, position);

        if (marksByAxisByPlayerForChecking[2] == 3 && marksByAxisByPlayerForChecking[3] == 2) {
            if (type == SETFLAGS_MODE) {
                tempTableForChecks[tempRowForChecks[0]] = OCCUPIED;
                tempTableForChecks[tempRowForChecks[1]] = OCCUPIED;
            }
            if (type == CLEAN_MODE)
                return tempRowForChecks[0];
        }
        if (marksByAxisByPlayerForChecking[2] == 4 && marksByAxisByPlayerForChecking[3] == 1 && type == CHECK_MODE)
            return position;
    }

    for (j = 0; j < 6; j++) {
        resetAllMarksAlongAxesForFirstHalfOfBoard();

        for (k = 0; k < 5; k++) 
        {
            position  = checkFor5AlongDiagDownRightAxis(playerMark, x, j, k, l, position);

            position2 = checkFor5AlongDiagUpRightAxis(playerMark, x, j, k, l, position2);
        }

        if (marksByAxisByPlayerForChecking[0] == 3 && marksByAxisByPlayerForChecking[1] == 2) {
            if (type == SETFLAGS_MODE) {
                tempTableForChecks[tempRowForChecks[0]] = OCCUPIED;
                tempTableForChecks[tempRowForChecks[1]] = OCCUPIED;
            }
            if (type == CLEAN_MODE) return tempRowForChecks[0];
        }
        if (marksByAxisByPlayerForChecking[0] == 4 && marksByAxisByPlayerForChecking[1] == 1 && type == CHECK_MODE) return position;



        if (marksByAxisByPlayerForChecking[2] == 3 && marksByAxisByPlayerForChecking[3] == 2) {
            if (type == SETFLAGS_MODE) {
                tempTableForChecks[tempRowForChecks[0]] = OCCUPIED;
                tempTableForChecks[tempRowForChecks[1]] = OCCUPIED;
            }
            if (type == CLEAN_MODE) return tempRowForChecks[0];
        }
        if (marksByAxisByPlayerForChecking[2] == 4 && marksByAxisByPlayerForChecking[3] == 1 && type == CHECK_MODE) return position2;
    }
}
return (NONE);

}

So, you know what? That method was much scarier before we got a hold of it. Almost none of the variable names revealed any intention, and all of those semi-helpful-sounding private methods? They were all in-line. But there are limits to our cruelty, it turns out. We had to fix it up at least a little.


Note: This session is not a complete survey of all of the differences between old-fashioned bad procedural code, and new-fangled agile OO goodness. It is instead a deep dive, from the perspective of a poor programmer with a nasty deadline. It attempts to help answer the questions “Why should I care about how Clean my Code is? Why should I care about extensibility?”

Further Note: Cleaner Code Base B is not, itself, perfectly Clean Code. How do you think it could it be cleaner? Attend the session and make refactoring suggestions. Refactor on the spot for extra credit.

Learning outcomes
  • Code extensibility and Legacy Code are much bigger deals than many programmers in the industry admit or understand.
  • Most of us do not grasp the differences in extensibility between really ugly code and Clean Code, because our work lives do not give us that A/B “Delta Perspective.”
  • It takes an A/B comparison (what the instructors call a “Delta Game”) to begin to really grasp the differences.
  • TDD and Refactoring are far easier to learn in Clean Code codebases.
Featured participants
Primary target persona