# Let's Talk Code Toolings: Compilers

## **Introduction**

In modern software development, creating efficient, optimised code is crucial for building fast, scalable applications. To achieve this, developers rely on a variety of tools that help transform and optimise code before it reaches the browser or server. Among these tools, **compilers** play a vital role in improving performance by converting high-level code into a form that a computer can understand and execute.

In this article, we’ll dive deep into compilers—exploring how they work, why they’re significant in development, and how they transform source code into machine-readable instructions that run faster and more efficiently.

**Let’s dive in!**

## **What is a Compiler?**

A **compiler** is a tool that transforms high-level programming code (like JavaScript, TypeScript, or C++) into a lower-level language that a computer can understand and execute. This lower-level language can either be [**machine code**](https://en.wikipedia.org/wiki/Machine_code) (which the computer's CPU directly executes) or [**bytecode**](https://en.wikipedia.org/wiki/Bytecode) (which is executed by a [virtual machine](https://en.wikipedia.org/wiki/Virtual_machine)).

**Think of it like this:  
Your Code** (Source Code) → **Compiler** (Processing & Optimisation) → **Machine Code** (Executable Instructions)

This entire process helps improve performance by making the code run faster and more efficiently on a computer.

## **Importance of Compilers**

1. ### **Improving Performance**
    
    By optimising the code before execution (through processes like removing redundant calculations), compilers ensure that the program runs efficiently. This reduces lag and increases speed.
    
2. ### Cross-Platform Compatibility
    
    Compilers enable software written in one programming language to be executed on different platforms (Windows, macOS, Linux). By converting code into machine code or bytecode specific to a platform, developers can ensure their applications work across various environments.
    
3. ### Enabling New Technologies
    
    Compilers make it possible to run languages like WebAssembly in the browser. WebAssembly allows high-performance execution of code written in languages like C, C++, and Rust, expanding the types of applications that can run directly in a web browser.
    

## **Examples of Compilers**

| **Compiler** | **Language Input** | **Compilation Output** |
| --- | --- | --- |
| [**V8 Engine**](https://v8.dev/docs) | JavaScript | Machine Code |
| [**Emscripten**](https://emscripten.org/docs/introducing_emscripten/about_emscripten.html) | C++ | [WebAssembly (Wasm)](https://developer.mozilla.org/en-US/docs/WebAssembly) |
| [**Dart Compiler**](https://dart.dev/overview#native-platform) | Dart | Machine Code, JavaScript, Wasm |
| [**Rust Compiler**](https://rustc-dev-guide.rust-lang.org/overview.html) | Rust | WebAssembly (Wasm) |
| [**AssemblyScript Compiler**](https://www.assemblyscript.org/compiler.html#compiler-options) | AssemblyScript | WebAssembly (Wasm) |

## **How Do Compilers Work?**

Compilers typically go through the following stages:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1739397907012/1659d021-1380-48be-9f81-1b822c2f8ea0.png align="center")

1. ### **Lexical Analysis**
    
    **What Happens Here?**
    
    The compiler breaks the source code into small, meaningful pieces called **tokens**.
    
    **Output Example:**
    
    For the code `let a = 5;`, the tokens are:
    
    * `let` (keyword)
        
    * `a` (identifier)
        
    * `=` (operator)
        
    * `5` (number)
        
    * `;` (symbol)
        
    
    **Why is it important?**
    
    Tokens simplify the code into manageable pieces for further processing.
    
2. ### **Syntax Analysis (Parsing)**
    
    **What Happens Here?**
    
    The compiler checks if the code follows the correct syntax of the programming language and builds an **Abstract Syntax Tree (AST)**.
    
    **Output Example:**
    
    For the code `let a = 5;`, the AST might look like this:
    
    ```json
    {
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": { "type": "Identifier", "name": "a" },
          "init": { "type": "Literal", "value": 5 }
        }
      ],
      "kind": "let"
    }
    ```
    
    **Why is it important?**
    
    The AST is a tree representation of the code's structure, making it easier to analyse and manipulate.
    
3. ### **Semantic Analysis**
    
    **What Happens Here?**
    
    The compiler checks if the code makes **logical sense** (e.g., no undeclared variables or type mismatches).
    
    **Output Example:**
    
    For the code `let b = a + "hello";`, the output is:
    
    * An error (e.g., "Cannot add a number and a string") if the code is invalid.
        
    * A validated AST if the code is logically correct.
        
    
    **Why is it important?**
    
    Ensures the code is not only syntactically correct but also logically sound.
    
4. ### **Optimisation**
    
    **What Happens Here?**
    
    The compiler improves the code’s performance by eliminating inefficiencies (e.g., removing redundant calculations).
    
    **Output Example:**
    
    For the code `let a = 5 + 3;`, the optimised output might be:
    
    ```basic
    MOV R1, 5      ; Load 5 into register R1
    MOV R2, 10     ; Load 10 into register R2
    ADD R1, R2     ; Add R1 and R2, store result in R1
    ```
    
    **Why is it important?**
    
    Optimised code runs faster and uses fewer resources.
    
5. ### **Code Generation**
    
    **What Happens Here?**
    
    The compiler converts the optimised code into **machine-readable instructions** (machine code).
    
    **Output Example:**
    
    For the code `let b = a + 10;`, the machine code might look like this:
    
    ```basic
    MOV R1, 5      ; Load 5 into register R1
    MOV R2, 10     ; Load 10 into register R2
    ADD R1, R2     ; Add R1 and R2, store result in R1
    ```
    
    **Why is it important?**
    
    Produces the final executable program that the computer can run.
    

## **Compilation Strategies**

There are two main strategies that is used to translate a program written in a high-level language into a format that a computer can understand. These are;

1. **Just-In-Time (JIT) Compilation**
    
2. **Ahead-Of-Time (AOT) Compilation**
    

Before we explore these strategies in detail, let’s define some key terms that will help us understand how they work.

* **Machine Code:** The **binary instructions** that a computer’s **Central Processing Unit (CPU)** directly understands and executes.
    
* **Bytecode:** A lower-level code representation that is not directly executed by a CPU but runs inside a virtual machine (e.g. Java bytecode runs inside the JVM, WebAssembly bytecode runs inside browsers).
    
* **Execution:** The fundamental process of **running a program** on a computer. Once the computer understands the code (through compilation or interpretation), it begins executing instructions to perform the program’s tasks.
    
* **Runtime**: The **period during which a program is actively running.** It is the phase when the compiled or interpreted code is being executed by the system.
    
* **Compilation:** A method of preparing code for execution by **translating high-level code** (e.g. JavaScript or Dart) into a **lower-level language** (such as machine code or bytecode) that the computer can efficiently execute.
    
* **Interpretation:** An alternative to compilation, where the code is **translated and executed line-by-line** instead of converting it into machine code beforehand.
    
* **Optimisation:** The process of improving code performance, such as making it run faster or use less memory.
    

With these terms in mind, let’s explore **JIT and AOT compilation** and how they impact web development.

### Just-In-Time (JIT) Compilation

Just-In-Time (JIT) compilation happens **while the program is running**. Instead of compiling the entire code before execution, a JIT compiler **translates code into machine code on demand**, only when it's needed.

At first glance, JIT compilation might seem similar to **interpretation**, but they work differently. Let’s explore how interpretation functions to better understand the differences.

### **How Interpretation Works**

1. The program **starts running immediately** (there’s no compilation step).
    
2. The interpreter **reads one line (or statement) of code** at a time.
    
3. It **translates and executes that line on the fly**.
    
4. This process **repeats for every line of the program**.
    

Since no compiled machine code is stored, the interpreter **repeats this process every time the program runs**, leading to slower execution speeds.

### **How JIT Compilation Works**

1. The program **starts running**, just like with an interpreter.
    
2. The JIT compiler **analyses which parts of the code are used most frequently**.
    
3. These frequently used parts are **compiled into machine code and stored**.
    
4. The next time those parts are needed, the **compiled version is executed instead of interpreting them again**.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1737592059594/19aad4e7-2bb8-4c44-85d4-9c8b1bd74472.png align="center")

This optimisation allows JIT compilers to reuse machine code, making execution much **faster than pure interpretation**.

As a result, JIT compilers are well-suited for **dynamic applications** that require real-time user interactions or handle constantly changing data, such as **web applications**, **games**, and **real-time data processing systems**. By compiling frequently used code during runtime, JIT allows applications to **adapt to their execution patterns**, enhancing performance over time and providing **faster, more responsive user experiences**.

### **Application of JIT Compilation:** [**JavaScript’s V8 Engine**](https://v8.dev/docs)

JavaScript was originally designed as an **interpreted language**, meaning its code was executed **line by line** without prior compilation. However, modern browsers (like **Google Chrome**) and runtimes (like **Node.js**) use **JIT compilation** to significantly improve performance. This is made possible by **Google’s V8 engine**, which powers JavaScript execution.

**How JIT Works in the V8 Engine**

1. **Initial Execution**: The V8 engine starts by **interpreting** JavaScript code line by line.
    
2. **Hot Code Detection**: If a function is executed **multiple times**, V8 **identifies it as frequently used (hot code)**.
    
3. **Optimisation & Caching**: The JIT compiler **compiles the function into optimised machine code** and **stores it for future executions**. This eliminates the need for repeated interpretation.
    
4. **Further Optimisations**: Over time, V8 **continuously refines and optimises** frequently used code, making JavaScript execution even more efficient.
    

**Example: JIT Compilation in Action**

Let’s look at a simple JavaScript function:

```javascript
function add(a, b) {
  return a + b;
}

console.log(add(8, 10)); // Outputs: 18
```

**What Happens Behind the Scenes?**

1. **First Run**: V8 **interprets** the `add()` function and executes it.
    
2. **Repeated Execution**: If `add()` is called multiple times, V8 detects it as **hot code**.
    
3. **JIT Compilation**: V8 **compiles** `add()` into optimised machine code and stores it in memory.
    
4. **Subsequent Executions**: The next time `add()` is called, V8 **runs the compiled version directly**, skipping interpretation.
    

This approach makes JavaScript **much faster** than traditional interpreted languages.

### **Ahead-Of-Time (AOT) Compilation**

AOT compilation happens **before runtime**, meaning the code is fully compiled into machine code **before the program starts running**. This results in faster execution because the program doesn’t need to compile anything while running.

**How AOT Compilation Works**

1. The entire program is **compiled before execution**, producing an optimised binary file.
    
2. The compiled file is then **executed directly** without additional compilation steps.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1737592079669/ce22ad2a-b7a7-4819-b3c8-5e0753103317.png align="center")

This approach makes AOT perfect for applications that need **fast and predictable performance**, such as **mobile apps**, **production web frameworks**, and environments where **startup speed** is critical. By **pre-compiling code ahead of time**, AOT reduces runtime work, making it especially useful for apps with **stable, well-defined processes** that don’t require frequent changes while running.

### **Application of AOT Compilation:** [**Angular AOT Compiler**](https://angular.dev/tools/cli/aot-compiler)

Angular uses **Ahead-of-Time (AOT) compilation** to improve web app performance by **compiling templates and TypeScript before deployment**. Instead of letting the browser compile templates at runtime, Angular does this work **during the build phase**, ensuring the app loads faster when a user accesses it.

#### **How Angular AOT Compilation Works**

1. **Build Phase (Before Deployment):**
    
    * When you run `ng build --aot`, Angular compiles the **HTML templates and TypeScript** into optimised JavaScript.
        
    * This means the browser **receives only precompiled JavaScript**, not templates or TypeScript.
        
2. **Production Deployment:**
    
    * Since templates are already compiled, the browser **doesn’t need to process them at runtime**, leading to faster execution.
        

**Example:** Angular Component Before AOT Compilation

```javascript
@Component({
  selector: 'app-hello',
  template: `<h1>Hello, {{name}}!</h1>`
})
export class HelloComponent {
  name = "World";
}
```

**What Happens in AOT?**

1. **Build Time Compilation (Before Deployment)**
    
    * Angular compiles this **before it reaches the browser**, replacing `{{name}}` with a **direct JavaScript expression**.
        
2. **Optimised Output**
    
    * Instead of shipping templates and compiling them in the browser, Angular generates **efficient JavaScript code** in advance.
        
3. **Faster Execution in the Browser**
    
    * Since templates are precompiled, the browser **skips extra processing** and directly runs the optimised JavaScript.
        

## **JIT vs AOT: Key Differences**

| **Feature** | **JIT Compilation** | **AOT Compilation** |
| --- | --- | --- |
| **Compilation Time** | Happens **at runtime** | Happens **before execution** |
| **Performance** | Starts slower but optimises over time | Faster startup, optimised beforehand |
| **Use Cases** | Dynamic apps, browsers, JavaScript | Mobile apps, WebAssembly, production frameworks |
| **Examples** | V8 (JavaScript), WebAssembly JIT | Angular, Flutter (Dart), Rust to WebAssembly |

## **In Conclusion,**

Compilers are essential tools that convert high-level code into machine-readable instructions, enhancing performance and ensuring efficient execution.

In this article, we explored two primary compilation strategies: **Just-In-Time (JIT)** and **Ahead-Of-Time (AOT)**. **JIT** compiles code during runtime, optimising performance for dynamic applications, while **AOT** compiles code ahead of time, leading to faster startup times and more efficient execution for performance-critical apps.

By understanding these strategies, developers can optimise their code for faster, more scalable software. Choosing the right compilation approach can significantly boost your application's speed and efficiency.
