Documentation Index
Fetch the complete documentation index at: https://www.aidonow.com/llms.txt
Use this file to discover all available pages before exploring further.
Executive Summary
Repository boilerplate for event-sourced domain entities follows a structurally uniform pattern that is amenable to procedural macro code generation. Across seven domain entities, manual repository implementations totaled 4,977 lines; after macro-driven refactoring, the equivalent functionality required 275 lines of declaration—a 94% reduction. The macro infrastructure itself comprises 2,917 lines of implementation code written once and applied uniformly across all entities. Verifier analysis identified three defects in the initial implementation—missing multi-item pattern support, absent GSI query generation, and undefined cache invalidation semantics—all prior to production deployment. A critical security defect (missing tenant isolation validation in generatedsave() methods) was identified through manual cargo expand inspection and remediated before deployment. The net maintenance benefit is substantial: bug fixes propagate from one macro change to all consuming entities simultaneously, rather than requiring manual application across each entity’s repository implementation.
Key Findings
- Procedural macros provide a 94% line count reduction for structurally uniform repository patterns. The investment in macro infrastructure (2,917 lines) reaches break-even at approximately four entities and yields compounding returns for all subsequent entities.
- AI agents excel at pattern extraction and implementation; they require human guidance for abstraction boundary design. Pattern analysis across existing implementations was produced by an AI evaluator with high fidelity; the decision of which abstractions to expose as macro attributes required human judgment.
- Generated code must be explicitly inspected for security-critical invariants. A missing tenant isolation check in generated
save()methods was not caught by automated testing and required manualcargo expandreview. AI-generated code does not inherit security awareness from surrounding code patterns. - Macro error messages require developer-experience design that AI does not apply by default. Initial macro error messages were technically accurate but non-actionable; human revision was required to produce messages that specified the problem, the fix, and a correct usage example.
- Incremental migration outperforms bulk refactoring. Migrating one entity, verifying thoroughly, and applying lessons to the macro before proceeding to the next entity surfaced defects that a simultaneous multi-entity migration would have embedded undetected.
- Macro changes are amplified changes. A single modification to a macro affects every entity using that macro simultaneously; versioning, deprecation, and backward-compatibility discipline are required for macro APIs from first use.
1. Introduction: The Boilerplate Problem
Event-sourced domain entities in a multi-tenant SaaS platform require a consistent set of surrounding infrastructure: event replay and versioning logic, repository implementations for test (in-memory) and production (DynamoDB) environments, a caching decorator, and event accessor helpers. Across seven CRM domain entities (Account, Contact, Lead, Opportunity, Activity, Product, Address), this infrastructure totaled 4,977 lines of near-identical code. The practical costs of this volume are not merely aesthetic. When a defect is found in the DynamoDBsave() method pattern, it must be identified and corrected in seven repository implementations independently. When a development agent produces a new entity, it must replicate the pattern without deviation across all repository types. These costs compound as entity count grows.
The following section documents a representative manual implementation to establish the baseline:
2. Macro Design
2.1 Pattern Analysis
An AI evaluator agent was provided access to all seven existing repository implementations with the directive to identify common patterns and recommend a macro abstraction strategy. The agent’s output identified five structurally distinct patterns:- Event sourcing infrastructure — version tracking, uncommitted event accumulation, replay from event sequence
- Event accessor helpers — aggregate ID access, event type string representation, metadata methods
- In-memory repository — HashMap-backed CRUD with
Arc<RwLock<>>concurrency - DynamoDB repository —
put_item/get_itemwith entity-domain conversion and tenant isolation - Caching decorator — Moka-based read-through cache with write-invalidate semantics
2.2 Design Decision: Explicit Over Inferential
Two macro design approaches were evaluated: Option A (Inference-heavy): The macro auto-detects field names, event types, and access patterns from struct field inspection, requiring minimal annotations. Option B (Explicit annotation): All configuration is expressed through explicit attributes; the macro fails with a diagnostic compile error if required annotations are absent. Option B was selected. Inference-based macros fail at runtime with opaque errors when field names change or naming conventions differ from assumed defaults. Explicit annotation macros fail at compile time with actionable messages. For security-critical patterns such as tenant isolation, the preference for compile-time failures over runtime surprises is not merely stylistic—it has direct operational safety consequences.3. Implementation
3.1 The Five Macros
The macro implementation comprised 2,917 lines across five procedural macros: DomainAggregate — generates event replay, uncommitted event tracking, version management, and test fixture construction. Eliminates approximately 150 lines per entity. Usage:aggregate_id(), tenant_id(), event_type(), and event metadata accessors. Eliminates approximately 100 lines per entity.
InMemoryRepository — generates a full CRUD implementation backed by a thread-safe Arc<RwLock<HashMap>>. Eliminates approximately 200 lines per entity.
DynamoDbRepository — generates save, get, query, and delete methods with CapsuleClient integration, tenant isolation enforcement, and TransactWriteItems for atomic multi-item writes. Eliminates approximately 250 lines per entity.
CachedRepository — generates a Moka-backed caching decorator with TTL expiration, LRU eviction, and configurable cache invalidation strategy. Eliminates approximately 150 lines per entity.
3.2 Defects Identified During Verification
A Verifier agent operating from a fresh context reviewed the initial macro implementation and identified three defects before any entity migration occurred:3.3 Critical Security Defect in Generated Code
Manual inspection of the generated code usingcargo expand identified a security defect not caught by the Verifier:
aggregate.tenant_id matches self.client.tenant_id(), the generated save() method would silently write an entity belonging to one tenant into the table partition of another tenant if the caller provided a mismatched aggregate.
The corrected generated code:
Inspect generated code with
cargo expand for every macro that touches security-critical paths. AI-generated macro logic does not inherit security constraints from the surrounding codebase—it produces code that satisfies the functional specification it was given. The specification must explicitly include security invariants, and the generated output must be manually verified to encode them correctly.4. Migration
4.1 Methodology: Incremental Entity Migration
Migration proceeded one entity at a time, verifying completeness against the existing test suite before proceeding to the next entity. This approach was chosen explicitly over bulk migration, which would have made it impossible to attribute any test failures to a specific entity’s migration. The Account entity was migrated first as the template, producing the following result:4.2 Migration Results by Entity
| Entity | Lines Before | Lines After | Reduction | Migration Time |
|---|---|---|---|---|
| Account | 812 | 43 | 769 (95%) | 2h |
| Contact | 734 | 38 | 696 (95%) | 1.5h |
| Lead | 698 | 41 | 657 (94%) | 1.5h |
| Opportunity | 856 | 47 | 809 (94%) | 2h |
| Activity | 612 | 35 | 577 (94%) | 1h |
| Product | 723 | 39 | 684 (95%) | 1.5h |
| Address | 542 | 32 | 510 (94%) | 1h |
| Total | 4,977 | 275 | 4,702 (94%) | 11.5h |
4.3 Defect Surfaced During Migration
Attempting to migrate the final three entities (Product, Activity, Address) simultaneously—after four successful sequential migrations—resulted in 14 compilation errors. The Product entity introducedDecimal fields for pricing data; the DomainAggregate macro’s generated apply() method assumed all fields implemented Clone, which Decimal did not.
AI Verifier analysis of the compilation errors identified the root cause:
“Product entity has Decimal fields for pricing. DomainAggregate macro generates apply() method that assumes all fields are Clone. Decimal type doesn’t implement Clone trait. Recommendation: Require where T: Clone bound on all fields.”
Adding the where T: Clone constraint to the generated method signature resolved all 14 errors. This defect would not have been identified during the incremental migration of Account through Opportunity, none of which used Decimal fields.
5. Comparative Analysis: Manual vs. Macro-Driven Repository Implementation
| Dimension | Manual Implementation | Macro-Driven Implementation |
|---|---|---|
| Lines per entity | 600–850 | 30–50 |
| Time to add new entity | 4–6 hours | 1–1.5 hours |
| Bug fix propagation | Manual update to each entity (3.5 hours average) | Single macro change, all entities updated (20 minutes) |
| Security invariant enforcement | Depends on developer | Generated by macro, consistent across all entities |
| Discoverability of generated logic | Direct (source is visible) | Requires cargo expand |
| Compile-time error quality | Direct from Rust compiler | Dependent on macro error message quality |
| Abstraction learning curve | None (standard Rust) | Requires understanding macro attribute semantics |
6. Principles Established
Macros should encode invariants, not merely reduce typing. A macro that generates convenient boilerplate but permits incorrect patterns provides false confidence. The tenant isolation check in the generatedsave() method is the canonical example: the macro cannot be used to write a non-isolating save because the check is generated unconditionally.
Explicit annotation outperforms inference in production macros. Inferential macros that detect field names or access patterns automatically are brittle under refactoring. Explicit attribute macros that require the developer to state intent directly produce clearer compiler errors and more predictable generated code.
Generated code must be treated as code, not as a black box. The security defect identified through cargo expand would not have been caught by the functional test suite, because the tests operated on the generated methods—they could not see that the isolation check was absent. Security-critical invariants require direct inspection of generated code, not only behavioral testing.
Migrate incrementally. Each entity migration is a test of the macro against the specific characteristics of that entity. Simultaneous bulk migration defers defect discovery and makes root cause attribution difficult. Incremental migration converts each entity into a macro integration test that improves the macro for subsequent entities.
AI is more effective at implementing abstractions than designing them. The evaluator agent produced a high-quality pattern analysis and implementation plan; the implementation agent produced 2,917 lines of macro code that passed verification with three defects (all fixed before deployment). The abstraction boundary design—which patterns to expose as macros, which attributes to require, what invariants to encode—required human judgment. The implementation of those decisions was effectively delegated to AI.
7. Recommendations
- Do not write macros until five or more examples of the target pattern exist. Pattern extraction is the foundational step; AI pattern analysis requires sufficient examples to distinguish structural uniformity from coincidental similarity. Five examples provide the minimum corpus for reliable abstraction.
- Design the macro attribute API before implementation begins. The attribute names, required versus optional semantics, and error messages are the user interface for the macro. These decisions require human judgment about developer experience and architectural intent; they should not be delegated to implementation agents.
- Add compile-time validation for all required attributes and pattern constraints. Every required attribute should produce a diagnostic compile error with a corrective action and usage example if absent. Macros that fail silently or with generic errors impose debugging costs that negate the productivity benefit.
-
Run
cargo expandon the generated output for at least one entity before deploying any macro to production. This step is mandatory for macros that touch security-critical paths. The inspection should verify that security invariants (tenant isolation checks, encryption invocations, audit event emissions) are present in the generated code. - Maintain macro API backward compatibility with versioned attribute semantics. Macro changes propagate to all consuming entities simultaneously. A breaking change in a macro attribute is a simultaneous breaking change across every entity in the codebase. Semantic versioning and documented deprecation cycles are required from the first external consumer.
8. Conclusion
Procedural macro code generation is a high-leverage technique for eliminating structurally uniform boilerplate in event-sourced domain architectures. The evidence from this analysis demonstrates a 94% line count reduction across seven entities, a 4× reduction in time-to-add-new-entity, and a maintenance model where bug fixes propagate from a single source to all consuming entities simultaneously. The technique carries specific risks: generated code can omit security invariants that are not part of the functional specification, error message quality requires explicit developer-experience investment, and breaking changes to macro APIs have amplified blast radius. Each of these risks has a known mitigation documented in the recommendations above. As AI-assisted development tooling continues to mature, the combination of AI-driven pattern extraction and human-designed abstraction boundaries will likely become the standard model for macro development. Future work should investigate automated detection of additional boilerplate patterns that have accumulated to macro-eligible scale, and systematiccargo expand integration into CI pipelines to enforce ongoing inspection of generated security-critical paths.
All content represents personal learning from personal projects. Code examples are sanitized and generalized. No proprietary information is shared. Opinions are my own and do not reflect my employer’s views.