Real Pipelines
Python, JavaScript, TypeScript, C++, Rust and Go: four genuinely different routes from source to behavior, compared without pretending they are the same.
Running a `.py` file compiles it. CPython tokenizes, parses, builds a symbol table and emits bytecode into a code object before a single statement executes — and then a stack machine written in C runs that bytecode.
A modern JavaScript engine parses lazily, executes bytecode immediately, watches what actually happens, and recompiles the hot parts into native code with the observed types baked in — then unbakes them when the observation turns out to have been wrong.
TypeScript parses, type-checks and emits — and the type information is generally erased from the emitted JavaScript. Checking and emitting are separate concerns, which is why tools that skip checking entirely can still produce correct output.
Preprocessor, compiler, assembler, linker: four programs, not one. The translation unit is the compilation boundary, headers are copied into every unit that includes them, and the linker is the only stage that sees the whole program.
A separate language that runs before the compiler and understands nothing about C++. It copies text, substitutes text and deletes text — and every problem it causes traces back to that one property.
C++ templates are compile-time generic programming by code generation: one template, one concrete function or class per type used. Checking happens at instantiation, which is why an error in your call site is reported inside the library.
One template becomes one concrete function per set of arguments used, in every translation unit that used it — and then the linker throws nearly all of those copies away. The bill arrives as compile time, object-file size and link work, in that order.
`constexpr`, `consteval`, `constinit`, Rust's `const fn` and Zig's `comptime` are all one idea: the compiler contains an interpreter for its own language, and work moved into it disappears from the running program and reappears in the build.
Rust runs a program through more distinct representations than any other mainstream compiler, and each one exists to make a specific check possible: traits need types, borrow checking needs a control-flow graph, and monomorphization needs both. LLVM only sees the last of it.
Go's compiler is fast because the language was designed to let it be: no headers, no textual inclusion, a strictly acyclic import graph, a compact export summary per package, and a deliberately small feature set. It has its own backend, and it emits one self-contained binary.
One trivial program — add two numbers, print the result — in C++, JavaScript, TypeScript and Python. Four genuinely different routes to the same six characters of output, and the differences decide what is checked, what survives to run time, and what has to be installed on the machine.