skip to content

Two common ways to build a model-to-text generator are template-based generation, filling text templates with model values, and rule-based generation, applying declarative mapping rules to produce output. What is the mechanical difference between the two approaches, and when would you choose one over the other?

level: middleimportance: must knowfreq 60%

answer

  1. templates = output-shaped text with holes
  2. rules = pattern-match then produce
  3. Acceleo/JET = template, ATL/QVT = rule
  4. rules compose via inheritance/guards
  5. templates readable locally, rules composable globally

basics

~20 s

Template-based generation fills in text templates that look like the output, with holes for model data. Rule-based generation runs a set of if-this-then-produce-that rules that decide what to build. Templates are easier to read; rules scale better for complex mappings.

solid answer

~50 s

Template-based generation, as in Acceleo or JET, starts from output-shaped text with embedded expressions and control structures (loops, conditionals) that pull values from the model; a developer reading the template sees roughly what the generated file will look like. Rule-based generation, as in QVT or ATL used for M2T-flavored mappings, declares pattern-matching rules over the source metamodel, each rule says "when you see this kind of source element in this context, produce this target construct", and the engine figures out execution order via rule matching and dependency resolution rather than the developer scripting a linear pass. Templates are more approachable and better when the mapping is close to a straightforward substitution; rule-based approaches shine when many small, reusable, composable mapping rules need to interact, be overridden, or be inherited, which matters as the number of source metaclasses and target variants grows.

go deeper

for a junior

Should be able to describe templates as 'text with blanks filled from the model' and recognize that rule-based tools decide output based on matching conditions instead.

for a middle

Should name concrete tools for each style and describe at least one situation where a template becomes unwieldy versus one where rules add unneeded complexity.

for a senior

Should discuss composability trade-offs (rule inheritance, guard conditions, implicit dispatch) and describe hybrid pipelines that use both styles for different pipeline stages.

for a principal

Should weigh long-term maintainability of a generator codebase across a growing number of edge cases and platform variants, and justify an organizational choice of generation style based on projected mapping complexity, not just current requirements.

## Two mental models for the same job Template-based and rule-based generation are two different mental models for the same job: turning a source model into text. In **template-based generation**, the artifact a developer authors looks like the target output with holes cut into it. `Acceleo`, built on the OMG MOF Model to Text (MTL) standard, is the canonical example: a template file mixes literal Java (or SQL, or XML) text with tags like `[for (a : Attribute | c.attributes)]` and `[a.name/]` that iterate over model elements and splice in their properties. `JET` and Xtend's template facility work the same way, closer to JSP-style text-with-embedded-expressions than to a general programming language. The mental model is "write the output, then parameterize it", which is why templates read naturally to anyone who already knows what the target code should look like; you can often hand a template to a developer unfamiliar with the source metamodel and they will still understand the shape of what gets produced. ## How rule-based generation inverts it **Rule-based generation** inverts this. Instead of writing output-shaped text, you declare a set of rules keyed on source-model patterns: - `QVT-Relations` rules state a relation that must hold between a source pattern and a target pattern (used more commonly for M2M, but the same rule-matching mental model underlies M2T-oriented tools too). - `ATL` rules declare, for each kind of source element, one or more target elements to create along with bindings for their properties. The engine, not the developer, decides which rule fires for which source element and in what order, based on pattern matching and, for M2M-flavored engines, on target-element uniqueness (an ATL rule, once it has created a target element for a given source element, returns that same target element if asked again, which is what makes reference resolution across rules work without manual bookkeeping). This inversion matters once a mapping stops being one-to-one: if a single kind of source element can map differently depending on context (an association mapped to a foreign key in one case and to a join table in another), a rule set can express that as separate, independently testable rules with guard conditions, where a monolithic template would need deeply nested conditionals that quickly become unreadable. ## The trade-off: discoverability versus composability The trade-off is discoverability versus composability. - **A template is locally readable**: open the file, and you see, roughly, the shape of the output, which lowers the barrier for a new team member or for a non-specialist stakeholder reviewing what will be generated. - **A rule set is globally composable**: rules can be organized into libraries, refined through inheritance (ATL supports rule inheritance and lazy rules that are only invoked, not automatically matched), and reused across transformations targeting related metamodels, but understanding what a given source element will produce means mentally simulating which rule matches, which is harder to do by inspection alone, especially once rule priority or guard conditions are involved. Template engines usually also make it easier to control exact whitespace and formatting of the output, since you are quite literally writing text, whereas rule-based tools sometimes need a separate pretty-printing pass because the engine's job is producing correct target structure, not correct target syntax layout. ## Failure modes in production - In production, **template-based generators** tend to fail by becoming unmaintainable spaghetti: as edge cases accumulate, a template grows dense thickets of nested for-loops and if-conditions that are hard to test in isolation, because the natural unit of testing, one template file, entangles many source-model shapes. - **Rule-based generators** tend to fail differently: because rule firing is implicit, a change to one rule's guard condition can silently stop another rule from matching in a context nobody anticipated, and debugging why a particular target element was not produced means tracing the engine's rule-matching decisions rather than reading a straight-line template top to bottom. Teams sometimes mitigate this by keeping a hybrid: rule-based logic decides what to build and how pieces relate, template-based logic renders the final textual layout of each piece, which is roughly what happens when an ATL or QVT M2M pass produces a platform-specific model and Acceleo templates then render that model to text. ## Where it shows up A concrete real-world illustration: generating a REST client SDK from an OpenAPI-derived model. - A **template-based approach** (much like OpenAPI Generator's Mustache templates) writes one template per target construct, a class template, a method template, and loops over operations and parameters. - A **rule-based approach** would instead declare, for each operation kind (GET with query params, POST with a body, etc.), a distinct mapping rule to a client method, letting the engine dispatch on operation shape. The template approach is faster to get started and easier for contributors to tweak formatting; the rule-based approach scales better once the number of operation variants and edge cases (authentication schemes, pagination styles, content types) grows large enough that a handful of nested conditionals in one template would no longer be tractable.

  • Why do some pipelines use both a rule-based M2M step and a template-based M2T step instead of picking one style throughout?
    Rules are good at deciding structure, what target elements should exist and how they relate, especially when that decision depends on matching many different source-element shapes. Templates are good at rendering final textual layout once the structure is already decided. Using rules for the M2M step and templates for the final M2T step plays to each style's strength rather than forcing one tool to do both jobs.
  • What makes debugging a rule-based generator harder than debugging a template-based one?
    In a template, execution order is exactly the order you wrote the loops and conditionals, so tracing why a particular line of output appeared is a straight read of the file. In a rule-based engine, which rule fires for a given source element is decided implicitly by pattern matching, guard conditions, and rule priority, so understanding why an expected target element did not appear means reasoning about the engine's matching logic rather than reading top to bottom.
  • Would you use a rule-based approach for a very simple one-to-one mapping, like turning each database column into one Java field?
    Generally no; a simple, uniform one-to-one mapping is exactly the case where a template's directness pays off, since there is no branching mapping logic for rules to help organize. Reaching for a rule-based engine there adds conceptual overhead, learning the engine's matching semantics, for a mapping that a single loop in a template already expresses clearly.

A template is like a fill-in-the-blank form letter: you can read the letter's exact wording just by looking at it. A rule set is like a tax code: a collection of separate clauses that each fire under specific conditions, and to know what your final tax bill looks like you have to work out which clauses apply to your situation, not read one document top to bottom.

saying these in an interview costs you the question

  • Thinks template-based and rule-based are just different syntaxes for the same execution model
  • Cannot explain why rule firing order is implicit rather than explicit
  • Believes rule-based tools cannot produce text output at all
  • Has never seen a nested-conditional template become hard to maintain
  • Assumes one approach is strictly better in all cases

context