What a Program Is: Source to Running Code

Getting Started with Code · 25 min

Programming foundations · Module 1

What a program is, and what happens when you run it

Six lines of text, one command, three lines on screen. This page follows one small program from the file you save to the output you see — and shows you exactly where a missing bracket gets caught, and where it does not.

Run the pipeline one stage at a time
card.py · 6 lines you type
terminal · 3 lines it prints

01 The idea

Instructions in order, and a file that is only text

A computer does not work out what you meant. It carries out instructions, one at a time, in the order it is handed them. A program is that list of instructions, written down. When you write print(edge) you are not asking for something — you are adding one more step to a list that will be worked through from the top.

The catch is that the list you write is not the list the machine runs. You type characters into a file, and a file is text. It is the same kind of thing as a shopping list, and just as unable to do anything by itself. Something has to read your text, check that it is written in a form it can understand, and turn it into instructions a processor can carry out. That reading step is where an entire family of errors lives, and knowing it is there is the difference between a message that unblocks you and a message that scares you off.

A program is a list of instructions run in order. Your source file is only text until a translator reads it — and it reads and checks the whole file before it runs a single line of it.
Source codeThe text you type and save, exactly as you typed it. Characters in a file. The processor cannot execute it and never tries to.
Compiler or interpreterThe program that reads your source, checks it is written legally, and produces something the machine can run. A compiler does it once up front into a separate file; an interpreter does it as part of running. Both read the whole file first.
RuntimeThe stretch of time while your program is actually executing. Anything that goes wrong before it starts is a syntax error; anything that goes wrong during it is a runtime error.

02 Execution trace

Six lines, and the order they actually run in

Here is the whole program. It prints a name card: a row of dashes, a name, the same row of dashes again. Save it as card.py and run it.

card.py · what you write

def card(name):    edge = "-" * 6    print(edge)    print("| " + name)    print(edge)card("Asha")

terminal · what you see

------| Asha------

Now the trace. One row per statement executed, in the order it executes. The left column is the line number. The three boxes are the two values the program creates — name and edge — and a running count of lines put on screen. A rose box is a value that just changed; a green box is settled and final.

line 10def stores the block; nothing inside it runs
line 6Asha0call: name becomes "Asha", jump to line 2
line 2Asha------0edge holds six dashes — still nothing on screen
line 3Asha------1prints ------
line 4Asha------2prints | Asha
line 5Asha------3prints ------ — block ends here
endAsha------3back to line 6, nothing left — exit code 0

Read the left column downwards: 1, 6, 2, 3, 4, 5, end. The order the lines run is not the order they are written, and that is normal rather than exceptional. Line 1 only stores the block. Line 6 is the first line that does anything, and it sends the machine backwards into the block that line 1 just stored.

Notice the other half of the trace too. Six lines run, and only three of them put anything on screen. Storing a block, binding a name and building a string are all real work, and none of them are visible. Beginners lose hours to a program that is running perfectly and was simply never told to print anything.

03 Anatomy

Reading the file, then reading the error

The same six lines, annotated. Four things make up any source file: statements, blocks, whatever marks where a block starts and stops, and one entry point where execution begins.

def card(name):          # header: opens a block, ends with a colon    edge = "-" * 6       # statement, indented, so it is inside the block    print(edge)          # statement    print("| " + name)   # statement — the widest line in the block    print(edge)          # last indented line, so the block ends herecard("Asha")             # left margin: top level, so this call is what runs the block

Indentation is not decoration. In Python those four spaces are the only thing saying "this line belongs to card". Delete the indentation on line 5 and it stops being part of the card and becomes a top-level statement that runs on its own. C, Java and JavaScript do the same job with { and } and treat indentation as a courtesy to humans — but every language has some marker, and it is never optional.

Defining is not running. Line 1 prints nothing. It stores the four indented lines under the name card and moves on to line 6. A block sits there unused until something calls it, which is why line 2 runs third rather than second.

The entry point is where execution begins, and there is exactly one. Python starts at the first top-level line and works down, so here execution starts at line 1 and the first line that makes anything happen is line 6. C and Java instead look for a function named main and start there. Either way the machine never starts at "the line that looks most important".

Now the part that actually unblocks you. Everything that can go wrong falls into three families, and the family tells you where in the pipeline it was caught — which in turn tells you how much of your program ran before it stopped.

Error familyCaught whenHow it announces itself
Syntax error
the file is not written legally
before line 1A file name, a line number, a caret under the spot, and a last line naming the fault. Your program produces no output at all.
Runtime error
legal code, impossible action
mid-runEverything printed up to that instant is already on screen, then a traceback listing the calls and a last line naming the exception.
Logic error
legal code, wrong idea
neverA clean run and an exit code of 0. Nothing is reported, because nothing is broken. The output is simply not what you wanted.

Here are the first two, produced by the same six-line program with one character changed each time. Look at what is above the message as much as at the message itself.

Syntax error · nothing ran

  File "card.py", line 4    print("| " + name         ^SyntaxError: '(' was never closed

Runtime error · one line already printed

------Traceback (most recent call last):  File "card.py", line 6, in <module>    card("Asha")  File "card.py", line 4, in card    print("| " + nme)NameError: name 'nme' is not defined

Read the last line first. It carries the two things you need: a type and a message. SyntaxError plus '(' was never closed. NameError plus name 'nme' is not defined. Everything above the last line is location. Most beginners read from the top, meet a wall of file paths, and give up one line before the useful part.

In a traceback, the top is the oldest call and the bottom is the newest. On the right, line 6 is listed first because that is where the call started, and line 4 is listed last because that is where it broke. The line you edit is almost always the bottom one. The right-hand run also got one line onto the screen first — that dash row above the traceback is proof the program really did start.

The line named is where the trouble started, not where the reader noticed it. On the left the caret sits on line 4 because that is where the bracket opened — but nothing was noticed until the end of the file, which is why lines 5 and 6 were swallowed as if they were still inside that bracket. A missing quote or a missing colon is kinder: Python can tell at once, so it names that exact line. Either way, check the line named first, then the line above it.

One habit. Before you change anything, read the last line of the error message. Type and cause first, location second. That one habit removes most of the time a beginner loses to errors.

05 Cheat sheet

The four stages, and what fails at each

StageWhat happensIf it fails
1 · SourceYou type text and save it as card.py. It is characters on a disk, nothing more.cannot fail
2 · CheckA compiler or interpreter reads the whole file and verifies its grammar. Nothing is calculated here.SyntaxError — zero output
3 · RunStatements execute in order from the entry point, jumping into blocks when something calls them.NameError and friends — whatever had already printed, then a traceback
4 · OutputWhatever you printed is on screen, and an exit code goes back to the operating system.wrong answer — a logic error, and the exit code is still 0
Exit code 0 means "did not crash"It is a number your program hands the operating system on the way out, and 0 means nothing blew up. It says nothing about whether the output was right. A program can be wrong from top to bottom and still hand back a cheerful 0.
Compiled vs interpreted is about whenC is translated into a separate runnable file before you run it; Python is translated as part of running. Both read and check your entire file first, which is why a syntax error on the last line stops the first line from printing in either.
The line number is a starting pointAn unclosed bracket is blamed on the line where it opened, even though nothing was noticed until the end of the file — so every line in between was misread on the way. Read the line named, then the line above it.

06 Where & why

The same four stages, in the tools you will actually use

The stages do not change between languages. What changes is how many commands you type and what the error messages are called. Once you can see the pipeline, a new language is mostly new vocabulary.

Python
python card.py

One command does the whole pipeline. Python parses your entire file into bytecode first, then runs it. That single fact is why a missing bracket on line 4 stops line 3 from printing, which surprises people who think an interpreter runs line by line.

C · gcc
gcc card.c -o card, then ./card

Two commands, because the stages are split across two tools. The first checks and translates and produces a file called card; the second runs it. If the check fails you never get that file at all, so there is literally nothing to run.

Java
javac Card.java, then java Card

Same split: javac checks and produces Card.class, and java runs it on the JVM. This is why Java people say "compile-time error" and "runtime exception" as two separate phrases — they happen at two separate commands.

Your browser
JavaScript in a web page

The browser parses and runs your script as the page loads, and its error messages go to the Console tab behind F12. They read exactly like the ones in section 03: a type, a message, a file and a line. A blank page usually means a syntax error, not a network problem.

You will forget most syntax and look it up forever. You will not get to skip this: every language you ever pick up has a source stage, a check stage, a run stage and an output stage, and every error you meet is a question about which of those four it happened in.

07 Interview questions

Say these out loud

These come up in first-round screens and in viva. The error-message question is the one that tells an interviewer whether you have actually written code or only read about it.

What is a program?
A list of instructions a computer carries out in order. You write those instructions as text in a source file, and the machine works through them from the entry point downwards, stepping into a block whenever something calls it. Nothing is guessed and nothing is invented — the machine only ever carries out the next instruction, which is not always the next line down the page.
What actually happens between saving a file and seeing output?
Four stages. Your source file is text on a disk; a compiler or interpreter reads the whole file and checks its grammar; the checked instructions then execute one at a time; and finally whatever you printed is on screen and an exit code goes back to the operating system. Different errors get caught at different stages, which is why some let your program print first and some do not.
Why does a syntax error on the last line stop the first line from printing?
Because the whole file is read and checked before any of it runs. The checker is not executing your code, it is reading its grammar, and it refuses to hand anything over until the file parses completely. So a missing bracket on line 40 means line 1 never gets its chance.
What is an entry point?
The place where execution begins. In Python it is the first top-level line of the file, so the program starts at the top and works down. In C and Java it is a function called main, which the runtime finds by name. Either way there is exactly one starting point, and a line inside a block is never it.
How does a language know which lines belong to a block?
By an explicit marker. Python uses indentation: the indented lines under def card(name): are its body, and the first line back at the left margin is outside it. C, Java and JavaScript use curly braces and treat indentation as a courtesy to the reader. Indent something illegally in Python and you get an IndentationError; move a line out of a block in a way that still parses and you get no complaint at all — just a line that runs at the wrong moment.
Syntax error versus runtime error — what is the difference?
A syntax error means the file could not be read as valid code, so it is caught before anything runs and you get no output whatsoever. A runtime error means the code was legal but something went wrong while executing it — a name that does not exist, a division by zero, an index off the end of a list. The quickest giveaway is the screen: if anything printed before the message, it is certainly a runtime error. An empty screen proves nothing on its own, because a runtime error that hits before your first print also shows you nothing.
What is a logic error, and why is it the worst kind?
Code that runs perfectly and produces the wrong answer. Nothing is malformed and nothing crashes, so the machine has no way to notice and the exit code is still 0. It is the worst kind because there is no message to read — the only way to catch it is to know what the output should have been and check it yourself.
You hit an error message you have never seen. Walk me through what you do.
Read the last line first, because that is the type and the cause. Then read the file and line number above it to find where the machine got stuck. Then look at that line and the one above it, since the line named is where the trouble started rather than where the reader finally noticed it — an unclosed bracket is blamed on the line that opened it. Searching for the last line is a reasonable next step; pasting in the whole wall of text is not.
Compiled or interpreted — what is the real difference?
When the translation happens, not whether it happens. A compiler translates the whole file into a separate runnable file ahead of time, so you type two commands: one to build, one to run. An interpreter translates as part of running, in one command. Both still read and check your entire file first, which is why syntax errors behave identically in C and in Python.
Is Python compiled or interpreted?
Both, and interviewers ask it precisely because the honest answer has two halves. CPython compiles your source into bytecode, and a virtual machine then interprets that bytecode. Those .pyc files in __pycache__ are that bytecode cached on disk — Python only saves them for modules you import, not for the script you run directly, which is why you rarely see one for your own file. In everyday use people call it interpreted, because you type one command and there is no separate executable to keep.
What does an exit code of 0 mean?
That the program finished without crashing. It is a small number handed back to the operating system on the way out, and 0 conventionally means no problem. It says nothing at all about whether the output was correct, which is exactly why a program full of logic errors still exits with 0 and why shell scripts that only check the exit code miss them.

08 Practice problems

Predict first, then run

Write your answer down before you touch a keyboard. Being wrong on paper and finding out why is the whole exercise; running it first skips the part that teaches you something.

The running order

Easy
Add a seventh line to the card program: card("Ram") sitting directly under line 6. Write down the order the lines execute, as a list of line numbers with repeats, and say how many lines end up on screen.
Follow-up
The list comes out longer than the file. Four line numbers appear twice, one appears once and shows nothing, and the list does not begin 1, 2, 3.
Show the hint
A def stores a block and moves on; each call jumps into the stored block, runs it to the end, and comes back to the line that called it.

Name the family

Easy
For each of these four faults, name the error family and say how many lines are on screen at the moment the program stops: a missing colon after def card(name); print(edg) on line 5; a call written as card() with no argument; and running the program unchanged with the name "Bhavana".
Follow-up
Two of the four leave the screen completely empty — yet they are caught at two different stages, so a blank screen on its own does not tell you which family you are in.
Show the hint
For each fault, count how many print statements had already finished before it hit. The screen is your evidence, and "nothing on screen" can happen in more than one way.

Move the call to the top

Medium
Move line 6, card("Asha"), so it becomes the first line of the file instead of the last. State exactly what happens, and which stage it happens in.
Follow-up
Nothing is misspelled and nothing is malformed, so the check stage passes cleanly. The failure is purely about order, which is a distinction people only make once they have seen it break.
Show the hint
The check stage only reads grammar. Ask what actually exists at the moment the very first line runs.

Read the traceback

Medium
A run of the card program ends with this: File "card.py", line 6, in <module> / card("Asha") / File "card.py", line 5, in card / print(edg) / NameError: name 'edg' is not defined. Write down exactly what was already sitting on the screen above this message, and how you knew.
Follow-up
The message never mentions line 3 or line 4, yet both of them ran and both of them printed before it appeared. A traceback lists the calls that were still open, not everything that happened.
Show the hint
Work the screen out separately from the message: walk the block from its first line down to the line the traceback names, and count the prints that got to finish.

The silent one

Medium
Run the card program with the name "Bhavana". It exits with code 0 and reports nothing. Say what is wrong with the output, then describe in words what line 2 would have to work out — instead of a fixed 6 — for the card to line up for a name of any length.
Follow-up
There is no message to read, because there is no error. This is the only family you have to catch with your own eyes, and the exit code actively lies to you about it.
Show the hint
Count the characters in the middle line and compare that with the number of dashes, then say where the difference between the two numbers comes from.

Three faults, one run

Hard
A file has three deliberate faults: line 4 is missing its closing bracket, line 3 says print(edg), and line 2 builds only four dashes. Predict exactly what the terminal shows on the first run, and say how many runs it takes before you have seen evidence of all three.
Follow-up
The three faults do not queue up and report together. The pipeline surfaces them strictly one at a time in a fixed order, and one of them never announces itself at any point.
Show the hint
Assign each fault to the stage that would catch it, then remember that a later stage never starts until the earlier one has finished cleanly.