Week 04 · lesson

Programs Have State

A computer program looks static when you read the file. The interesting part begins when it runs.

At runtime, values enter the program, variables change, conditions are evaluated, functions are called, and outputs are produced. That changing collection of values is the program's state.

If you can trace state, you can explain behavior. If you cannot trace state, debugging turns into guessing.

Start with a tiny system

Consider this fictional classroom access checker:

role = "student"
lab_open = True

if role == "student" and lab_open:
    decision = "allow-lab"
else:
    decision = "deny"

print(decision)

Do not rush to the output. Model the system first.

ElementMeaning
roleinput-like state describing the user category
lab_openstate describing whether the classroom lab is available
if conditiondecision rule
decisionstate produced by the rule
print()observable output

The program is small, but the same reasoning applies to a web service, robot controller, authentication system, or monitoring script.

An execution trace is evidence

Run the logic mentally with three cases.

Case A

role = "student"

lab_open = True

The expression is:

True AND True

The result is True, so the program assigns:

decision = allow-lab

Case B

role = "student"

lab_open = False

Now:

True AND False

The result is False, so the program assigns deny.

Case C

role = "guest"

lab_open = True

Now:

False AND True

Again the result is False.

Nothing mysterious happened. Different starting state produced a different branch result.

State tables scale better than memory

For a small program, build a table before changing anything.

Caserolelab_openconditiondecision
AstudentTrueTrueallow-lab
BstudentFalseFalsedeny
CguestTrueFalsedeny

This table is a baseline. It tells you what the current program does for known cases.

Later, if you change the condition, the same cases become regression tests.

Inputs can be explicit or indirect

Students often think input means input() or a form field. In systems work, input is broader.

A program may receive values from:

  • a command-line argument;
  • a configuration file;
  • an environment variable;
  • a network message;
  • a sensor;
  • a database row;
  • another process;
  • a previously stored value; or
  • a human operator.

Every input creates a boundary between what the program assumes and what the outside world may actually provide.

That boundary deserves attention.

A useful model: input → state → decision → output

Write this chain whenever a program feels confusing:

INPUT

STATE

DECISION

STATE CHANGE

OUTPUT / EVIDENCE

For the access checker:

role + lab_open

current values

boolean condition

decision

printed result

This model is more useful than memorizing syntax because it survives when the language changes.

Activity: trace before running

Use this program:

attempts = 2
account_enabled = True

if not account_enabled:
    status = "disabled"
elif attempts >= 3:
    status = "locked"
else:
    status = "active"

print(status)

Trace these states:

  1. attempts = 2, account_enabled = True
  2. attempts = 4, account_enabled = True
  3. attempts = 1, account_enabled = False
  4. attempts = 5, account_enabled = False

For each case, record:

  • the first condition evaluated;
  • whether execution continues to the elif;
  • the final status; and
  • one reason the order of the branches matters.

Check your reasoning

Case 4 should end as disabled, not locked, because the first branch is already true. The program does not continue looking for a different explanation after that branch is selected.

That detail becomes important in larger systems: branch order is policy.

Observation is not interpretation

Suppose the output is:

locked

That output is an observation.

You may infer that attempts >= 3 and the earlier not account_enabled branch was not selected, but you should not claim anything the program does not reveal. You do not know who caused the attempts, when they happened, or whether the counter is accurate unless other evidence tells you.

This is the same discipline used in defensive investigations:

state the observation first, then state the interpretation, then state the limitation.

Build the first part of your artifact

Create an Execution and Input Boundary Record with these fields:

Program purpose:
Inputs:
Important state variables:
Decision points:
Observable outputs:
Three baseline cases:
One assumption the program makes:
One piece of evidence the program does not provide:

Keep it. Lesson 2 will use the same record to compare alternate execution paths.

process flow

Program State to Validated Behavior

  1. Input

    Identify values entering the program and the assumptions attached to them.

  2. State

    Record the variables and conditions that exist before the decision.

  3. Branch

    Trace which condition selects the execution path.

  4. Observe

    Capture the output or supplied trace without adding interpretation.

  5. Validate

    Enforce the documented type, range, presence, or representation boundary.

  6. Retest

    Run positive, negative, boundary, and regression cases.

  7. Bound Claim

    State what the tested cases support and what remains unknown.

Read this concept flow as plain text
  1. Input. Identify values entering the program and the assumptions attached to them.
  2. State. Record the variables and conditions that exist before the decision.
  3. Branch. Trace which condition selects the execution path.
  4. Observe. Capture the output or supplied trace without adding interpretation.
  5. Validate. Enforce the documented type, range, presence, or representation boundary.
  6. Retest. Run positive, negative, boundary, and regression cases.
  7. Bound Claim. State what the tested cases support and what remains unknown.