Every programming language you write in must eventually become machine code before your CPU can run it. The question is when and how that translation happens — and that single difference changes everything about how a language feels, performs, and gets deployed.
What Does a Computer Actually Understand?
Your CPU understands exactly one thing: machine code — raw binary instructions like 10110000 01100001. Not Python. Not JavaScript. Not C++. Every language you write must, at some point, become machine code. The compiled vs interpreted distinction is just about when that translation happens.
Compiled Languages — Translate First, Run Later
A compiler reads your entire source code and translates it into machine code before you run it. The output is a standalone executable — a binary the OS can run directly, no translator needed at runtime.
// hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
// Compile: gcc hello.c -o hello
// Run: ./hello
When you run gcc hello.c -o hello, the compiler reads your whole file, checks for errors, optimizes the logic, and produces a binary. That binary runs at full CPU speed — nothing sitting in between translating at runtime.
What the compiler does behind the scenes: Lexing → Parsing → Semantic Analysis → Optimization → Code Generation. All that work happens once upfront so every future run is as fast as possible.
Languages That Are Compiled
- Fast execution — CPU runs pre-translated machine code directly. This is why OS kernels and game engines are written in C/C++.
- Errors caught early — compiler finds mistakes before the program ever runs.
- Platform-specific output — a Windows x64 binary won't run on Linux ARM without recompiling.
Interpreted Languages — Translate as You Go
An interpreter reads your source code line by line and executes it on the fly — no separate compilation step, no binary output. The interpreter itself is the runtime.
# hello.py
print("Hello, World!")
# Run it: python hello.py
When you run python hello.py, Python reads your file, processes each statement, and executes it. There's no .exe produced — because the interpreter handles everything at runtime.
Runtime errors show up differently: A bug on line 50 won't be caught until execution actually reaches line 50 — even if lines 1–49 ran fine. Compiled languages catch many of these before you even run the program.
Languages That Are Interpreted
- Fast development — write a line, run it, see the result instantly.
- Truly portable — the same Python file runs on Windows, Linux, and macOS as long as Python is installed.
- Slower execution — the interpreter adds overhead at runtime. Translation happens every single time the program runs.
The Modern Reality — It's Not Black and White
Most modern languages live somewhere in the middle. The compiled/interpreted line has blurred significantly. Here are the two big hybrid models:
The Bytecode Model — Java, Python, C#
Source code is compiled to an intermediate format called bytecode — not raw machine code, but a compact platform-neutral representation. At runtime, a Virtual Machine (JVM for Java, CPython for Python, CLR for C#) executes the bytecode.
This is why Java is "write once, run anywhere." The same .class file runs on any machine with a JVM installed — no recompiling for each platform.
JIT Compilation — JavaScript V8, Java HotSpot, PyPy
Just-In-Time compilation is how modern runtimes get near-compiled performance from interpreted code. The runtime watches which parts of your code run most frequently ("hot paths") and compiles those specific parts to native machine code on the fly.
This is why modern JavaScript in Chrome's V8 engine is shockingly fast — V8 JITs your hot loops into machine code at runtime. It's why Node.js can handle serious backend workloads.
Side-by-Side Comparison
| Feature | Compiled | Interpreted |
|---|---|---|
| Translation time | Before runtime (build step) | At runtime (line by line) |
| Execution speed | Fast (direct machine code) | Slower (interpreter overhead) |
| Error detection | At compile time | At runtime (when that line runs) |
| Portability | OS/arch-specific binary | Cross-platform with interpreter |
| Dev speed | Slower (compile → run cycle) | Faster (run immediately) |
| Output file | Standalone executable | No separate output |
| Examples | C, C++, Go, Rust | Python, Ruby, PHP, Bash |
Common Myths, Busted
Which Should You Use?
Compiled (C, Go, Rust) — when raw performance matters: OS kernels, game engines, embedded systems, CLI tools where startup time counts.
Interpreted/bytecode (Python, JavaScript, Java) — web development, data science, scripting, prototyping, or any domain where developer productivity and ecosystem outweigh squeezing out milliseconds.
Most working developers use both — Python for data pipelines, Go for the API server, JavaScript for the frontend.
Quick Check: Java compiles to bytecode that runs on the JVM. Is Java compiled or interpreted? The answer: both — and now you know exactly why. Try writing the same loop in Python and C, time both, then try PyPy and watch the gap close.
Enjoyed this? Next up: How Memory Management Works in Python vs C — the hidden layer that explains even more of what happens when your code runs.