Subroutine Design
A main routine handles the overall flow of a program and hands specific work to subroutines.
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.
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.
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
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.
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.
- 1State what must be true afterward
After the subroutine finishes, every rectangle in the mosaic has the same chosen color. This is the postcondition.
- 2Check 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.
- 3Only 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.
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?
Reveal 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 likeStarting to write loops for fillWithRandomColors() without first stating that every rectangle must end up with a random color.
Why it failsWithout a postcondition there is no clear test for whether the steps are correct.
Do this insteadState 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 likeAdding a delay inside fillWithRandomColors() to slow the animation.
Why it failsSpeed belongs to the loop in the main routine, and mixing it into a subroutine blurs what the subroutine is responsible for.
Do this insteadPlace 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 likeRereading every line when only one rectangle keeps its old color.
Why it failsA wrong result that conflicts with a specific postcondition points to one routine.
Do this insteadIdentify 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
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, whilechangeToRandomColor(int,int)andrandomMove()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.