Program Design and Testing: Plan, Build and Debug Code
Every app on your phone began as an idea that people turned into a working program by planning it, building it in small steps, and testing it until it did what its users needed.
That app is a computing innovation: something new that depends on a program to do its job. Some computing innovations are physical objects, like a robot vacuum or a fitness band. Some are software, like a video editor. Others are ideas that only exist because of computing, like video streaming or online banking.
Why do teams build better programs?
Most real software is built by teams. A team whose members bring different talents and backgrounds makes a better product, because each person notices things the others miss. One teammate is good at layout, another knows the code best, and a third uses a screen reader every day and spots every button that has no label.
Different perspectives also help a team avoid bias. Picture a voice-controlled homework helper tested only by people who speak with the same accent as its developers. It could work perfectly for them and fail for half the school. A team that includes different voices, or that asks different users for feedback, catches the problem before launch.
That is why developers consult the people who will use their program. What users say in conversations, and what developers see them do, shows the purpose of the program from many points of view, and the team can then build something that serves all of those users.
Online tools let a team work together even when its members are not in the same room. Shared documents let everyone write a plan together and comment on each other's ideas. Code-sharing sites such as GitHub let programmers see each other's changes, review them, and suggest fixes.
A common way for two people to work together is pair programming. One person, the driver, types the code. The other, the navigator, reviews each line as it is written, thinks ahead, and spots mistakes. The two swap roles often, so both of them understand the whole program.
Effective teams also practice people skills:
What is a program for, and how does it work?
Every computing innovation has a purpose. It either solves a problem, like a bus-tracking app that tells you when to leave home, or lets people pursue an interest through creative expression, like a beat-making app. Developers who understand the purpose clearly build a better innovation, because they can check every feature against it.
A program is a collection of statements that performs a specific task when a computer runs it. Programs are also called software. A code segment is a smaller collection of statements that is part of a program, such as one function or one loop.
A program has to work for many different inputs and situations, not only the one its author tried first. A calculator that adds 2 + 2 correctly but crashes on 0.5 + 0.25 is not finished.
The behavior of a program is what it does while it runs, and it is usually described by how a user interacts with it. You can describe a program at two levels of detail:
Where do inputs come from, and where do outputs go?
Input is data sent to a computer for a program to process. Output is any data a program sends to a device, such as a screen, a speaker or a motor. Both come in many forms:
| Form | Input example | Output example |
|---|---|---|
| Tactile (touch) | tapping a screen, pressing a controller button | a game controller vibrating |
| Audio | speaking to a voice assistant | a song playing |
| Visual | a phone camera scanning a QR code | a map drawn on the screen |
| Text | typing a search | a text message |
Input can come from a person or from another program. A weather app gets its forecast from a weather service's program, not from you.
Inputs usually affect the output. Output also depends on the program's prior state, the values it has stored. A game that shows "New high score!" compares your score (input) with the best score it saved earlier (state).
Many programs spend most of their time waiting for events. An event is tied to an action, such as a key press, a mouse click, a tap, the program starting, or any other action the program is set up to respond to, and it supplies input data to the program. In event-driven programming, statements run when an event triggers them, instead of running in order from top to bottom. The "jump" code in a game sits idle until you press the jump button.
The demo below fakes a stream of events with a list, so you can watch each event trigger its own handler. Notice that the same tap event produces different output depending on the state.
Tap ignored: press start first Game started Tap! Score is now 10 Tap! Score is now 20 Game over. Final score: 20
A music app starts at volume 5. Write final_volume(events) that takes a list of button events and returns the volume after all of them have been handled:
"up" raises the volume by 1, but never above 10."down" lowers it by 1, but never below 0."mute" sets it to 0.final_volume(["up", "up", "down"]) -> 6How do programmers plan and build a program?
There is more than one way to develop a program. Some developers follow an ordered, planned process; others explore, trying ideas to see what works. Most development processes include these phases:
An iterative process goes around these phases again and again. After each round of feedback, testing or reflection, the team refines and revises the program, and that sometimes means going back to an earlier phase. If testers find the bus app's countdown confusing, the team returns to designing.
An incremental process breaks the problem into smaller pieces and makes sure each piece works before adding it to the whole. You might build the part that downloads bus locations and test it, then add the distance calculation and test again, and so on. Many teams do both: they build in pieces and keep improving each one.
How do you find out what a program needs to do?
Designing a program starts with investigation to find its requirements. Investigation uncovers the program's constraints, the limits it must work within ("it has to run on old phones", "it must be ready by May"), as well as the concerns and interests of the people who will use it. Common ways to investigate:
Requirements describe how the program must function, and they often include the interactions it must offer users, such as "a student can see their next three classes with one tap." A program's specification is the document that defines its requirements.
The design phase plans how to meet the specification. It may include brainstorming, planning and storyboarding the screens, organizing the program into modules and functions, drawing diagrams of the user interface layout, and planning how the program will be tested, all before much code is written.
Check your understanding
Two students are pair programming on a quiz app. Which description matches how pair programming works?
A team is designing an app that helps students sign up for after-school tutoring. Which two activities are ways to investigate what the app must do? Select two answers.
Select 2 answers.
A student builds a weather app by first getting the part that downloads today's temperature to work, then adding a five-day forecast, then adding storm alerts. Each part is tested before the next one is added. Which approach does this describe?
How do you document code and give credit?
Program documentation is a written description of what a code segment, event, procedure or program does and how it was developed. Comments are documentation written into the program itself for people to read. The computer ignores them, so they never change how the program runs. In Python a comment starts with #, and a string placed right under a def line, called a docstring, describes the function.
Document your program throughout development, not only at the end. Notes written along the way help you keep a program correct as it changes, and they help teammates understand code they did not write. Some programming environments, including some block-based ones, do not support comments. In those, you document in another way, such as a separate document or a design journal.
Any code you did not write yourself needs an acknowledgement. That includes code written with a partner, starter code from your teacher, code from a library or an open-source project, and code produced by an AI assistant. The acknowledgement can go in the program's documentation, usually as a comment, and it should name where the code came from or who wrote it.
Age 8 | $5 Age 30 | $10 Age 70 | $6 Return the school-play ticket price in dollars for a person's age.
What kinds of errors can a program have?
There are four kinds of errors to recognize:
ZeroDivisionError and IndexError.The demo triggers one of each. try and except catch each error so the demo keeps going; read more about error messages in Python Error Messages.
Syntax error: invalid syntax. Perhaps you forgot a comma? Run-time error: list index out of range Overflow error: int too large to convert to float Logic error: average([80, 90]) returned 86.0, not 85.0
How do you find and fix a bug?
Effective ways to find and correct errors:
print() calls that show what is happening inside the program.Testing uses defined inputs to check that a program produces the expected outcomes, and the results tell you what to revise. You need the requirements to choose those inputs, because the requirements say what the expected outcomes are. The ticket function above follows the drama club's requirements for its school play: under 12 pays $5, ages 12 to 64 pay $10, and 65 and over pay $6. Those numbers show where bugs like to hide: at the boundaries, where the price changes. Strong tests check both sides of each boundary (11 and 12, 64 and 65). They also check the extremes of the input, the youngest and oldest ages the app accepts, and values one step beyond those extremes, which the app should reject.
Here is a version of ticket_price with a bug in it. The tests find it.
age 0: expected $5 got $5 pass age 11: expected $5 got $5 pass age 12: expected $10 got $10 pass age 64: expected $10 got $10 pass age 65: expected $6 got $10 FAIL age 120: expected $6 got $6 pass
Only the test at exactly 65 fails. Someone who tested ages 30 and 80 would have shipped this bug. After fixing a failing test, run every test again, because a fix can break something that worked before.
When a test fails and you cannot see why, add extra output. The function below should count the scores of 70 or more. It returns 2 for a list that has three passing scores, and the extra print() shows why.
checking index 1: 60 checking index 2: 70 checking index 3: 88 Result: 2 (expected 3)
The output shows the loop starting at index 1, so the first score, 95, is never checked. This is an off-by-one error: a logic error where a loop runs one time too few or one time too many. Changing range(1, len(scores)) to range(len(scores)) fixes it. Delete the extra print() once the bug is gone.
Practice: fix a bug, then write tests
A reading-log app counts how many pages you read. Reading from page 10 to page 12 means you read pages 10, 11 and 12, which is 3 pages. The function below returns 2 instead. Find the off-by-one error and fix it.
pages_read(10, 12) -> 3
pages_read(5, 5) -> 1The drama club's requirements: under 12 pays $5, ages 12 to 64 pay $10, and 65 and over pay $6.
Write price_tests() so it returns a list of test cases, where each test case is a tuple (age, expected_price). The checker runs your tests against a correct ticket_price and against buggy versions that each have one boundary in the wrong place. Your tests pass when:
1. you have at least 4 test cases and every expected price matches the requirements, and
2. every buggy version fails at least one of your tests.
Check your understanding
Mia's game uses a collision-detection procedure that she copied from an open-source project. What is the best way for her to acknowledge it?
The code segment below is meant to display the sum of the values in scores. What happens when it runs?
scores ← [88, 92, 75]total ← 0i ← 0REPEAT 3 TIMES{total ← total + scores[i]i ← i + 1}DISPLAY(total)
The procedure countAtLeast is supposed to return how many values in numList are greater than or equal to target. Which procedure call would reveal that it contains an error?
PROCEDURE countAtLeast(numList, target){count ← 0FOR EACH num IN numList{IF(num > target){count ← count + 1}}RETURN(count)}
A program should accept a quantity from 1 to 10, inclusive, and reject anything else. The tester has already tried 1, 5 and 10. Which two additional inputs best check the behavior one step beyond the extremes of the input? Select two answers.
Select 2 answers.
A video game stores a player's coins in a value that can hold whole numbers from 0 to 65,535. A player with 65,535 coins picks up one more, and the coin counter shows 0. Which kind of error is this?
Exam topics: 1.1 Collaboration (CRD-1.A, CRD-1.B, CRD-1.C), 1.2 Program Function and Purpose (CRD-2.A, CRD-2.B, CRD-2.C, CRD-2.D), 1.3 Program Design and Development (CRD-2.E, CRD-2.F, CRD-2.G, CRD-2.H), 1.4 Identifying and Correcting Errors (CRD-2.I, CRD-2.J)