Skip to content

Lesson 2.2 · Program architecture· 2 min read

Lifecycle: initialization, state, and failure paths

Model lifecycle transitions and explain why a selected operation, a completed operation, and a failed operation are different states.

Platform:
Cross-platform
Language:
Python
Difficulty:
Beginner

What you will learn

Model lifecycle transitions and explain why a selected operation, a completed operation, and a failed operation are different states.

Before you begin: Command dispatch tables.

Add time to the control-flow model

The first lesson followed a single sequence of calls. A longer-lived program also has a lifecycle: it starts, becomes ready, performs work, encounters errors, and stops. A state model makes those distinctions explicit. When reading malware source, use the model to ask which actions are possible at each point, rather than assuming that every handler is always available.

This lesson uses a small local transition table. Events are invented strings supplied by the example. There are no operating-system actions, network connections, or hidden timers behind them.

An inert state machine

transitions = {
    ("new", "initialize_ok"): "ready",
    ("new", "initialize_failed"): "stopped",
    ("ready", "begin_demo"): "working",
    ("working", "finished"): "ready",
    ("working", "failed"): "stopped",
    ("ready", "stop"): "stopped",
}


def advance(state, event):
    return transitions.get((state, event), state)


state = "new"
for event in ["initialize_ok", "begin_demo", "failed"]:
    state = advance(state, event)
print(state)  # stopped

Read the contract

The key is a pair: current state and incoming event. A valid pair selects a new state. An unknown event leaves the state unchanged. That last behavior is a choice in this teaching model, not a rule that other programs must follow. Another implementation might log an error or terminate.

Notice that beginning work does not mean work succeeded. The model preserves that distinction with separate events. Likewise, failure does not pass through the normal completion transition. These differences matter when a log contains an “operation started” message but no final result.

Ownership and cleanup

A real implementation may own resources while it is working. To explain its failure path, identify who owns each resource and which exits release it. Do not infer cleanup from a function named cleanup; follow its actual calls and arguments. This example has only strings and a table, so there is no resource acquisition to undo.

Repeated events are another useful question. If initialization is reported twice, this table ignores the second event once it is ready. If completion arrives after failure, it remains stopped. Writing down such cases makes ambiguous assumptions visible before they become claims about a sample.

Exercise

Predict the state after initialize_ok, begin_demo, finished, and stop. Then consider begin_demo as the first event, before initialization.

Check your understanding

The complete sequence ends in stopped. Beginning before initialization leaves the state at new, because that pair has no transition. This lesson prepares you to distinguish lifecycle decisions from the individual handlers studied earlier.

Discussion

Related snippets

Distinguish a library dependency, an API call, and a successful privileged operation when reading an implementation.
Separate communication topology, message authority, and endpoint behavior using a static graph model.
Connect source revisions, build choices, packaged components, and execution roles without treating a repository as a single executable.