Full teaching narration is free with Private Starter.Create free account
Back to curriculum
Computer ScienceGrade 10· U.S. National — Common Core & NGSS
Aligned to:U.S. educational frameworks

Decomposing a Program into Functions

Students break a complex program specification into smaller functions, define each function’s inputs and outputs, and trace how the components work together.

Decomposing a Program into Functions

Illustrations are auto-generated and may be placeholders. They can be refreshed to match the narration.

Full teaching narration is included free with a Private Starter account.Create free account

Why Decompose Programs?

A complex program can be difficult to understand, build, test, and repair if all its instructions are placed in one long block. Decomposition means breaking the program into smaller functions, with each function handling a specific task. Imagine a program that registers students for a school event. It must collect student information, check available seats, calculate any fee, save the registration, and send a confirmation. Treating each task as a separate function makes the design easier to manage. Team members can work on different functions, and each function can be tested independently. If confirmations are incorrect, the programmer can inspect the confirmation function instead of searching the entire program. Decomposition supports the engineering practice of turning one complex problem into smaller, solvable problems.

A large event registration program block splits into smaller connected task blocks.
A large event registration program block splits into smaller connected task blocks.Source: Illustrated for this lesson

Identify the Program Requirements

Before defining functions, programmers must identify exactly what the program is required to do. Requirements describe needed behaviors, information, rules, and constraints. For a school event registration program, requirements might state that the program must collect a student ID, allow the student to choose an event, reject registration when no seats remain, calculate a fee based on ticket type, and provide a confirmation number. Constraints may include protecting student information and preventing duplicate registrations. Separate required features from optional ideas, such as displaying event photos. Also identify unusual cases: What happens if the student ID is missing or the payment fails? Clear requirements prevent the team from solving the wrong problem. Each requirement should be specific enough that the finished program can be tested against it.

A requirements board sorts event registration items into required features, constraints, and optional ideas.
A requirements board sorts event registration items into required features, constraints, and optional ideas.Source: Illustrated for this lesson

Divide the Problem into Functions

After listing requirements, group related actions into functions. Each function should have one clear purpose and a descriptive name. In the event registration program, getStudentInfo can collect and validate identifying information. checkSeatAvailability can determine whether the selected event has an open seat. calculateFee can apply ticket prices or discounts. saveRegistration can store an approved registration, and createConfirmation can produce a confirmation number and message. Avoid making functions too broad. A function named handleEverything does not reveal its purpose and would be difficult to test. Also avoid splitting a simple action into unnecessary pieces. A useful test is to complete this sentence: “This function is responsible for one task.” If the sentence requires several unrelated tasks, divide the function again.

A function map shows five focused event registration functions beside one crossed-out overly broad function.
A function map shows five focused event registration functions beside one crossed-out overly broad function.Source: Illustrated for this lesson

Define Inputs, Outputs, and Responsibilities

For every function, define its inputs, output, and responsibility before writing code. Inputs are values the function receives, while the output is the value or result it returns. The responsibility states what the function does and what it does not do. For example, checkSeatAvailability might receive an event ID and the current seat count. Its responsibility is to compare the seat count with the event capacity, and its output is a Boolean value: true if a seat is available or false if the event is full. It should not charge a fee or save student data. Clear definitions create a contract between functions. Other parts of the program can use the output without needing to know every internal step. This also makes test cases easier to design.

A function contract diagram shows data entering and a Boolean result leaving the seat availability function.
A function contract diagram shows data entering and a Boolean result leaving the seat availability function.Source: Illustrated for this lesson

Trace the Flow Between Functions

Functions must work together in the correct order, so programmers trace the flow of data and decisions through the design. In the registration example, getStudentInfo first returns validated student data. The program then sends the chosen event to checkSeatAvailability. If that function returns false, the program displays an event-full message and stops. If it returns true, calculateFee uses the ticket type to return a cost. Next, saveRegistration receives the student data, event ID, and cost, then returns a registration ID. Finally, createConfirmation uses that ID to produce a message. Tracing helps reveal missing data, incorrect order, and paths that have no result. Programmers can perform a desk check by following one sample registration through every arrow and recording each intermediate value.

A flowchart traces a successful registration and a false branch that ends with an event-full message.
A flowchart traces a successful registration and a false branch that ends with an event-full message.Source: Illustrated for this lesson

Review and Refine the Design

A first design is rarely the best design. Review the functions to find missing requirements, repeated work, unclear names, unnecessary dependencies, and unhandled cases. For example, suppose both getStudentInfo and saveRegistration check whether a student ID is valid. That repeated rule could lead to inconsistent results. The design can be refined so validation occurs once in a separate validateStudent function, and later functions use the validated data. Then test the revised design with normal, boundary, and error cases. A normal case might register a student when many seats remain. A boundary case might use the final available seat. An error case might provide a missing student ID. Refinement continues until every requirement is assigned to a function, each data path is clear, and the design handles expected failures safely.

A revised design routes student data through one validation function and shows three test-case cards.
A revised design routes student data through one validation function and shows three test-case cards.Source: Illustrated for this lesson