Code Execution Fundamentals
You write Python or TypeScript. The CPU executes machine instructions for this chip (x86-64, ARM). Something in the middle translates. That something is a compiler, an interpreter, or both (bytecode + JIT).
The labels on languages are marketing. CPython compiles to bytecode then interprets it. V8 JITs. Go compiles ahead of time to a binary. Knowing the pipeline tells you why a program starts fast or runs fast, and why "Python is slow" is incomplete.
[!TIP] ELI5: Translating a speech AOT compiler: translate the whole book, print it, hand the reader a finished English copy. Slow to prepare, fast to read, one edition per language (CPU). Interpreter: a person at your elbow translating sentence by sentence. Starts immediately, extra work every sentence. JIT: the interpreter notices "thank you" for the 50th time and writes it on a whiteboard. Hot paths become a compiled book; cold paths stay live-translated.
1. Ahead-of-time compilers
Typical: C, C++, Rust, Go, Zig.
go build / rustc produce a native binary. The compiler sees the whole program (or a lot of it), type-checks, optimizes, emits machine code and a calling convention for one OS/arch.
- Run speed: no translator in the way. Tight loops are C-like (Rust/C) or close (Go).
- Start speed: the OS loads a binary and jumps. No VM warmup.
- Cost: compile time, less runtime flexibility, rebuild per platform (
linux/arm64≠windows/amd64) unless you cross-compile.
Go still has a runtime (GC, goroutines). "Compiled" does not mean "no runtime." It means "not interpreting source at run time."
2. Interpreters (and the bytecode cheat)
A pure interpreter walks the AST and performs operations. Slow.
Most "interpreted" languages compile to bytecode first:
source.py → .pyc bytecode → CPython VM loop
The VM is a program in C that switches on opcodes (LOAD_FAST, BINARY_ADD). Still not native machine code for your loop, but cheaper than re-parsing text.
- Portable: ship source or bytecode; the VM is per platform.
- Edit-refresh: no 5-minute C++ link.
- Slow hot loops: every iteration pays the VM. Numeric Python is fast because it spends time in C extensions, not in the bytecode loop.
Ruby, PHP, and CPython all live here by default (with optional JITs appearing later).
3. Bytecode VMs + JIT
Typical: Java (JVM), C# (CLR), JavaScript (V8, JavaScriptCore), sometimes PyPy / LuaJIT.
Pipeline:
- Source → bytecode (
.class, or V8's internal IR). - VM starts by interpreting or quickly compiling.
- A profiler notices hot functions.
- JIT emits optimized machine code (type-specialized, inlined).
- If assumptions break (the variable was always an int, now it is a string), deoptimize back to slow path.
That is why JS is "slow" on first load and "fast" in a long-running server, and why JVM apps have a warmup story.
javac App.java → App.class → java App
bytecode interpret + JIT
4. Where common languages sit
| Language | What actually runs |
|---|---|
| C / Rust | Native AOT |
| Go | Native AOT + GC runtime |
| Java / Kotlin | Bytecode + JIT (JVM) |
| C# | Bytecode + JIT (or AOT in some modes) |
| JavaScript in Chrome | Parse → bytecode → JIT tiers (V8) |
| CPython | Bytecode + C interpreter (JIT experimental) |
| TypeScript | Erased to JS, then whatever the JS engine does |
TypeScript does not make JS faster. It is a compile-time type checker. V8 never sees your types.
5. Why this shows up at work
- Cold start (Lambda, CLI): AOT binaries and small interpreters win. JVM/JIT want warmup.
- CPU-bound loops: move them out of CPython bytecode (numpy, Rust extension) or use a compiled language.
- "Works on my machine" binaries: you shipped
darwin/arm64tolinux/amd64. - Debugging: a JIT stack trace may not match source lines until you remember source maps / debug symbols.
[!NOTE] "Is Python compiled or interpreted?" The precise answer: compiled to bytecode, then interpreted (CPython). Interviewers want that nuance, not a fight.
What to remember
- CPUs run machine code. Compilers, interpreters, and JITs are different schedules for producing it.
- AOT: fast run, per-platform binary. Interpreter/bytecode: portable, slower hot paths. JIT: slow start, fast hot paths.
- Language marketing ≠ pipeline. Look at what the implementation does.
- TS types and Python types (hints) are not what the CPU sees unless a compiler uses them.