Free Ebook cover Pseudocode Mastery for Beginners

Pseudocode Mastery for Beginners

New course

10 pages

Procedures and Functions: Modular Pseudocode for Reusable Logic

Capítulo 7

Estimated reading time: 8 minutes

+ Exercise

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 App

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., Item or Order).
  • 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 number
FUNCTION ValidateItem(item, catalog) RETURNS ValidationResult
PROCEDURE 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.

TypeMeaningExample
PreconditionAssumption required for correct behaviorquantity > 0
PostconditionGuaranteed result if preconditions were metreturns 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 * quantity
FUNCTION 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 > 0

4) 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 -> unitPrice
  • taxRate: 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 FOR

Step-by-step refactoring plan

  1. Extract validation into ValidateItem because the rules are a distinct responsibility and likely to change.
  2. Extract total computation into ComputeTotal to isolate arithmetic and make it testable.
  3. Extract receipt formatting into FormatReceipt so the main algorithm does not contain string-building details.
  4. 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 result

Helper 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 totals

Helper 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 lines

Main 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 FOR

What 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 lines
PROCEDURE ProcessOrder(orderItems, catalog, taxRate)    totals = ComputeTotal(orderItems, catalog, taxRate)    receiptLines = FormatReceiptWithTotals(orderItems, catalog, totals)    FOR EACH line IN receiptLines        OUTPUT line    END FOR

Now answer the exercise about the content:

In modular pseudocode, what is the main benefit of extracting validation, total computation, and receipt formatting into separate helpers for an order-processing algorithm?

You are right! Congratulations, now go to the next page

You missed! Try again.

Separating validation, computation, and formatting keeps the main procedure readable and isolates likely changes (like validation rules). Centralizing repeated logic in helpers also reduces bugs and improves testability.

Next chapter

Translating Everyday Processes into Pseudocode

Arrow Right Icon
Download the app to earn free Certification and listen to the courses in the background, even with the screen off.