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.
| Element | Meaning |
|---|---|
role | input-like state describing the user category |
lab_open | state describing whether the classroom lab is available |
if condition | decision rule |
decision | state 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.
| Case | role | lab_open | condition | decision |
|---|---|---|---|---|
| A | student | True | True | allow-lab |
| B | student | False | False | deny |
| C | guest | True | False | deny |
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:
attempts = 2,account_enabled = Trueattempts = 4,account_enabled = Trueattempts = 1,account_enabled = Falseattempts = 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
Input
Identify values entering the program and the assumptions attached to them.
State
Record the variables and conditions that exist before the decision.
Branch
Trace which condition selects the execution path.
Observe
Capture the output or supplied trace without adding interpretation.
Validate
Enforce the documented type, range, presence, or representation boundary.
Retest
Run positive, negative, boundary, and regression cases.
Bound Claim
State what the tested cases support and what remains unknown.
Read this concept flow as plain text
- Input. Identify values entering the program and the assumptions attached to them.
- State. Record the variables and conditions that exist before the decision.
- Branch. Trace which condition selects the execution path.
- Observe. Capture the output or supplied trace without adding interpretation.
- Validate. Enforce the documented type, range, presence, or representation boundary.
- Retest. Run positive, negative, boundary, and regression cases.
- Bound Claim. State what the tested cases support and what remains unknown.