Skip to content

Metaprogramming Roadmap

"Code that writes code is the highest form of code reuse — and the easiest way to ship code nobody can debug."

This roadmap is about programs that manipulate programs: reflection, macros, code generation, annotation processing, AST rewriting. Used well, metaprogramming eliminates entire categories of boilerplate. Used badly, it produces magic that no one — including the author six months later — can read.

Looking for the machinery underneath (ASTs, parsers, IRs)? See Compilers & Interpreters.

Looking for codegen at build time (protobuf, gRPC, go generate)? Sections 04 and 05 below cover this directly.


Why a Dedicated Roadmap

Every mainstream language has metaprogramming — but they disagree wildly on when and how:

  • Rust does it at compile time with macros (macro_rules!, procedural macros, syn / quote)
  • Java does it at compile time with annotation processors and at runtime with reflection
  • Python does it at runtime with __getattr__, decorators, metaclasses, inspect
  • Go has almost no metaprogramming — go generate and a bit of reflect — and the language designers consider that a feature

Understanding the spectrum makes you fluent across stacks and lets you judge "is this annotation magic actually buying us anything?"

Roadmap Question it answers
Design Patterns What structures recur in OO code?
Type Systems What can the compiler prove?
Metaprogramming (this) When is it worth writing code that writes code?

Sections

# Topic Focus
01 What Metaprogramming Is (and Isn't) Reflection, codegen, macros, eval; runtime vs compile-time
02 Reflection Inspecting types and values at runtime — Java java.lang.reflect, Go reflect, Python inspect
03 Annotations & Decorators Java annotations + APT, Python decorators, attribute-driven behavior
04 Build-Time Code Generation go generate, protoc, OpenAPI codegen, source-level templating
05 Macros Lisp's homoiconicity, Rust macro_rules!, Rust procedural macros (syn / quote), C preprocessor (briefly)
06 Metaclasses & Class-Level Magic Python metaclasses, Ruby method_missing, Smalltalk influences
07 Dynamic Dispatch & Proxies JDK proxies, byte-buddy / ASM, Python __getattr__, "duck typing" mechanisms
08 DSLs via Metaprogramming Building internal DSLs with operator overloading, builder patterns, or macros
09 When NOT to Metaprogram Cognitive cost, debuggability, IDE support, "the framework knows but I don't"
10 Compile-Time vs Runtime Trade-offs Startup time, binary size, observability, refactoring safety

Languages

The whole roadmap is a comparison: Rust (compile-time, type-checked macros), Java (annotation processing + reflection), Python (runtime, very dynamic), and Go (deliberately limited — go generate + reflect). Studying all four side-by-side makes the design trade-offs visible.


Status

Structure defined; content pending.


References

  • On Lisp — Paul Graham (the macro classic)
  • Let Over Lambda — Doug Hoyte
  • Rust Reference — Procedural Macros and Declarative Macros
  • Java Reflection in Action — Forman & Forman
  • Fluent Python — Luciano Ramalho (metaclasses, descriptors, decorators)

Project Context

Part of the Senior Project — a personal effort to consolidate the essential knowledge of software engineering in one place.