Programming

How code actually runs

The code you write is only text, and a processor does not read text; before anything can happen it has to be translated into a form the machine can execute. Languages differ in when that translation happens and where its result is kept, and most of the confusing errors come from exactly that difference.

  • Lesson 2 of 14
  • Beginner
  • Free, no signup

The chain every line of code passes through

  1. 1 The text you wrote

    An ordinary text file; to the disk it is no different from a note.

  2. 2 The syntax check

    Over the whole file, before any execution. Break here and not one line runs.

  3. 3 Translation into a runnable form

    Bytecode, opcode or machine code. Sometimes kept on disk, sometimes in memory, sometimes nowhere.

  4. 4 Execution on the processor

    The only place where work actually happens, and the only place a runtime error appears.

The chain is simplified. In practice more layers sit between the last two links, from the operating system to optimisations the language engine performs while running.

Last checked: Facts and tool names in this lesson are re-checked against their sources on this date.

A computer does not read your text; so what does it run?

The file you save is plain text; as far as the disk is concerned it is no different from a note. The processor does not read that text. What a processor executes is a set of very simple numeric instructions: take this number from memory, add that one to it, put the result over there, and if it came out zero jump to that point. This is called machine language and nobody works directly in it today.

So between your text and the processor there is always a translation stage. It has three links and the order never changes: the text you wrote, the program that reads and checks and translates it, and the translated form that finally runs. No language escapes this chain. Languages differ on only two things: when the translation happens, and whether its result is kept.

One point that becomes useful very quickly: the translation stage checks syntax before anything else, meaning whether your text obeys the rules of that language at all. If it does not, work stops right there and not one line of your program runs, not even a line that was fine and came before the mistake. The fourth section of this page shows that with two real files.

What is the difference between compiled and interpreted?

The simplest version: in an interpreted language the text is handed to the language engine as it is and runs from top to bottom; in a compiled language the text is first transformed into another form and that form is what runs. Mozilla puts it in exactly those terms, that in interpreted languages the code is received in its text form and processed directly from it, while compiled languages are transformed into another form before they are run.

Now the awkward part. That split is not a clean line any more. On the same page Mozilla adds that most modern JavaScript interpreters use a technique called just-in-time compiling, turning the source into a faster binary form while the script is being used; and it still counts JavaScript as interpreted, because that compilation happens at run time rather than ahead of time.

So the question is it compiled or interpreted is slowly losing its usefulness. Two better questions: when does the translation happen, and is its result kept or rebuilt from scratch every time? Those two answers explain why a C program has a separate build step before it can run, and why a Python script that imports a module starts a little faster the second time.

Two timings for the same job

Both translate. The difference is when the translation happens, not whether there is one.

Translated before running

  • Has a separate build step that you run yourself
  • The output is an executable file that stays on disk
  • Many errors are found before the first run
  • Changing the file has no effect until you build again

Translated while running

  • No separate step; you hand over the file and it runs
  • The translation result may be kept in memory or on disk
  • Many errors are not found until that line is reached
  • You change the file and see the effect next time

These two columns are not cleanly separate any more: most modern engines do both at once, which is what makes the label unhelpful.

Where do Python, JavaScript and PHP sit in this chain?

The table below puts the two questions from the previous section against four languages. The third column is the important one, because that is where languages actually differ, not in their label.

LanguageWhat translates itWhat the translation producesWhere it runs
PythonThe Python interpreterBytecode; for modules it is kept on disk in a __pycache__ folderThe same machine or server
JavaScriptThe browser engine, or Node on a serverMachine code built while running, kept nowhere on diskThe user browser, or a server
PHPThe PHP engineOpcode; held in server memory by opcache, not on diskThe server
CA compiler, in a separate step before runningAn executable file on diskDirectly on the operating system

Put the first three rows side by side and a pattern appears: all three translate at run time, and they differ only in where they put the result. Python on disk, PHP in memory, JavaScript nowhere. The fourth row is here on purpose so you can see the genuinely different case: in C, if you do not compile the program, there is nothing to run at all.

One practical consequence you can use straight away: in JavaScript, PHP and Python you change the file and see the effect next time. In C you have to build again. If someone tells you they changed the file and nothing changed, the first question is which row of this table their language is in, and the second question is the subject of section five.

Why does nothing run sometimes, and half the program other times?

Make two small files and run each on its own. The first is bad.py, with an unclosed bracket on line two:

print("first line")
print("second line"

The real output is this:

  File "bad.py", line 2
    print("second line"
         ^
SyntaxError: '(' was never closed

The second is boom.py, whose syntax is fine but which asks for something impossible:

print("first line")
print(1/0)

And its output:

first line
Traceback (most recent call last):
  File "boom.py", line 2, in <module>
    print(1/0)
          ~^~
ZeroDivisionError: division by zero

Look at the difference, because this one thing is half of your debugging. In the first file the words first line were never printed, even though line one was perfectly correct and came before the mistake. In the second they were printed and then the program fell over halfway. The reason is the chain from the first section: the syntax check runs over the whole file before any execution, so an unclosed bracket on line two takes line one down with it. But a division by zero is not discovered until the moment we actually reach it, because until then nobody knows the divisor is zero.

These two cases have names: a syntax error and a runtime error. Their practical value is that you can tell them apart from the output alone, even without knowing the language. If something was printed before the error message, the program had started running and you are dealing with a runtime error; if nothing was printed, it very likely never ran at all.

One question that tells you which kind of error you have

Before the error message, had the program printed anything?

No, nothing

A syntax error

  • The program never started; the syntax check broke before execution
  • The line number in the message is usually right, or one or two lines later
  • Look for a missing bracket, quote or colon
Yes, something printed

A runtime error

  • The program ran and hit something halfway that was not possible
  • The last line of the message names the error type and matters most
  • Print the values right there to see which one is not what you assumed

The question tells you the kind of error, not its cause. A program that gives a wrong answer with no error at all falls into neither branch.

The file you saved is not the thing that runs

Now the interesting part of the chain. The result of translation does not only live in memory; very often it is kept so it does not have to be built again next time. Python does this right in front of you. Make two files, tools.py and a main.py that imports it, then:

python3 main.py
ls __pycache__/

A folder called __pycache__ appears with exactly one file inside it: tools.cpython-312.pyc. Nothing was written for main.py. Python's own documentation explains both halves: Python caches the compiled version of each module in the __pycache__ directory, and for the module loaded directly from the command line it always recompiles and does not store the result.

PHP does the same job with one difference: it does not put the result on disk, it keeps it in server memory. That layer is called opcache, and its behaviour has one setting that drives beginners mad: how often to check whether the file on disk has changed. PHP's own manual describes the setting in exactly those words, how often to check script timestamps for updates, in seconds. Which means there is a gap between saving a file and the save taking effect, and the gap is deliberate.

So that familiar sentence, I changed the file and nothing happened, has three common answers and all three live in this chain: either the file you changed is not the file that runs, or an old translated copy is still in memory, or the output is coming from a cache layer further forward and never reached your program at all. The section from our own work at the bottom of this page gives the number for that setting on our own server.

The layers sitting between your text and the metal

  1. 1

    The processor

    The only thing that actually executes, and it understands only simple numeric instructions.

  2. 2

    The operating system

    Divides memory, files and processor time between programs.

  3. 3

    The language engine

    The Python interpreter, the browser JavaScript engine, or the PHP engine on a server.

  4. 4

    The translated form

    Bytecode, opcode or machine code; the thing that is actually read and run.

  5. 5

    Your text file

    The outermost layer, and the only one you change directly.

The numbers are an order of distance, not of importance. Day to day programming needs none of these layers learned; it needs you to know they are there.

When the program does not work, where do you look first?

Read the error message from the bottom, not the top. The last line names the type of error and it is the most important word on the screen; the line above it usually says where. In the example in section four the last line was ZeroDivisionError: division by zero and the line above pointed at the exact expression that blew up. Beginners usually start at the top, where only file paths are written, and then say the error is incomprehensible.

Once you have read it, name the layer. There are only four. Syntax: nothing ran, the message came before any output. Runtime: something was printed and then it fell over. Environment: the program is fine but something it needs is not installed, is a different version, or cannot be found. Logic: there is no error at all, the program runs to the end and gives the wrong answer.

Here is our position and it is a simple one: before you change a single character, say the name of the layer out loud. Most of the hours beginners lose go into changing code in a layer where the problem is not, such as rewriting a perfectly good function when the library was never installed. The fourth layer is the worst of them, because it raises no error at all; the only way to catch it is to guess what the output should be before you run, then compare that against what came out.

The fast path, with AI

What a language model genuinely made faster for a beginner is reading an error message, but only if you keep the order. The common mistake is guessing where the problem is and then asking about the guess; what comes back is an answer about your guess, not about the error. The recipe below reverses the order: layer first, then explanation, then the change. A frontier model suits this better because it is diagnosis; our current pick sits in the AI section of this site.

  1. Copy the full text of the error, from the first line to the last. Not a screenshot, not a summary, not only the final line; the upper lines carry the place.
  2. Before any explanation, ask only for the layer to be named: syntax, runtime, environment or logic. One word is enough, and that one word decides everything after it.
  3. Ask it to quote back, verbatim, the line of your code the error points at. If it cannot, it does not have your code yet and anything it says is a guess.
  4. Now ask for the explanation and the smallest possible change, not a rewrite of the file. A full rewrite hides the bug and you never learn what it was.
  5. Apply the change yourself and run it, and if it errors again start from step one. If the layer was the environment, put your language version and operating system in the next message too.

Copy-ready recipe

I got this error. Before any explanation, answer only the three questions below, in this order.

1. Which layer is this error in? Pick exactly one: syntax, runtime, environment, logic.
2. Which line of my code caused it? Quote that line verbatim. If you do not have the code, say what you need.
3. What is the smallest change that fixes it? That change only, with no rewrite of the rest of the file.

Full text of the error:
{paste here, from the first line to the last}

The code I ran:
{paste here}

Environment: {language version} on {operating system}

Before you trust the output: The environment layer is the one a model guesses worst, because it cannot see your computer: it does not know your language version, your operating system or what you have installed, and if you do not supply those it fills the gap with a guess. So verify any file path or command name it proposes with one simple check before you run it. And if the answer to step one disagrees with what you saw in the output yourself, trust the output rather than the answer.

AI in this kind of work

One habit raises a beginner's speed more than anything else: instead of searching the error text in forums, hand the full text to a model and ask it to explain. What used to take an hour of hunting is now one message. Our position is that even this has an order: layer first, explanation second; the other way round is wasted time wearing the costume of productivity.

Tools that actually help

  • Claude Good at reading a long error message and saying which of its lines matters, especially when you hand over the full text without summarising it. Iran is on neither of Anthropic two supported-countries lists; we read that on Anthropic own page rather than measuring it.
  • Gemini Handles Persian well and is enough for asking what terms like bytecode, opcode and just-in-time compiling mean. Google own page says the Gemini web app runs in over 230 countries and territories, and Iran is not on that list.
  • ChatGPT The most common choice and acceptable for reading errors. We do not have an entry for it in our AI section yet, so we make no claim here about its access or pricing.

Where it backfires

The risk here belongs to this lesson specifically: a model answers at whatever layer your question pointed to, and a beginner question usually names the wrong layer. Ask why your loop is not working when the real problem was an uninstalled library, and you get a detailed answer about loops that has nothing to do with the cause and can cost you an hour. The cure is the fast path on this page: full error text first, then the layer, then the explanation. The environment layer is the most dangerous of all because the model cannot see your computer, and if you do not give it the versions it fills the gap with a guess; Anthropic itself calls this hallucination in its own documentation and explains how to reduce it, which means the vendor treats it as a real weakness. The rule is simple: anything the model says about your system has to be measured against a real run, not against the confidence of its tone. For how each of these tools can be paid for from Iran, see the buying guide.

Sources: Anthropic: reduce hallucinations Anthropic: supported countries Google: where the Gemini web app is available

Where this advice stops

Understanding this chain repairs nothing; it only makes you look in the right place when you do repair something. The model on this page is deliberately simplified too: just-in-time compiling, garbage collection, the optimisations a language engine performs while running and the scheduling done by the operating system are each their own field and only get named here. The four-layer split is a working tool rather than a formal taxonomy; in practice one bug can have roots in two layers at once. And the opcache number in the next section is ours: on your hosting it may be zero, meaning every request checks, or the whole thing may be off.

From our own work

On the day this lesson was checked, we measured both halves of section five on this very server. Python first: a main.py importing a tools.py was run, and afterwards the __pycache__ folder held exactly one file, tools.cpython-312.pyc, with nothing written for main.py itself. Then PHP: the opcache.revalidate_freq setting on this server is 60. We changed a data file of this learning section one second after a request and then fetched the page every five seconds; for 43 seconds the old value kept coming back, and at second 48 the new one appeared. So for close to a minute, every visitor was served a copy PHP had compiled earlier, while the file on disk had changed from the first second. If you have access to your own server, see the same with php -i | grep opcache.revalidate_freq.

Real follow-up questions

What does bytecode mean?

It is an intermediate form: neither human readable text nor a direct processor instruction. The language interpreter turns your code into bytecode and then runs that itself, and because the conversion happens once, keeping it makes the next run faster. The files inside a __pycache__ folder are exactly this.

Why are some languages faster than others?

Because they leave less work for run time. A language compiled before running has made most decisions in advance, while one that translates during execution and works out data types on the spot has to do that same work mid-run. But for most websites the language is not the bottleneck at all; the database, the network and heavy images show up long before it.

I changed the file but the site did not change; where is the problem?

There are three common answers and all three live in this chain. Either the file you changed is not the file that runs, for instance another copy loads from another folder. Or an old translated copy is still in server memory and does not change until the check interval passes. Or the output is coming from a cache layer further forward and never reached your program at all.