Concepts / Subroutine Design

Subroutine Design

A main routine handles the overall flow of a program and hands specific work to subroutines.

  • Programming

A Mosaic That Looks Wrong

Imagine a window filled with a grid of colored rectangles. The colors keep changing while a small cursor wanders from one rectangle to the next. Now imagine the result looks wrong. Where do you start looking? This article answers that question using a mosaic animation program as a case study. The program is short, but it already contains the habit that matters most in program design: the main part of the program is written first, and the parts it depends on are described and written afterward.

The key idea is that a program is easier to understand when you separate two questions. The first question is when something happens. The second question is what that something accomplishes. The main routine answers the first question, and each subroutine answers the second.

The Main Routine as Flow Manager

A main routine handles the overall flow of a program and hands specific work to subroutines. In the mosaic program, the main routine opens the window, decides where the animation begins, and repeats the core action until the user closes the window. It does not hold every detail of how colors are chosen or how the cursor moves. Those details are pushed into separate pieces. A subroutine is a separate, small task, and the main routine is the only place that decides when each subroutine runs.

Main routinefillWithRandomColors()changeToRandomColor(int,int)1. call once at start2. return to next line3. call inside loop4. return to loop body
When the main routine calls a subroutine, where does control go, and how does it come back to the main routine's next line?

Read the sequence as a series of hand-offs. Control leaves the main routine at a call, runs through the subroutine's own steps, and then returns to the main routine at the point just after the call. The fill subroutine is called once at the start, so its arrow appears only once. The change subroutine is used inside the loop, so it is called again on every pass. Notice that to follow this flow you did not need to read how the color is chosen. The position of each call in the main routine is enough to answer questions about order.

Identifying the Mosaic's Subroutines

The author has a main routine but not yet a complete program, because three subroutines are still missing: fillWithRandomColors(), changeToRandomColor(int,int), and randomMove(). The first is called once at the start, while the other two are used inside the loop. The hierarchy below shows how these pieces sit under the main routine.

Mosaic main routinefillWithRandomColors()Called once at startchangeToRandomColor(int,int)Called inside looprandomMove()Called inside loopdelegatesdelegatesdelegates
Which subroutines does the mosaic program need, and how do they sit under the main routine?

The source names the three subroutines and tells you when each one runs. It does not give the full definition of changeToRandomColor(int,int) or randomMove() in this excerpt, so this article does not claim how they are implemented. From the name and the case study, changeToRandomColor(int,int) takes two integer arguments and is responsible for changing a color, and randomMove() is responsible for the cursor's wandering. Those roles are the ones to keep in mind when you later decide which routine owns a given behavior.

Describing a Subroutine by Its Postcondition

Definition

A postcondition is a statement about what must be true when a subroutine finishes. It describes the state the subroutine must leave behind, and it does not say how the work is done. Describing a subroutine this way is useful because you can state exactly what success looks like before deciding on any steps.

The source describes fillWithRandomColors() by its postcondition: each of the rectangles in the mosaic has been changed to a random color. Notice what this sentence leaves out. It says nothing about loops, indexes, or the order in which rectangles are visited. Those are algorithm questions, and they come later.

Once the postcondition is clear, the author writes pseudocode, an informal description of an algorithm that accomplishes the task. Pseudocode is not tied to any one language, so it lets you check the logic of a subroutine before translating it into code. The source gives pseudocode for fillWithRandomColors(), and the same approach applies to the other two subroutines. The order matters: postcondition first, then algorithm, then code.

Worked example

Writing a postcondition for a new subroutine

A mosaic program needs a subroutine that makes every rectangle in the window the same single color. Describe it by its postcondition before any algorithm.

  1. 1
    State what must be true afterward

    After the subroutine finishes, every rectangle in the mosaic has the same chosen color. This is the postcondition.

  2. 2
    Check it against the main routine

    The main routine only needs to know that the rectangles match this statement afterward. It does not need to know whether the subroutine loops over them or handles them in some other way.

  3. 3
    Only then write the algorithm

    Now describe steps in pseudocode, such as visiting each rectangle and setting its color, and check that those steps really produce the stated postcondition.

Result

The subroutine is defined by a checkable statement about its result, and its pseudocode is verified against that statement.

Where Timing Changes Belong

After running the completed program, the author made one small change. To prevent the animation from running too fast, a delay call was added inside the while loop. The statement is Mosaic.delay(10), which takes an integer argument. Because the call sits in the loop body, it runs once per pass, so the pause applies to every step of the animation rather than only once at startup.

Tracing a Rectangle That Kept Its Old Color

Suppose the finished mosaic shows a rectangle that keeps its old color after the initial fill, even though every other rectangle changed. The question is not what is wrong with the whole program. The better question is which routine owns the step that should have changed that rectangle. Starting from the postcondition makes the search concrete. The fill subroutine is defined as leaving every rectangle changed to a random color, so a leftover old color points at that subroutine's result rather than at the main routine's loop.

What do you think happens?

A rectangle keeps its old color after the initial fill, while the others changed. Which routine should you check first?

  • The main routine's loop, because it controls the animation

  • The Mosaic.delay(10) call, because it changes the animation speed

  • fillWithRandomColors(), because its postcondition says every rectangle changed

Reveal answer
Answer:

fillWithRandomColors(), because its postcondition says every rectangle has been changed to a random color.

The fill subroutine is the owner of that initial change. A leftover old color violates its postcondition, so that is where the search starts. The delay only affects speed and cannot change which color a rectangle ends up with.

Mistakes Beginners Make with Subroutines

  • Common mistake

    Writing the algorithm before deciding what the subroutine must accomplish

    What it looks like

    Starting to write loops for fillWithRandomColors() without first stating that every rectangle must end up with a random color.

    Why it fails

    Without a postcondition there is no clear test for whether the steps are correct.

    Do this instead

    State the postcondition first, then write pseudocode and check it against that statement.

  • Common mistake

    Putting timing changes inside a subroutine's job

    What it looks like

    Adding a delay inside fillWithRandomColors() to slow the animation.

    Why it fails

    Speed belongs to the loop in the main routine, and mixing it into a subroutine blurs what the subroutine is responsible for.

    Do this instead

    Place Mosaic.delay(10) in the loop body of the main routine, where it runs on every pass.

  • Common mistake

    Searching the whole program when one result is wrong

    What it looks like

    Rereading every line when only one rectangle keeps its old color.

    Why it fails

    A wrong result that conflicts with a specific postcondition points to one routine.

    Do this instead

    Identify which subroutine's postcondition is violated, then inspect that routine first.

  • Common mistake

    Forgetting that a subroutine is called from a particular place in the main routine

Practice: Stating a Postcondition

MEDIUM

Using the mosaic program as a model, write the postcondition for randomMove(), the routine that moves the small cursor across the mosaic. Describe what must be true when it finishes, without describing how it moves. Then explain which part of the main routine decides when it runs.

Hints
  • Ask what must be true about the cursor's position when the subroutine ends.
  • Remember that the postcondition says nothing about the steps used to get there.
  • The main routine calls randomMove() inside the loop, so its timing is controlled there.

Key Takeaways

  • A main routine handles the overall flow of a program and delegates specific work to subroutines, which return control to the main routine after finishing.
  • The mosaic program needs three subroutines: fillWithRandomColors() runs once at the start, while changeToRandomColor(int,int) and randomMove() run inside the loop.
  • A subroutine is best described first by its postcondition, the state it must leave true, and only then by an algorithm written in pseudocode and translated into code.
  • Timing changes such as Mosaic.delay(10) belong to the main routine's loop, not to the results of subroutines.
  • When a result is wrong, identify which subroutine's postcondition is violated and start the search there.