- Guiding Principles
- Roadmap
- Phase 1 — 🎯 Formalize the product thesis
- Phase 2 — 🧱 Define the stable zyn surface area
- Phase 3 — 🗂️ Restructure the workspace around capabilities
- Phase 4 — 🩺 Build a first-class diagnostics subsystem
- Phase 5 — 🧪 Expand testing into a full macro QA system
- Phase 6 — 🔨 Introduce structured builders
- Phase 7 — 🪟 Introduce a zyn syntax facade
- Phase 8 — 🧭 Introduce context-driven macro APIs
- Phase 9 — 🚚 Build the migration path
- Phase 10 — 🐶 Dogfood the framework internally
- Phase 11 — 🔬 Evaluate internal dependency replacement
- Long-Term Outcome
When I started zyn, my goal was not simply to build a nicer templating system for Rust procedural macros. The real goal is much larger:
Zyn should become the cohesive macro SDK for Rust.
Today, Rust macro development is powerful but fragmented. The typical stack looks like this:
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#fabd2f', 'primaryTextColor': '#282828', 'primaryBorderColor': '#d79921', 'lineColor': '#83a598', 'secondaryColor': '#3c3836', 'background': '#282828', 'nodeBorder': '#504945', 'textColor': '#ebdbb2'}}}%%
graph TD
classDef hub fill:#fabd2f,color:#282828,stroke:#d79921,font-weight:bold
classDef leaf fill:#3c3836,color:#ebdbb2,stroke:#504945
A[proc_macro] --> B[proc_macro2]
B --> C[syn]
B --> D[quote]
B --> E[proc_macro2_diagnostics]
B --> F[prettyplease]
G[trybuild]
H[cargo-expand]
class B hub
class A,C,D,E,F,G,H leaf
Each of these tools solves a specific problem well, but together they create a developer experience that feels stitched together rather than designed.
Zyn exists to unify that experience.
The long-term goal is to provide a single coherent framework that handles:
- macro entrypoints
- parsing
- syntax inspection
- code construction
- diagnostics
- testing
- debugging
- expansion tooling
Importantly, this does not require replacing the entire ecosystem immediately. In the near term, zyn may still rely on syn and quote internally. But those crates should become implementation details, not the mental model macro authors must learn.
My strategy is therefore progressive capture:
- own the authoring experience
- wrap the existing ecosystem behind zyn abstractions
- gradually replace internal dependencies where it makes sense
Before outlining the roadmap, these principles guide every design decision.
If writing a zyn macro still requires constantly thinking about:
syn::Typeproc_macro2::TokenStreamquote!- diagnostics compatibility caveats
then zyn has not truly unified the macro experience.
Zyn should provide its own conceptual model.
Today these areas are fragmented across multiple crates.
In zyn they should feel like parts of a single pipeline:
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#fabd2f', 'primaryTextColor': '#282828', 'primaryBorderColor': '#d79921', 'lineColor': '#83a598', 'background': '#282828', 'textColor': '#ebdbb2'}}}%%
flowchart LR
classDef step fill:#fabd2f,color:#282828,stroke:#d79921,font-weight:bold
Parse --> Inspect --> Transform --> Emit --> Test
class Parse,Inspect,Transform,Emit,Test step
The zyn! template system is an important ergonomic feature and adoption wedge. However, the long-term architecture should prioritize:
- structured builders
- composable components
- reusable transformations
Templates should exist as ergonomic sugar, not the only abstraction.
A macro framework should make it easy to produce compiler-quality diagnostics, including:
- labels
- notes
- help messages
- suggestions
- structured spans
Diagnostics should work consistently in:
- proc macro contexts
- tests
- debugging environments
Most macro authors already have existing code using syn and quote. Zyn should make it possible to adopt the framework incrementally, without rewriting entire macro crates.
Compatibility layers and migration guides are therefore a core part of the roadmap.
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#fabd2f', 'primaryTextColor': '#282828', 'primaryBorderColor': '#d79921', 'lineColor': '#83a598', 'background': '#282828', 'nodeBorder': '#504945', 'textColor': '#ebdbb2'}}}%%
graph TD
classDef active fill:#fabd2f,color:#282828,stroke:#d79921,font-weight:bold
classDef pending fill:#3c3836,color:#ebdbb2,stroke:#504945
P1["1 · Product thesis"] --> P2["2 · Stable surface area"]
P2 --> P3["3 · Workspace restructure"]
P3 --> P4["4 · Diagnostics subsystem"]
P4 --> P5["5 · Macro QA system"]
P5 --> P6["6 · Structured builders"]
P6 --> P7["7 · Syntax facade"]
P7 --> P8["8 · Context-driven APIs"]
P8 --> P9["9 · Migration path"]
P9 --> P10["10 · Dogfood internally"]
P10 --> P11["11 · Dependency replacement"]
click P1 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-1---formalize-the-product-thesis--in-progress"
click P2 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-2---define-the-stable-zyn-surface-area--in-progress"
click P3 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-3--️-restructure-the-workspace-around-capabilities--in-progress"
click P4 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-4---build-a-first-class-diagnostics-subsystem--in-progress"
click P5 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-5---expand-testing-into-a-full-macro-qa-system--in-progress"
click P6 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-6---introduce-structured-builders--in-progress"
click P7 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-7---introduce-a-zyn-syntax-facade--not-started"
click P8 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-8---introduce-context-driven-macro-apis--not-started"
click P9 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-9---build-the-migration-path--not-started"
click P10 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-10---dogfood-the-framework-internally--not-started"
click P11 "https://github.com/aacebo/zyn/blob/main/ROADMAP.md#phase-11---evaluate-internal-dependency-replacement--not-started"
class P1,P2,P3,P4,P5,P6 active
class P7,P8,P9,P10,P11 pending
The first step is to explicitly define what zyn is.
Zyn is not simply a templating crate. It is intended to be a macro SDK.
The repository should include a clear design document that defines:
- the long-term vision
- the scope of the framework
- the architectural principles
- explicit non-goals
This prevents feature drift and clarifies the project's direction.
- a canonical design document exists
- non-goals are clearly documented
- internal dependencies (
syn,quote, etc.) are explicitly treated as implementation details
Before replacing internals, I need to define the public conceptual model that macro authors interact with.
This means introducing zyn-owned types such as:
zyn::Tokens
zyn::Span
zyn::Diagnostic
zyn::Ast<T>
zyn::Result<T>
These types wrap the existing ecosystem primitives but provide a stable surface for users.
Initially these may simply delegate to proc_macro2, syn, and quote, but over time they will allow the framework to evolve independently of those crates.
- zyn exposes its own wrapper types
- examples prefer zyn abstractions over raw ecosystem types
-
synre-exports remain available but are clearly positioned as compatibility APIs
Currently the project contains:
zyn
zyn-core
zyn-derive
As the framework grows, the workspace should instead reflect capabilities rather than current implementation details.
Target structure:
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#fabd2f', 'primaryTextColor': '#282828', 'primaryBorderColor': '#d79921', 'lineColor': '#83a598', 'background': '#282828', 'nodeBorder': '#504945', 'textColor': '#ebdbb2'}}}%%
graph TD
classDef root fill:#fabd2f,color:#282828,stroke:#d79921,font-weight:bold
classDef crate fill:#3c3836,color:#ebdbb2,stroke:#504945
classDef compat fill:#32302f,color:#928374,stroke:#504945
zyn --> zyn-macro
zyn --> zyn-syntax
zyn --> zyn-build
zyn --> zyn-template
zyn --> zyn-diag
zyn --> zyn-test
zyn-syntax --> zyn-compat-syn
zyn-build --> zyn-compat-quote
class zyn root
class zyn-macro,zyn-syntax,zyn-build,zyn-template,zyn-diag,zyn-test crate
class zyn-compat-syn,zyn-compat-quote compat
This forces all dependencies on syn and quote to pass through clearly defined compatibility layers.
- direct usage of
synorquoteis isolated - public documentation focuses on framework capabilities rather than underlying crates
Diagnostics are one of the fastest ways to make macro development feel more professional.
Zyn already provides diagnostic macros like error!, warn!, note!, and help!. These should evolve into a complete diagnostics subsystem.
The system should support:
- structured diagnostics
- labeled spans
- notes and help text
- accumulation of multiple diagnostics
- rendering in multiple environments
Backends may include:
- proc macro emission
- test snapshot rendering
- debugging output
- diagnostics can be constructed without macros
- diagnostics can be snapshot tested
- backend implementations are internal details
Testing macro output today typically involves ad-hoc combinations of tools.
Zyn should provide a unified testing system supporting:
- expansion snapshots
- diagnostic snapshots
- structured AST assertions
- compile-fail tests
- golden-file comparisons
- zyn includes a dedicated testing crate
- examples demonstrate snapshot testing
- macro refactors can be validated through stable tests
At this stage, the framework should provide APIs for constructing Rust syntax programmatically.
Examples:
Item::impl_block(...)
Method::new(...)
Field::new(...)
Expr::call(...)
Path::from_segments(...)
Templates (zyn!) should remain available but should compile into or interoperate with these structured representations.
- complex macros can be implemented without templates
- builders and templates compose naturally
- documentation encourages builders for reusable transformations
Replacing syn entirely would be extremely expensive and unnecessary.
Instead, I plan to introduce a thin syntax facade that covers the subset of Rust syntax most macro authors work with.
Initially this facade will delegate to syn. Over time it may diverge where appropriate.
- macro inputs can be represented using zyn syntax types
- most examples no longer require direct use of
syn - conversion layers exist between zyn syntax and
syn
Zyn currently supports extractors for macro inputs.
The next step is to introduce explicit context objects:
MacroContext
DeriveContext
AttributeContext
FunctionContext
These contexts provide structured access to macro inputs while extractors remain available as ergonomic sugar.
- each macro type has a context object
- advanced documentation focuses on context-based APIs
- extractors remain optional
Adoption requires a clear migration story.
The project should provide guides such as:
- migrating from
quote!tozyn! - migrating
synparsing patterns to zyn syntax - incremental adoption strategies
Compatibility traits and conversions should make it easy to interoperate with existing code.
- comprehensive migration guides exist
- compatibility layers are documented and stable
Once the abstractions exist, the zyn codebase should begin using them extensively.
Key components to migrate include:
- pipes
- attribute parsing
- macro entrypoints
- debugging tools
- documentation examples
- most internal macro logic uses zyn abstractions
- direct usage of
quote!becomes rare -
synusage is isolated to parser/compat modules
Only after the previous phases are complete does it make sense to consider replacing internal dependencies.
At this point it may be possible to:
- replace parts of
quote - implement custom emitters
- develop alternative parsing strategies
However, these decisions should be driven by clear benefits rather than ideology.
The primary goal is to ensure that users of zyn never need to care about the underlying implementation.
If this roadmap succeeds, zyn will evolve from a templating system into a true macro development platform.
Instead of learning a patchwork of tools, macro authors will work within a single coherent framework:
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#fabd2f', 'primaryTextColor': '#282828', 'primaryBorderColor': '#d79921', 'lineColor': '#83a598', 'background': '#282828', 'textColor': '#ebdbb2'}}}%%
mindmap
root((zyn))
Syntax Inspection
Code Generation
First-class Diagnostics
Testing & Debugging
In other words, the experience of writing macros in Rust will feel like using a designed system rather than an ecosystem workaround.
That is the long-term goal of this project.