Definition
Metaprogramming is the practice of writing programs that manipulate other programs (or themselves) as data.
This means code can inspect, generate, or modify other code — at runtime or compile-time.
It’s sometimes called “programming about programs.”
Metaprogramming
Code that manipulates code
Key Capabilities
- Reflection → A program can examine its own structure (classes, functions, variables).
- Code Generation → Code that produces or modifies other code.
- Runtime Modification → Altering behavior while the program is running.
- Macros → Compile-time transformations that expand into new code (in languages like Lisp, Rust).
Examples
🐍 Python (Decorators)
def log(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log
def greet(name):
print(f"Hello, {name}!")
greet("Jonas")
# Output:
# Calling greet
# Hello, JonasHere, the @log decorator is metaprogramming — it wraps the greet function to inject logging behavior.
☕ Java (Reflection)
import java.lang.reflect.Method;
public class MetaExample {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.lang.String");
Method method = clazz.getMethod("substring", int.class, int.class);
String result = (String) method.invoke("Hello, World", 0, 5);
System.out.println(result); // "Hello"
}
}Java uses reflection to look up the substring method of String at runtime and call it dynamically.
🟨 JavaScript (Proxies)
const person = { name: "Jonas" };
const proxy = new Proxy(person, {
get(target, property) {
console.log(`Accessing property: ${property}`);
return target[property];
}
});
console.log(proxy.name);
// Logs: "Accessing property: name"
// Then returns "Jonas"Here, the Proxy intercepts property access dynamically — metaprogramming at runtime.
🌿 Lisp (Macros)
(defmacro unless (condition body)
`(if (not ,condition)
,body))
(unless (= 1 2)
(print "1 is not 2"))Macros transform code before it’s executed. This unless macro is essentially creating new syntax in the language.
🗄️ ORMs (Object–Relational Mappers)
ORMs are a real-world use of metaprogramming. They use reflection and code generation to map classes ↔ database tables, so you don’t have to write raw SQL.
Python (SQLAlchemy):
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
# ORM inspects the class and auto-generates SQL like:
# CREATE TABLE users (id INTEGER PRIMARY KEY, name VARCHAR)Java (Hibernate):
@Entity
@Table(name = "users")
public class User {
@Id
private int id;
private String name;
}
// Hibernate generates SQL automatically:
// CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR)Here, you define classes with annotations, and the ORM generates SQL for table creation, queries, and joins at runtime.
This is metaprogramming in action — your class definitions become data for code that writes SQL.
Why Use Metaprogramming?
- Reduce boilerplate (e.g., ORMs generating SQL queries automatically)
- Implement cross-cutting concerns (logging, transactions, security)
- Increase flexibility (dynamic behaviors without rewriting code)
- Enable frameworks (Spring, Django, Rails rely heavily on metaprogramming)