State and change.
In the previous video, we built a mental model of what a program is. A program is a description of how to transform state. Initial state goes in. Instructions are applied. Final state comes out.
That model is correct. But it is incomplete.
Today, we go deeper. We will understand what happens between the initial state and the final state. We will understand how state changes. Step by step. Transition by transition.
This is fundamental. If you understand how state changes, you understand how programs work.
Let us begin with a brief reminder.
State is a snapshot of system data.
At any moment, your program has data. Variables have values. Memory contains information. All of this data, frozen at one instant, is the state.
We said that a program transforms initial state into final state. That is true.
But there is more to the story. Between the initial state and the final state, there are many states in between. The program does not jump directly from start to finish. It moves through a sequence of states, one step at a time.
Understanding this sequence — understanding how state changes — is what this video is about.
When a program runs, it does not transform state in one leap.
It transforms state in many small steps.
Think about it. A program has many instructions. Each instruction does something small. Each instruction changes the state slightly.
So as the program runs, the state changes. Then it changes again. Then again. Each instruction causes a small change.
The execution of a program is not: initial state, then final state.
The execution of a program is: state zero, then state one, then state two, then state three, and so on, until the final state.
A sequence. Many states. Many transitions.
This is how programs actually work. Not in one big transformation, but in many small transformations, one after another.
Let us give this a name. When the state changes from one snapshot to the next, we call that a state transition.
A state transition is the moment when state changes.
Before the instruction executes, the state is one thing. After the instruction executes, the state is something different. The change from before to after is the transition.
Every instruction causes a state transition.
Some transitions are small. A variable changes from five to six. One small change.
Some transitions involve multiple changes. Several variables update at once. But it is still one transition — the state before, the state after.
Understanding a program means understanding its state transitions. What was the state before? What instruction executed? What is the state after?
If you can answer these questions for every step, you understand exactly what the program does.
Let us introduce an important technique: tracing state.
Tracing means following the state through each transition. You start with the initial state. You look at the first instruction. You determine what changes. You write down the new state.
Then you repeat. Look at the next instruction. Determine what changes. Write down the new state.
Step by step, you build a complete picture of how the program executes.
This is not abstract. This is a concrete skill. When you debug a program, you are tracing state. When you reason about whether code is correct, you are tracing state in your mind.
The ability to trace state — to follow transitions carefully and accurately — is one of the most important skills a programmer can have.
Let us trace a simple program. Imagine we have two variables: x equals five, and y equals three. Then we run three instructions: x equals x plus y, then y equals x minus y, then x equals x minus y.
What does this program do? Let us trace the state.
Initial state: x is five, y is three.
First instruction: x equals x plus y. x becomes five plus three, which is eight. State: x is eight, y is three.
Second instruction: y equals x minus y. y becomes eight minus three, which is five. State: x is eight, y is five.
Third instruction: x equals x minus y. x becomes eight minus five, which is three. State: x is three, y is five.
Final state: x is three, y is five.
Look at what happened. We started with x as five and y as three. We ended with x as three and y as five. The values swapped.
By tracing state transitions, we can see exactly how this program works. Each step is small and clear. The overall effect emerges from the sequence.
When you trace state transitions, you notice something important.
At each step, some things change. And some things stay the same.
In our example, when we executed x equals x plus y, the value of x changed. But the value of y stayed the same.
This is always true. Each instruction changes some part of the state. Other parts remain unchanged.
Understanding what changes and what stays the same is crucial.
When something changes that you expected to stay the same — that is a bug.
When something stays the same that you expected to change — that is also a bug.
Reasoning about programs means reasoning about change. What should change? What should not change? Does the code do what you expect?
Now I introduce another important term. Invariant.
An invariant is a condition that must always hold.
Think about what this means. As your program runs, the state changes constantly. Transition after transition. But some conditions should remain true throughout.
These conditions are invariants.
For example, imagine a bank account. The balance changes with every transaction. But there might be an invariant: the balance should never be negative.
No matter what transactions occur, no matter how the state changes, this condition should always be true. The balance is always zero or greater. That is an invariant.
Invariants are powerful because they constrain the state. They say: no matter what happens, this must be true.
When an invariant is violated — when a condition that should always hold becomes false — something has gone wrong. The program has a bug.
Let us see an invariant in code. Imagine a simple Counter class. It has a variable called count, starting at zero. It has an increment function that adds one. And it has a decrement function.
This is a simple counter. It starts at zero. You can increment it. You can decrement it.
But look at the decrement function. It checks if count is greater than zero before subtracting. Why?
Because there is an invariant: count should never be negative.
The code enforces this invariant. No matter how many times you call decrement, count will never go below zero. The invariant holds.
This is how we use invariants in practice. We identify conditions that must always be true. Then we write code that ensures those conditions are never violated.
Now you have two concepts: state transitions and invariants.
These concepts work together.
A state transition changes the state. But it should not violate any invariants. The state after the transition should still satisfy all the conditions that must hold.
This is a powerful way to think about correctness.
You can ask: what are my invariants? What conditions must always be true?
Then, for each state transition, you can ask: does this transition preserve the invariants? After this instruction executes, are all the conditions still true?
If every transition preserves every invariant, then the invariants hold throughout the entire execution. From initial state to final state, the conditions are always true.
This is how we reason about programs. We identify what must be true. We verify that every change preserves what must be true.
Why does understanding state transitions matter?
Because programs are sequences of state transitions. That is all they are.
When you write code, you are specifying transitions. Given this state, change it to that state.
When you read code, you are understanding transitions. What was the state? What changed? What is the state now?
When you debug code, you are finding incorrect transitions. The state changed in a way you did not expect. Where did that happen? Which transition was wrong?
When you verify code is correct, you are checking that transitions preserve invariants. Every change should maintain the conditions that must hold.
State and change. Transitions and invariants. These are the fundamental concepts.
If you understand them deeply, you can understand any program.
Let us summarize.
A program execution is a sequence of states. Not just initial and final, but many states in between.
A state transition is the change from one state to the next. Each instruction causes a transition.
Tracing state means following each transition carefully. This is how we understand what a program does.
An invariant is a condition that must always hold. Invariants constrain the state throughout execution.
Correct programs preserve their invariants. Every transition maintains the conditions that must be true.
State and change. This is how programs work.
Understanding state transitions gives you the ability to reason about programs precisely. You can trace what happens. You can verify correctness. You can find bugs.
This is the foundation of thinking like a programmer.