Compiled vs. Interpreted
Every language implementation must eventually turn source code into something the CPU can run. How and when that translation happens — all at once ahead of time, line-by-line at runtime, or something in between — defines the execution model. (The full pipeline is covered in depth in the future Compiler Internals module.)
A compiler translates the entire source file into machine code (or another lower-level form) before the program ever runs, producing a standalone executable. An interpreter reads and executes source code (or an intermediate form of it) directly, translating and running it piece by piece as it goes.
Compiling ahead of time usually gives faster execution and lets you distribute a program without needing the original source. Interpreting gives faster iteration (no separate build step), simpler tooling, and easier cross-platform distribution of the same source file.
Compiled languages (C, C++, Rust, Go) dominate operating systems, games, and performance-critical software. Interpreted or hybrid languages (Python, JavaScript, Ruby) dominate scripting, web development, and rapid-prototyping contexts.
A compiler is like translating an entire book into another language before publishing it — the reader gets a finished, fast-to-read book, but the translation work happens once, up front. An interpreter is like a live interpreter at a conference, translating a speech sentence by sentence as it's spoken — more flexible and immediate, but redoing the translation work every single time the speech is given.
The compiled C program only pays the translation cost once, at build time. The Python program re-parses and re-translates its source (to bytecode) essentially every time it's launched.
'Compiled' and 'interpreted' describe an implementation's strategy, not an inherent property of the language itself — Python has compiled implementations (e.g. ahead-of-time compilers exist for it) and C has interpreters, though these are far less common than the mainstream implementations.
Most modern high-level languages use a hybrid model: source is compiled to an intermediate bytecode, which a virtual machine then interprets or further JIT-compiles at runtime (covered fully in the Bytecode & Virtual Machines and JIT/AOT modules).
Compilation catches many errors (syntax errors, some type errors) before the program ever runs; a pure interpreter can only discover an error in a code path once that path actually executes.
Distribution differs sharply: a compiled executable can run on a matching machine with zero extra tooling; interpreted source generally requires the correct interpreter/runtime to be installed on the target machine.
Saying 'Python is slow because it's interpreted' as if that were the whole story — CPython's interpreter overhead, its dynamic typing, and its Global Interpreter Lock all contribute; 'interpreted' alone doesn't fully explain performance.
Assuming a compiled language can never have runtime errors — type errors and syntax errors are usually caught at compile time, but logic errors, null dereferences, and array-bounds violations still surface at runtime in most compiled languages.
Believing bytecode and machine code are the same thing — bytecode (e.g. Java's, Python's) is an intermediate, typically CPU-independent format meant to be run by a virtual machine, not directly by hardware.
Shipping a compiled C++ game engine as a single binary so end users don't need any development tools installed.
Distributing a Python data-science script as source, relying on the recipient having Python and the right libraries installed.
Using a JIT-compiled language (JavaScript in a browser, Java on the JVM) to get near-compiled performance while still distributing portable, human-readable-ish intermediate code.
The real spectrum: AOT, interpretation, bytecode+VM, JIT Ahead-of-time (AOT) compilation produces native machine code before execution. Pure interpretation walks the source (or its AST) and executes it directly, with no separate machine-code artifact. Bytecode + VM interpretation compiles to a portable intermediate format executed by a virtual machine. JIT compilation compiles (parts of) that bytecode to native machine code during execution, often guided by runtime profiling. Production language runtimes frequently combine several of these — this is expanded fully in the Interpreters, Bytecode & Virtual Machines, and JIT/AOT modules.
Compilation doesn't eliminate a runtime Even a fully AOT-compiled C program depends on a runtime: the C standard library, the OS loader, and startup code that sets up the process before main() ever executes. 'Compiled' does not mean 'no runtime support' — it means the translation step happened before execution rather than during it.
Error-detection timing shifts the whole development experience
Static, ahead-of-time compilation with a strict type checker (Rust, Haskell) pushes many classes of bugs to compile time, at the cost of a slower edit-compile-run loop. Dynamic, interpreted languages shorten that loop dramatically but push more error discovery to runtime and, often, to production if test coverage is incomplete.
Is JavaScript compiled or interpreted?
Modern JavaScript engines (like V8) use a hybrid approach: source is parsed and initially interpreted, while a JIT compiler compiles frequently executed ('hot') code paths to optimized native machine code during execution. So calling it purely 'interpreted' is an oversimplification.
Why does a compile error stop a C program from running at all, while a Python program can run partway before crashing?
The C compiler must successfully translate the entire program before producing an executable — a syntax or type error anywhere blocks the whole build. A Python interpreter translates and executes incrementally, so code before the faulty line can run successfully before the interpreter reaches and fails on the broken statement.
What's the difference between bytecode and machine code?
Machine code is CPU-architecture-specific binary that the hardware executes directly. Bytecode is a portable, typically CPU-independent intermediate format designed to be executed (interpreted or JIT-compiled) by a virtual machine, not run directly by hardware.
Can the same language be both compiled and interpreted?
Yes. The language itself is just a specification; different implementations can choose different execution strategies. For example, there are interpreters for C and ahead-of-time compilers for languages like Python, even though each language is far more commonly associated with the opposite strategy.
C:
gcc program.c -o program // translate once
./program // run the translated binary, any number of times
Python:
python3 program.py // reads, translates and runs in the same step, every time