Why Procedures and Functions Matter
As algorithms grow, they become harder to read and harder to change safely. Procedures and functions solve this by letting you decompose a solution into small, named blocks that each do one job. You then read the main algorithm like a high-level story, while the details live in well-named helpers.
Procedure: a named block that performs an action (often modifies outputs or state) and does not return a single value. Function: a named block that computes and returns a value.
1) When to Extract a Function or Procedure
Extract repeated logic
If you copy/paste the same steps in multiple places, extract them. This reduces bugs because you fix the logic once.
- Signal: you see the same 5–10 lines repeated with small variations.
- Action: turn the repeated steps into a function/procedure with parameters for the varying parts.
Apply single responsibility
A good function/procedure has one clear purpose. If a block of pseudocode is doing multiple jobs (e.g., validating inputs, computing totals, and formatting output), split it into separate named blocks.
- Signal: you need to use “and” to describe what the block does (e.g., “validate and compute and print”).
- Action: create one helper per responsibility, then call them from the main algorithm.
Extract to improve readability
Even if logic is not repeated, extracting can make the main algorithm easier to scan. If a section is detailed but conceptually simple (e.g., “format receipt lines”), move it into a helper with a descriptive name.
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
Extract to isolate change
If a rule is likely to change (tax rate rules, discount rules, validation rules), isolate it in a function so changes do not ripple across the entire algorithm.
2) Defining Parameters and Return Values
Parameters: what the helper needs
Parameters are the inputs to a procedure/function. Choose parameters so the helper is usable in more than one place, but not so generic that it becomes unclear.
- Prefer passing needed data explicitly rather than relying on hidden global variables.
- Keep parameter lists short. If you need many related values, consider passing a single record/object (e.g.,
ItemorOrder). - Name parameters by role:
item,catalog,taxRate,quantity.
Return values: what the helper produces
A function returns a value. Use a function when the main algorithm needs a computed result (e.g., total cost). Use a procedure when the main algorithm needs an action performed (e.g., printing, appending to a list), or when multiple outputs are produced.
When you need multiple results, you have common options:
- Return a record (e.g.,
{isValid, message}). - Use output parameters (e.g.,
ValidateItem(item, OUT errorMessage)). - Split into smaller functions that each return one thing.
Example: signature sketches
FUNCTION ComputeLineTotal(unitPrice, quantity) RETURNS numberFUNCTION ValidateItem(item, catalog) RETURNS ValidationResultPROCEDURE AppendReceiptLine(INOUT lines, text)3) Documenting Preconditions and Postconditions (Short Notes)
Preconditions and postconditions are short notes that clarify what must be true before a helper runs and what will be true after it finishes. They prevent misuse and make testing easier.
| Type | Meaning | Example |
|---|---|---|
| Precondition | Assumption required for correct behavior | quantity > 0 |
| Postcondition | Guaranteed result if preconditions were met | returns total >= 0 |
Keep these notes brief and close to the helper definition.
FUNCTION ComputeLineTotal(unitPrice, quantity) RETURNS number NOTE Pre: unitPrice >= 0, quantity is an integer and quantity > 0 NOTE Post: returns unitPrice * quantityFUNCTION ValidateItem(item, catalog) RETURNS ValidationResult NOTE Pre: item has fields {sku, quantity}; catalog maps sku -> unitPrice NOTE Post: result.isValid is TRUE only if sku exists and quantity > 04) Calling Helpers While Keeping the Main Algorithm Readable
The main algorithm should read like a checklist of business steps. Aim for calls that communicate intent:
validation = ValidateItem(item, catalog)is clearer than repeating the validation rules inline.total = ComputeTotal(orderItems, catalog, taxRate)communicates purpose immediately.receiptText = FormatReceipt(orderItems, catalog, taxRate)keeps formatting details out of the main flow.
Practical guidelines:
- Name helpers with verbs:
Validate...,Compute...,Format.... - Keep main steps at one level of abstraction: avoid mixing “business steps” with “string concatenation details” in the same block.
- Let helpers hide complexity, but not meaning: a helper name should explain what it does without needing to open it.
Case Study: Refactoring an Order-Processing Algorithm
Scenario: You receive a small list of order items. Each item has a sku and quantity. A catalog provides unit prices by sku. You must validate items, compute totals (subtotal, tax, grand total), and produce a receipt.
Data model used in this case study
OrderItem: { sku, quantity }catalog: mapping sku -> unitPricetaxRate: number (e.g., 0.07)ValidationResult: { isValid, message }Totals: { subtotal, tax, grandTotal }
Before refactoring: one long block (harder to read)
This version preserves correct logic, but mixes validation, computation, and formatting in one place.
PROCEDURE ProcessOrder(orderItems, catalog, taxRate) subtotal = 0 receiptLines = empty list FOR EACH item IN orderItems IF item.sku NOT IN catalog ADD "ERROR: Unknown SKU " + item.sku TO receiptLines ELSE IF item.quantity <= 0 ADD "ERROR: Invalid quantity for " + item.sku TO receiptLines ELSE unitPrice = catalog[item.sku] lineTotal = unitPrice * item.quantity subtotal = subtotal + lineTotal ADD item.sku + " x" + ToText(item.quantity) + " @ " + Money(unitPrice) + " = " + Money(lineTotal) TO receiptLines END IF END FOR tax = subtotal * taxRate grandTotal = subtotal + tax ADD "Subtotal: " + Money(subtotal) TO receiptLines ADD "Tax: " + Money(tax) TO receiptLines ADD "Total: " + Money(grandTotal) TO receiptLines FOR EACH line IN receiptLines OUTPUT line END FORStep-by-step refactoring plan
- Extract validation into
ValidateItembecause the rules are a distinct responsibility and likely to change. - Extract total computation into
ComputeTotalto isolate arithmetic and make it testable. - Extract receipt formatting into
FormatReceiptso the main algorithm does not contain string-building details. - Rewrite the main procedure to call these helpers in a readable sequence.
After refactoring: modular version (same overall logic)
Helper 1: ValidateItem
FUNCTION ValidateItem(item, catalog) RETURNS ValidationResult NOTE Pre: item has {sku, quantity}; catalog maps sku -> unitPrice NOTE Post: isValid TRUE only if sku exists and quantity > 0 result = new ValidationResult IF item.sku NOT IN catalog result.isValid = FALSE result.message = "Unknown SKU " + item.sku RETURN result END IF IF item.quantity <= 0 result.isValid = FALSE result.message = "Invalid quantity for " + item.sku RETURN result END IF result.isValid = TRUE result.message = "OK" RETURN resultHelper 2: ComputeTotal
FUNCTION ComputeTotal(orderItems, catalog, taxRate) RETURNS Totals NOTE Pre: taxRate >= 0; catalog contains prices for valid items NOTE Post: returns totals where grandTotal = subtotal + tax subtotal = 0 FOR EACH item IN orderItems validation = ValidateItem(item, catalog) IF validation.isValid = TRUE unitPrice = catalog[item.sku] subtotal = subtotal + (unitPrice * item.quantity) END IF END FOR tax = subtotal * taxRate totals = new Totals totals.subtotal = subtotal totals.tax = tax totals.grandTotal = subtotal + tax RETURN totalsHelper 3: FormatReceipt
FUNCTION FormatReceipt(orderItems, catalog, taxRate) RETURNS list of text NOTE Pre: orderItems is a list; catalog maps sku -> unitPrice; taxRate >= 0 NOTE Post: returns receipt lines including errors and totals lines = empty list subtotal = 0 FOR EACH item IN orderItems validation = ValidateItem(item, catalog) IF validation.isValid = FALSE ADD "ERROR: " + validation.message TO lines ELSE unitPrice = catalog[item.sku] lineTotal = unitPrice * item.quantity subtotal = subtotal + lineTotal ADD item.sku + " x" + ToText(item.quantity) + " @ " + Money(unitPrice) + " = " + Money(lineTotal) TO lines END IF END FOR tax = subtotal * taxRate grandTotal = subtotal + tax ADD "Subtotal: " + Money(subtotal) TO lines ADD "Tax: " + Money(tax) TO lines ADD "Total: " + Money(grandTotal) TO lines RETURN linesMain algorithm: readable orchestration
PROCEDURE ProcessOrder(orderItems, catalog, taxRate) NOTE Pre: orderItems is provided; catalog contains unit prices; taxRate >= 0 receiptLines = FormatReceipt(orderItems, catalog, taxRate) FOR EACH line IN receiptLines OUTPUT line END FORWhat stayed the same (logic) vs. what improved (structure)
- Same logic: items are validated; valid items contribute to subtotal; errors are reported; totals are appended; receipt is output.
- Improved structure: validation rules are centralized; total computation can be tested separately; receipt formatting is isolated; the main procedure is short and clear.
Optional refinement: avoid duplicated subtotal logic
In the refactored version, both ComputeTotal and FormatReceipt compute subtotal. If you want a single source of truth, you can compute totals once and pass them into formatting.
FUNCTION FormatReceiptWithTotals(orderItems, catalog, totals) RETURNS list of text NOTE Pre: totals already computed from the same orderItems and catalog NOTE Post: returns receipt lines including totals lines = empty list FOR EACH item IN orderItems validation = ValidateItem(item, catalog) IF validation.isValid = FALSE ADD "ERROR: " + validation.message TO lines ELSE unitPrice = catalog[item.sku] lineTotal = unitPrice * item.quantity ADD item.sku + " x" + ToText(item.quantity) + " @ " + Money(unitPrice) + " = " + Money(lineTotal) TO lines END IF END FOR ADD "Subtotal: " + Money(totals.subtotal) TO lines ADD "Tax: " + Money(totals.tax) TO lines ADD "Total: " + Money(totals.grandTotal) TO lines RETURN linesPROCEDURE ProcessOrder(orderItems, catalog, taxRate) totals = ComputeTotal(orderItems, catalog, taxRate) receiptLines = FormatReceiptWithTotals(orderItems, catalog, totals) FOR EACH line IN receiptLines OUTPUT line END FOR