By the end of this chapter, you will be able to:
These skills will help you build reliable and well-organized software that works smoothly in real-world applications.
Implementing modular programming is essential in developing efficient, maintainable, and scalable software solutions across all professional fields. In Kenya's diverse sectors such as healthcare, education, finance, and agriculture, structured programming enhances code clarity and facilitates collaboration among teams. This chapter focuses on the creation and management of subroutines, a cornerstone of modular programming, enabling developers to break down complex problems into manageable components that align with user needs and organizational goals.
A subroutine is a named, self-contained block of code that performs one specific task and can be called from any point in a program. Creating subroutines lets a programmer split a large problem into small modules, reuse tested code, and find faults quickly. In this unit the examples are written in C, but the same ideas apply to any structured language: for instance, a stock-control program for a hardware shop may have separate subroutines to add stock, price an item and print a receipt.
Subroutines improve the quality of software and the productivity of the people who write it. A program built from well-named subroutines is shorter, easier to read and easier to change than one long block of statements.
Modularity means dividing a program into separate modules, each handling one distinct function. The programmer can design, write and test one module at a time. For example, a hotel booking program may use separate subroutines for checking room availability, calculating the bill and printing a confirmation, so each can be developed independently.
A subroutine is written once and called as many times as needed, in the same program or in other projects. This removes duplicate code and saves development time. A billing subroutine that calculates a line total from a unit price and a quantity can be reused in a point-of-sale program and in a quotation program for the same shop.
When code is modular, an error is isolated inside one subroutine, so it is found and fixed in one place. If the rule for calculating a discount changes, only the discount subroutine is edited and every part of the program that calls it receives the correction. Other subroutines, such as the one that prints receipts, are not touched.
Meaningful subroutine names such as calculateDiscount or printReceipt make the main program read almost like a plan of the solution. Several programmers can work on the same project at once, each owning different subroutines, with fewer misunderstandings about who is responsible for what.
Each subroutine can be tested on its own with chosen inputs before it is joined to the rest of the program. In a payroll program, a tax-calculation subroutine can be checked with many salary values before it is used in the full payroll run. Faults are therefore found early, when they are cheapest to correct.
Good subroutine design begins with what the user needs the software to do and ends with small, reliable components that deliver it. Each design decision below links a user requirement to a clear piece of code.
List the tasks the user needs the software to perform, then give each task to a subroutine. For a hotel management program, the user needs to book rooms, produce bills and record customer feedback, so subroutines such as bookRoom, calculateBill and recordFeedback are identified from those needs.
A subroutine must have well-defined inputs (parameters) and outputs (return value). A loan-repayment subroutine for a lending program, for example, states that it needs the loan amount, the monthly rate and the number of months, and that it returns the monthly instalment or an error code.
Each subroutine should do one well-defined task. A records program should have separate subroutines for entering data, retrieving a record and printing a report, instead of one large subroutine that does all three. Small, focused subroutines are easier to name, test and reuse.
A subroutine should check its inputs and handle faults gracefully. A withdrawal subroutine in a wallet program should check that the amount is positive and not larger than the balance before it subtracts anything, and should return a clear error code when the check fails.
Descriptive names and short comments tell other programmers what a subroutine does and how to call it. In a crop-planning program, a subroutine named calculateFertilizerNeeds shows its purpose, and a comment above it lists the meaning of each parameter and of the value returned.
Functions and subprograms (procedures) are the two forms of subroutine used in structured programming. Both package a task under a name, but they differ in whether they hand a value back to the code that called them. Choosing the right form for each task keeps the program clear.
A function performs a calculation or operation and returns a value to the calling program, so it can be used inside an expression. For example, an interest function takes principal, rate and time and returns the interest amount. A subprogram, also called a procedure, carries out a task without necessarily returning a value, often changing program state or producing output. A procedure that updates employee records in a payroll program or prints a report is an example. In C, a procedure is written as a function whose return type is void.
| Feature | Function | Procedure (subprogram) |
|---|---|---|
| Returns a value | Yes, through a return statement | No, return type is void in C |
| Used in an expression | Yes, for example total = lineTotal(p, q); | No, called as a statement, for example printReceipt(); |
| Typical purpose | Calculate and give back a result | Perform an action such as displaying, storing or updating |
| Example | double calculateInterest(double p, double r, double t) | void printHeader(void) |
In built functions (library functions) are ready-made subroutines supplied with the programming language and its standard library. The programmer does not write their bodies; they include the correct header file and call them by name. This saves time and gives tested, efficient code, for example sqrt to find a square root or strlen to count the characters in a text.
| Function | Header file | Use |
|---|---|---|
| printf | stdio.h | Displays formatted output on the screen |
| scanf | stdio.h | Reads formatted input from the keyboard |
| sqrt | math.h | Returns the square root of a number |
| pow | math.h | Raises a number to a power |
| fabs | math.h | Returns the absolute value of a decimal number |
| strlen | string.h | Returns the number of characters in a string |
| strcmp | string.h | Compares two strings |
| toupper | ctype.h | Converts a letter to upper case |
| abs | stdlib.h | Returns the absolute value of an integer |
The example below uses two in built functions in one program:
#include <stdio.h>
#include <math.h>
#include <string.h>
int main(void)
{
double area = 78.5;
double radius = sqrt(area / 3.14159);
char label[] = "Invoice";
printf("Radius: %.2f\n", radius);
printf("Length of label: %zu\n", strlen(label));
return 0;
}
Calculation:
radius = sqrt(area ÷ 3.14159)
radius = sqrt(78.5 ÷ 3.14159) = sqrt(24.9873)
radius = 4.9987, displayed as 5.00
The label "Invoice" has 7 characters, so strlen returns 7. On some systems the maths library must be linked with the option -lm when compiling.
A user defined function is a subroutine written by the programmer to solve a task that no in built function covers. It has a return type, a name, a parameter list in brackets and a body in braces. Writing user defined functions turns repeated logic, such as pricing an order line, into a single named unit.
The steps to create and use a user defined function are:
#include <stdio.h>
double lineTotal(double unitPrice, int quantity); /* prototype */
int main(void)
{
double total = lineTotal(250.0, 4);
printf("Total: Ksh %.2f\n", total);
return 0;
}
double lineTotal(double unitPrice, int quantity) /* definition */
{
return unitPrice * quantity;
}
Calculation:
total = unit price × quantity
total = 250.00 × 4
total = Ksh 1000.00
Parameters are the variables listed in a function's brackets that receive the data the function works on. They form the function's input interface, so the calling code and the function agree on what is exchanged. A shop program that updates stock might pass the product code and the quantity sold as parameters.
#include <stdio.h>
void applyDiscount(double *price, double percent)
{
*price = *price - (*price * percent / 100.0);
}
int main(void)
{
double price = 2000.0;
applyDiscount(&price, 10.0);
printf("New price: Ksh %.2f\n", price);
return 0;
}
Calculation:
new price = price − (price × percent ÷ 100)
new price = 2000.00 − (2000.00 × 10 ÷ 100)
new price = 2000.00 − 200.00 = Ksh 1800.00
Because price was passed by reference, the variable in main now holds 1800.00. Had it been passed by value, main would still hold 2000.00.
The return type, written before the function name, states what kind of value the function gives back to the caller. The return statement sends that value back and ends the function. Choosing the correct return type ensures the caller receives the result in a usable form, for example a decimal amount for money or a whole number for a count.
| Return type | Meaning | Example |
|---|---|---|
| int | Returns a whole number, also used for status codes (1 for true, 0 for false) | int isValidQuantity(int q) |
| double | Returns a decimal number | double calculateInterest(double p, double r, double t) |
| char | Returns a single character | char gradeFromMarks(int marks) |
| void | Returns nothing; the function only performs an action | void printReceipt(void) |
| pointer (for example int *) | Returns the address of data | int *findLargest(int *list, int size) |
A function can return only one value directly. To give back several results, the programmer passes pointers as parameters or returns a structure. The example below returns a status code that the caller can test:
int isValidQuantity(int quantity)
{
if (quantity > 0 && quantity <= 1000)
return 1; /* valid */
return 0; /* invalid */
}
A call such as isValidQuantity(50) returns 1, while isValidQuantity(-3) returns 0. The returned value must match the declared type; a void function must not return a value.
Variables declared inside a function are local: they exist only while the function runs and cannot be seen by other functions. This prevents one function from accidentally changing data that belongs to another, which is important in a program that calculates several students' marks or several customers' bills in turn. A global variable, declared outside all functions, is visible everywhere and should be used sparingly because any function can change it.
A subroutine library is a collection of tested subroutines for tasks that many programs need, kept in one place so that every project can use the same code. It improves development speed and consistency because programmers stop rewriting the same validation, formatting and calculation routines. In C, a library is normally made of a header file (.h) holding the declarations and a source file (.c) holding the definitions.
Study the programs being developed and list operations that appear in more than one of them. Data validation, date formatting, percentage calculation, rounding and writing messages to a log file are common examples for a billing program, a booking program and a payroll program alike. Only tasks that are genuinely shared should enter the library.
Use uniform parameter orders, naming style and return conventions across the library. For instance, every validation function can return 1 for valid and 0 for invalid, and every function can use camelCase names such as isPositive and percentageOf. Programmers then predict how an unfamiliar library subroutine behaves.
Each library subroutine should do one job, be tested independently and carry a comment that states its purpose, parameters, return value and an example call. For a hotel booking library, functions for room availability, booking confirmation and cancellation are each documented with a usage example.
Keep the library under version control so every change is recorded and earlier versions can be recovered. A new interest calculation method can be added as a new function while the old one remains for programs that still depend on it. Changes should never alter the behaviour of an existing function without a new version number.
Decide who may use the library and who may change it. Programmers may be allowed to read and link the library freely, while only designated team members approve changes, which protects the quality of code that many programs depend on.
| Name | Specification | Use |
|---|---|---|
| utils.h | Header file with include guards and function prototypes | Declares the library functions so other files can call them |
| utils.c | Source file that includes utils.h and defines each function | Holds the code of the library subroutines |
| main.c | Program source that includes utils.h | Calls the library functions |
| gcc compiler | C compiler run from the command line | Compiles the source files and links them into one program |
utils.h:
#ifndef UTILS_H
#define UTILS_H
int isPositive(double value);
double percentageOf(double amount, double percent);
#endif
utils.c:
#include "utils.h"
int isPositive(double value)
{
return value > 0;
}
double percentageOf(double amount, double percent)
{
return amount * percent / 100.0;
}
main.c:
#include <stdio.h>
#include "utils.h"
int main(void)
{
double amount = 5000.0;
if (isPositive(amount))
printf("16 percent of Ksh %.2f is Ksh %.2f\n", amount, percentageOf(amount, 16.0));
return 0;
}
Calculation:
result = amount × percent ÷ 100
result = 5000.00 × 16 ÷ 100
result = Ksh 800.00
Writing a subroutine from a specification means turning a written description of inputs, outputs and rules into working, tested code. Following a fixed sequence of activities prevents omissions and keeps the result aligned with what the user asked for.
Read the requirements carefully and identify the purpose, the inputs, the outputs and any constraints. A retail business may specify a billing subroutine that calculates a discount from the customer's loyalty status and the purchase amount. Any unclear point must be clarified before coding starts.
Choose the name, parameters and return type that match the specification. A subroutine calculating fertiliser dosage for a crop-planning program might accept the soil type and crop variety as parameters and return the quantity in kilograms as a double.
Implement the logic with suitable selection, calculation and error handling. A tax subroutine might include conditional checks for exemptions before applying a rate. Keep the body short and the variable names meaningful.
Run test cases covering valid data, boundary values and invalid data, then compare the actual output with the expected output. An examination-marks subroutine, for example, is tested with a normal mark, the lowest and highest allowed marks, and a negative mark. Any mismatch is traced and corrected.
Add the subroutine to the main program, call it with real data and document its purpose, parameters, return value and limits. A booking subroutine in a hotel management program is documented so that other programmers know how to call it.
Review the working subroutine for repeated calculations, unclear names and unnecessary complexity, and improve them without changing the result. A loan-approval subroutine that calculates the same value twice can store it in a local variable and use it twice, which makes the code faster and easier to read.
Specification: write a function amountPayable that takes the purchase amount (double) and the membership status (int, 1 for member, 0 for non-member). A member whose purchase is Ksh 5000 or more receives a 5 percent discount. Anyone else pays the full amount. A negative purchase amount returns -1.
double amountPayable(double purchase, int isMember)
{
if (purchase < 0)
return -1.0;
if (isMember && purchase >= 5000.0)
return purchase - (purchase * 0.05);
return purchase;
}
| Test | Purchase (Ksh) | Member | Expected result |
|---|---|---|---|
| Discount applies | 6000 | 1 | 5700.00 |
| Boundary value | 5000 | 1 | 4750.00 |
| Below threshold | 4000 | 1 | 4000.00 |
| Non-member | 6000 | 0 | 6000.00 |
| Invalid amount | -100 | 1 | -1 |
Calculation for the first test:
payable = purchase − (purchase × 0.05)
payable = 6000.00 − (6000.00 × 0.05)
payable = 6000.00 − 300.00 = Ksh 5700.00
Explain five benefits of using subroutines in software development, providing examples from Kenyan institutions. (10 marks)
Describe the steps involved in designing a subroutine that meets user needs, emphasizing the importance of each step. (12 marks)
Differentiate between functions and subprograms, illustrating your answer with examples relevant to county government applications. (8 marks)
Outline how to design and implement a subroutine library for common tasks, highlighting key considerations for maintenance and access control. (10 marks)
Given a specification for a subroutine to calculate employee allowances based on grade and years of service, describe the process of creating this subroutine, including testing and documentation. (15 marks)
Create a free account to open more of this chapter.
Free: practical guides, quick cards, workplace scenarios and more.
Create a free accountThis chapter explored the implementation of modular programming through the creation of subroutines, emphasizing their benefits such as modularity and reusability. It covered how to design subroutines that effectively meet user needs, distinguishing between built-in functions and user-defined functions, and the development of subroutine libraries to handle common programming tasks. The application of data structures was demonstrated in the context of looping through arrays within functions, highlighting the use of various looping control structures including for loops, while loops, and do-while loops. Techniques and tools for debugging programs were examined to equip learners with strategies for identifying and correcting errors. Finally, the process of compiling programs was discussed, including exercises using programs with intentional errors to understand and interpret compiler error messages. This comprehensive approach provides a solid foundation for writing, testing, and maintaining modular, efficient, and error-free code.
Type: Individual
| Tools & Equipment | Materials |
|---|---|
| Computer with Visual Studio Community Edition installed | .NET SDK (latest stable version) |
| Visual Studio Code with C# extension | Printing Papers |
| Snipping Tool or Screenshot Software |
| S/N | Item | Quantity |
|---|---|---|
| 1 | Computer with Visual Studio Community Edition installed | 1 Pc per Candidate |
| 2 | .NET SDK (latest stable version) | 1 Pc per Candidate |
| 3 | Visual Studio Code with C# extension | 1 Pc per Candidate |
| 4 | Snipping Tool or Screenshot Software | 1 Pc per Candidate |
| 5 | Printing Papers (for printouts if needed) | 10 Sheets per Candidate |
| Items to be Evaluated | Marks Available | Marks Obtained | Comments |
|---|---|---|---|
| TASK 1: Project Setup and Folder Organization | |||
| Created a folder named "ModularProgrammingApp" on the desktop (Award 1 mark if folder created correctly) | 1 | ||
| Created a new C# Console Application project with a meaningful name (Award 1 mark if project named appropriately) | 1 | ||
| Project compiles and runs successfully on first attempt (Award 1 mark if no compilation or runtime errors) | 1 | ||
| Sub-Total | 3 | ||
| TASK 2: Implementation of Basic Subroutines | |||
| Defined a subroutine to calculate the sum of five numbers (Award 3 marks for correct method definition, parameters, and return type) | 3 | ||
| Defined a subroutine to calculate the product of five numbers (Award 3 marks for correct method definition, parameters, and return type) | 3 | ||
| Defined a subroutine to calculate the average of five numbers (Award 3 marks for correct method definition, parameters, and return type) | 3 | ||
| Each subroutine uses appropriate parameter passing and returns correct results (Award 3 marks if parameter passing and return values are implemented correctly) | 3 | ||
| Correct syntax and no compilation errors in subroutine implementations (Award 2 marks for error-free code) | 2 | ||
| Sub-Total | 14 | ||
| TASK 3: User Input and Validation | |||
| Prompted user clearly to enter five numeric values with numbered messages (Award 2 marks for clear prompts) | 2 | ||
| Declared variables correctly to store numeric inputs (int or double) (Award 2 marks for appropriate variable declarations) | 2 | ||
| Read user inputs from console and converted to numeric types (Award 2 marks for correct input reading and conversion) | 2 | ||
| Implemented validation to handle invalid numeric entries gracefully (Award 4 marks for input validation with error handling) | 4 | ||
| Sub-Total | 10 | ||
| TASK 4: Display and Formatting of Results | |||
| Displayed calculated sum, product, and average using string interpolation (Award 3 marks for correct use of string interpolation syntax) | 3 | ||
| Output messages are clear, labeled, and easy to read (Award 2 marks for clear and user-friendly output) | 2 | ||
| Displayed a final thank you message at program termination (Award 1 mark for final message) | 1 | ||
| Sub-Total | 6 | ||
| TASK 5: Code Documentation and Style | |||
| Header comment with candidate name, date, and program purpose (Award 2 marks for complete and accurate header comment) | 2 | ||
| Inline comments explaining major steps and logic (Award 3 marks for meaningful inline comments) | 3 | ||
| Consistent code indentation and spacing throughout the program (Award 2 marks for consistent formatting) | 2 | ||
| Avoided code repetition by reusing subroutines appropriately (Award 2 marks for modular code and avoiding redundancy) | 2 | ||
| Sub-Total | 9 | ||
| TASK 6: Testing and Screenshots | |||
| Tested the application with at least two different sets of valid inputs (Award 2 marks for thorough testing) | 2 | ||
| Application handles unexpected or invalid inputs without crashing (Award 2 marks for robust error handling) | 2 | ||
| Provided at least five screenshots showing code and output at key steps (Award 2 marks for clear and relevant screenshots) | 2 | ||
| Sub-Total | 6 | ||
| PRODUCT CHECKLIST | |||
| Console application correctly calculates and displays sum, product, and average for five inputs (Award 6 marks for accurate results matching expected calculations) | 6 | ||
| Subroutines implemented correctly with proper parameter passing and return values (Award 5 marks for correct modular programming structure) | 5 | ||
| User interface is clear, prompts are unambiguous, and outputs well formatted (Award 4 marks for user-friendly interface) | 4 | ||
| Code is well documented, readable, and follows good programming style (Award 5 marks for code quality and documentation) | 5 | ||
| Sub-Total | 20 | ||
| GRAND TOTAL | 68 | ||
Type: Individual
| Tools & Equipment | Materials |
|---|---|
| Visual Studio Community Edition | Computer with Windows 10 or later |
| .NET SDK | Snipping Tool or Screenshot Software |
| Keyboard and Mouse | Word Processor |
| S/N | Item | Quantity |
|---|---|---|
| 1 | Visual Studio Community Edition | 1 License per Candidate |
| 2 | .NET SDK | 1 Installation per Candidate |
| 3 | Computer with Windows 10 or later | 1 per Candidate |
| 4 | Keyboard and Mouse | 1 set per Candidate |
| 5 | Snipping Tool or Screenshot Software | 1 per Candidate |
| 6 | Word Processor (e.g., Microsoft Word) | 1 per Candidate |
| Items to be Evaluated | Marks Available | Marks Obtained | Comments |
|---|---|---|---|
| TASK 1: Setup and Project Creation | |||
| Created a folder named "ModularProgram" on the desktop (Award 1 mark if folder created correctly) | 1 | ||
| Created a new C# Console Application project named "ModularProgram" (Award 1 mark for correct project name, 1 mark for successful compilation) | 2 | ||
| Sub-Total | 3 | ||
| TASK 2: Implement Subroutines | |||
| Created subroutine for input: prompts user and reads multiple numeric values (Award 1 mark each for prompt clarity, correct variable types, and input reading) | 3 | ||
| Created subroutine for processing: calculates sum, product, and average (Award 1 mark each for correct calculations and use of subroutine) | 4 | ||
| Created subroutine for output: displays results using string interpolation (Award 1 mark each for correct interpolation, clear messages, and neat formatting) | 3 | ||
| Main method calls the subroutines correctly and is concise (Award 2 marks if main method only contains calls to subroutines and minimal code) | 2 | ||
| Sub-Total | 12 | ||
| TASK 3: Code Quality and Documentation | |||
| Header comment includes candidate name, date, and program purpose (Award 2 marks if all header details are present and clear) | 2 | ||
| Inline comments explain major code sections and logic (Award 1 mark each for comments in input, processing, and output subroutines) | 3 | ||
| Code uses consistent indentation and spacing throughout (Award 2 marks for consistent and readable code style) | 2 | ||
| Avoids code repetition by proper use of subroutines (Award 3 marks if no duplicated code and subroutines are reused appropriately) | 3 | ||
| Sub-Total | 10 | ||
| TASK 4: Testing and Validation | |||
| Tested application with at least two different sets of numeric inputs (Award 1 mark per test with correct output) | 2 | ||
| Application handles invalid input gracefully without crashing (Award 2 marks if input validation and error handling implemented) | 2 | ||
| Saved screenshots of code and output for each test (Award 1 mark each for code and output screenshots) | 2 | ||
| Sub-Total | 6 | ||
| PRODUCT CHECKLIST | |||
| Program runs without errors and produces correct outputs for inputs (Award 5 marks if all outputs are accurate and program is stable) | 5 | ||
| Subroutines demonstrate modularity and reusability clearly (Award 5 marks if subroutines are properly defined, used, and improve code structure) | 5 | ||
| Sub-Total | 10 | ||
| GRAND TOTAL | 41 | ||
At the start of this chapter we promised you would be able to:
Tick each one you can genuinely do.
So, are you there yet?
You're competent when you can confidently do 50% or more of what this chapter promised.
Sign in to record how you're doing.