Programming Languages

Compiled vs Interpreted

How source text becomes behavior: translated ahead of time, executed by a program, or both. A property of implementations, not languages.

Level: beginner

What is it?

A compiled language is translated ahead of time into machine code (C, C++, Rust, Go) that the CPU runs directly. An interpreted language is executed by another program, the interpreter, which reads source or bytecode at run time (classic Python and Ruby). Most modern systems mix the two: Java and C# compile to bytecode that a virtual machine interprets and just-in-time compiles, and JavaScript engines do the same. 'Compiled' and 'interpreted' describe an implementation, not the language.

Why does it matter?

The execution model explains start-up time, peak speed, how you ship software, what an error message can tell you, and why a language can or cannot be embedded, sandboxed, or run in a browser. It is also the first step toward understanding what a compiler actually does.

Mental model

Translating a book. A compiler translates the whole book once, and every reader gets the finished translation. An interpreter is a live translator who speaks each sentence as it is read: no wait at the start, but the translator is present every time.

How it works

  • Ahead-of-time compilation produces a native executable: fast start, fast run, but built per platform.
  • An interpreter runs on any machine that has it, and supports a fast edit-run loop, at the cost of speed.
  • Bytecode plus a VM is the middle path: portable bytecode, with a JIT compiling hot code to machine code as the program runs.
  • Compilers catch a class of errors (syntax, types) before the program runs. Interpreters find them when the line executes.
  • The same language can have several implementations: CPython and PyPy for Python, GCC and Clang for C.

Steps

  • Source text
  • Lex and parse
  • Type-check and optimize
  • Emit machine code or bytecode
  • CPU or virtual machine runs it

Code example: CPython compiles to bytecode, then interprets it

import dis

def add(a, b):
    return a + b

dis.dis(add)   # prints the bytecode instructions the interpreter will run

Real-world usage

  • Picking a language for a system service versus a script
  • Understanding why a Docker image for Go is tiny and one for Python needs an interpreter
  • Reading compiler and interpreter error messages

Common mistakes

  • Saying 'Python is slow because it is interpreted' — the interpreter loop and dynamic dispatch are the cost, and other implementations differ.
  • Assuming compiled means the program cannot be changed at run time.
  • Benchmarking a JIT language without warming it up.

Interview questions

What is the difference between compiled and interpreted languages?

Compiled code is translated to machine code before it runs; interpreted code is executed by another program at run time. Many real systems combine them, such as bytecode with a JIT.

What does a JIT compiler do?

It watches a running program, finds hot code, and compiles it to machine code at run time using observed types and branches, often generating faster code than a static compile could assume.

Why can a compiled language still be slow to start or run poorly?

Start-up can include dynamic linking or runtime initialization, and generated code can be limited by memory layout, cache behavior, or algorithms. Compilation removes interpreter overhead, not bad design.

ApproachExamplesStart-upPeak speedYou ship
Ahead-of-time compiledC, C++, Go, RustFastHighA native binary per platform
Bytecode + VM with JITJava, C#, JavaScript (V8)Slower warm-upHigh once warmBytecode plus a runtime
InterpretedCPython, Ruby, shellFastLowerSource plus an interpreter

Prerequisites

Related concepts

Continue learning

Browse the full computer science library