Concepts / Program Design

Program Design

Software engineering is a discipline for constructing correct, working, and well-written programs.

  • Programming

A Mosaic Program Begins as a Vague Plan

Suppose you have been asked to write a program that opens a window, fills it with colors, and then keeps changing squares in that window. You already know roughly what the program should do, but you do not yet know how each piece will be coded. A natural first move is to write the plan down as a short list of big steps. The source example does exactly this: open a Mosaic window, fill the window with random colors, and then move around changing squares at random.

This list is a legitimate algorithm, but it is far too vague to turn directly into code. This article follows that plan from its rough form to a refined form, then shows how a program keeps track of where it is while it runs, and how to find the first step where a refined plan goes wrong. Along the way it explains why this kind of design work should happen before construction begins at all.

What Software Engineering Asks of a Programmer

Definition

Software engineering is a discipline for constructing correct, working, and well-written programs.

Three words in that definition carry most of the weight. A program is correct when it does what it is supposed to do, working when it actually runs, and well-written when its structure lets people read and maintain it. Program design is the part of this discipline concerned with planning how a program will be built so that those goals can be met before the code is written.

Within that discipline, software engineers analyze the problem before designing a program to solve it. Accepted and proven methods guide both problem analysis and program design. Analysis asks what the program must accomplish. Design asks how the program will accomplish it. Construction, which is writing the code, comes after both.

Why Design Comes Before Construction

A common beginner belief is that building a complex program simply means sitting at a computer and composing the program directly. This belief overlooks the design work that must come first. A complex program should not be composed without prior design, and the order of activities matters: analysis and design come before construction rather than being attempted without preparation.

Each stage hands something to the next. Problem analysis produces an understanding of what the program must do. Program design turns that understanding into a plan of steps and parts. Construction turns the plan into code. Testing then checks whether the code meets what the analysis required. A stage that skips its predecessor leaves the next stage guessing.

Tracking Where the Program Is

The refined Mosaic loop moves around the window, so it needs a record of where it currently is. The author represents the current position with two int variables. currentRow holds the row number of the square where the disturbance is located, and currentColumn holds the column number. A variable holding information like this, which describes what the program is doing at a given moment, is one way of representing program state.

The mosaic has 16 rows and 20 columns of squares. Placing the current position in the center therefore means setting currentRow to 8 and currentColumn to 10. Those two values are the state the loop depends on. Each time the loop moves the current position, these values are the things that must change.

Worked example

Tracing the position through three moves

The loop starts at the center of a 16 by 20 mosaic, with currentRow 8 and currentColumn 10. Trace the two state variables through three moves: right, then down, then right.

  1. 1
    Record the starting state

    Setting the current position to the center gives currentRow = 8 and currentColumn = 10.

  2. 2
    Apply the first move

    A move to the right changes only the column. currentColumn increases from 10 to 11, and currentRow stays at 8.

  3. 3
    Apply the second move

    A move down changes only the row. currentRow increases from 8 to 9, and currentColumn stays at 11.

  4. 4
    Apply the third move

    A move to the right changes only the column again. currentColumn increases from 11 to 12, and currentRow stays at 9.

Result

After three moves the state is currentRow = 9 and currentColumn = 12. Each move changed one variable and left the other alone, so checking the two values after every move shows exactly where the program is.

Stepwise Refinement

Definition

Stepwise refinement develops a program's algorithm gradually, turning a rough list of steps into smaller steps that are precise enough to implement.

The method is to keep examining each step and asking whether it can be described more precisely. Each pass produces a slightly more detailed algorithm. This process of repeated, gradual elaboration is the heart of stepwise refinement. It also means you do not have to decide everything at once.

In the mosaic example the vague third step, move around changing squares at random, is still too loose to code. The author expands it. The program starts in the middle of the window and then repeatedly moves to new squares and changes their colors, for as long as the window remains open. The expansion adds two things the vague version lacked: a starting condition and a stopping condition. A refined loop usually needs explicit starting and stopping conditions of this kind.

Choosing Which Steps Become Subroutines

Not every step in a refined outline should be pulled into its own piece. The useful question is whether a step describes a coherent task that can be handled on its own. Filling the window with random colors is a clear, self-contained job. It does not depend on how the program later moves around the window, so it can be worked on separately, and the author therefore writes a separate subroutine for it.

A subroutine is a separate block of code that carries out a specific task. The main routine can call it by name at the point where that task is needed. The source notes that subroutines make the main routine simpler and easier to understand, and that is the reason the mosaic author gives for pulling tasks out. The source already has one such subroutine, Mosaic.open(), which opens the window. The author then decides to write two more to carry out the two tasks inside the while loop, so the main routine reads as a short sequence of calls.

Notice what the source does not do. The starting position and the loop are refinements of the vague third step, and they are not separate subroutines in the source. Refinement produces details at every level, but subroutines are drawn only around units that stand on their own.

Locating Where a Refinement Goes Wrong

A refined algorithm can look correct line by line and still produce the wrong result. To find the fault, trace the state variables after each step and compare them with the trace you intended. The first step where the two traces disagree is where the refinement first departs from what was intended. The check is simple: the values of currentRow and currentColumn are the first place to look.

  • Common mistake

    Assuming the program is correct because the loop looks right

    What it looks like

    A loop that moves the current position correctly still changes the wrong square if currentRow and currentColumn start with the wrong values.

    Why it fails

    The loop's correctness depends on the state variables it reads and writes, not only on its structure.

    Do this instead

    Trace the state values after each step and compare them with the intended values.

  • Common mistake

    Writing code directly from the first list of big steps

    What it looks like

    Coding open window, fill with random colors, and change squares at random as they stand, with no starting or stopping condition for the loop.

    Why it fails

    The vague third step does not say where the program starts or when the loop stops, so it cannot be coded precisely.

    Do this instead

    Refine each step, adding starting and stopping conditions, until each piece is precise enough to implement.

  • Common mistake

    Making every refined step a subroutine

    Why it fails

    Only steps that describe a coherent, self-contained task are good candidates for subroutines. Starting positions and loop details are refinements, not separate units.

    Do this instead

    Extract a step only when it can be handled on its own, such as filling the window with random colors.

  • Common mistake

    Skipping analysis and design and composing a complex program directly at the keyboard

Practice Tracing and Refining

EASY

Using the mosaic's 16 rows and 20 columns, start the loop at the center with currentRow 8 and currentColumn 10. Apply these moves in order: right, right, down, down. After each move, write down the new values of currentRow and currentColumn. Then state the final position and say which variable changed on each move.

Hints
  • Start by writing currentRow 8 and currentColumn 10 as your first line.
  • A move to the right changes only currentColumn, and a move down changes only currentRow.
  • After four moves you should have currentRow 10 and currentColumn 12.
MEDIUM

A teammate's refined loop starts at currentRow 8 and currentColumn 10, and the first three moves are right, down, right. Their trace reads (8,10), (8,11), (9,11), (9,12). Your intended trace for the same moves reads (8,10), (8,11), (9,11), (9,12) as well, but after the fourth move the teammate's trace reads (9,13) where you expected (9,12) after a left move. Which step is the first departure from the intended behavior, and which state variable should you check?

Hints
  • Compare the two traces one row of values at a time, and stop at the first difference.
  • Ask which variable changed in the wrong direction on that step.

Summary of the Design Process

  1. 1
    Software engineering is a discipline for constructing correct, working, and well-written programs, and program design plans how that construction will happen.
  2. 2
    A complex program should not be composed without prior design. Analysis and design come before construction.
  3. 3
    Stepwise refinement turns a rough list of steps into progressively more precise steps, adding details such as starting and stopping conditions.
  4. 4
    A refined step becomes a subroutine when it describes a coherent, self-contained task, such as filling the window with random colors.
  5. 5
    State variables such as currentRow and currentColumn record where the program is. Tracing them step by step reveals the first step where a refined algorithm departs from what was intended.

At a glance

  • Software engineering is a discipline for constructing correct, working, and well-written programs, and program design plans construction before it begins.
  • Complex programs should not be composed without prior design. Problem analysis and design come before construction.
  • Stepwise refinement develops an algorithm gradually, turning a rough list of steps into precise steps, including starting and stopping conditions for loops.
  • Refined steps that describe coherent, self-contained tasks are good candidates for subroutines.
  • State variables such as currentRow and currentColumn record where a program is, and tracing them step by step reveals where a refined algorithm first departs from what was intended.