Procedural and Structured Programming

Procedural programming organizes instructions into named procedures or functions.

3.1 Procedural programming

Procedural programming organizes instructions into named procedures or functions. Each procedure should have a clear responsibility, inputs, outputs, and documented effects.

calculate_tax and final_price are reusable procedures/functions. Python functions return values, while other languages distinguish procedures that return no value from functions that do.

def calculate_tax(amount: float, rate: float) -> float:
    if amount < 0 or rate < 0:
        raise ValueError("amount and rate must be non-negative")
    return amount * rate

def final_price(amount: float, rate: float) -> float:
    return amount + calculate_tax(amount, rate)

3.2 Top-down decomposition

Break a large problem into smaller units:

Each unit should have clear inputs, outputs, and failure behavior. Decomposition improves testing and makes changes local.

process_order
 ├── validate_order
 ├── calculate_total
 ├── authorize_payment
 ├── reserve_inventory
 └── confirm_order

3.3 Parameters and argument passing

Languages may pass values, references, pointers, or use other parameter semantics. Do not assume that every language's “pass by reference” means the same thing.

Python example:

Output (CPython 3.13.1): Python passes object references by assignment: the function receives a reference to the same list object and mutates it. Rebinding the local name would not rebind the caller's variable.

def add_item(items: list[str], item: str) -> None:
    items.append(item)

cart = ["book"]
add_item(cart, "pen")
print(cart)  # ['book', 'pen']
['book', 'pen']

3.4 Scope and lifetime

Local scope: name visible within a function or block, depending on language.

Module/global scope: name available at a wider program level.

Lexical scope: name resolution follows source-code nesting.

Dynamic scope: name resolution depends on the active call chain in languages or systems that support it.

Lifetime: how long an object or storage location exists.

Prefer local state and explicit parameters over hidden global dependencies.

3.5 Structured programming

The core control structures are:

Structured control flow makes programs easier to understand than arbitrary jumps. break, continue, return, and exceptions can be appropriate, but deeply tangled control flow is harder to review.

3.6 Modules and cohesion

A module should group closely related behavior and hide internal details behind a small interface. Cohesion describes how strongly its responsibilities belong together. Coupling describes dependency between modules. High cohesion and controlled coupling generally improve maintenance.

3.7 Procedural design versus global state

Avoid using global variables as a universal communication channel. Global mutable state creates hidden dependencies and makes tests order-dependent. Prefer explicit inputs, return values, dependency injection, and scoped state.

Exercise

Refactor a 100-line function into smaller functions. Preserve observable behavior, name each responsibility, and write tests for each unit.

Quiz

? In Python, `add_item(cart, "pen")` appends to `items`. Why does the caller's `cart` change?
- Python copies the list into the function. :: No copy is made.
+ The parameter refers to the same list object, and the function mutates it. :: Python passes object references by assignment; a real run printed `['book', 'pen']`.
- Python uses dynamic scope. :: Python uses lexical scope.
- Because `cart` is global. :: It would behave the same if `cart` were local to another function.

? What are the three control structures of structured programming?
+ Sequence, selection, iteration :: Any algorithm can be expressed with these three.
- Goto, label, return :: Structured programming avoids arbitrary jumps.
- Class, object, method :: Those are object-oriented concepts.
- Map, filter, reduce :: Those are functional operations.

? Why is global mutable state a problem in procedural design? (select all that apply)
+ It creates hidden dependencies between procedures. :: Callers cannot see what a procedure reads or changes.
+ It makes tests order-dependent. :: One test's leftover state affects the next.
- It makes programs run slower in every language. :: Performance is not the main problem.
+ It i
…