skip to content

A team generates Java classes from a domain model, but developers need to hand-write business logic inside those same generated files. If the model changes and the generator runs again, how do you avoid destroying the hand-written code, and what does this problem reveal about round-trip engineering?

level: middleimportance: must knowfreq 65%

answer

  1. protected regions = markers inside one file
  2. generation-gap = base class + hand-written subclass
  3. @generated NOT tag (EMF)
  4. merging requires parsing prior output
  5. splitting needs no merge at all

basics

~20 s

Mark off areas in the generated file as 'protected' or split hand-written code into a separate file the generator never touches. Round-trip engineering is about letting generated output and manual edits coexist without one wiping out the other.

solid answer

~60 s

The two standard solutions are protected regions and file splitting. Protected regions are marker comments (e.g. 'BEGIN USER CODE' / 'END USER CODE') inside a generated file; on regeneration, the generator re-emits everything outside the markers and preserves whatever text sits between them, which requires the generator to parse its own previous output. File splitting instead uses the generation-gap pattern: the generator owns one file or class (often an abstract base or partial class) entirely, and hand-written logic lives in a separate file the generator never writes to and that extends or implements the generated one, so there is nothing to merge because the two files have disjoint ownership. Round-trip engineering is the broader problem this exemplifies: keeping a model and its generated artifacts consistent as both evolve, ideally letting changes flow in both directions, model-to-code and code-to-model, without loss. Protected regions and file splitting solve the easy direction (preserve manual code across regeneration); true bidirectional round-tripping, propagating a manual code edit back into the source model, is much harder and rarely fully automated in practice.

go deeper

for a junior

Should recognize that regenerating a file naively can destroy hand-written edits and that some convention (markers or separate files) is needed to prevent it.

for a middle

Should be able to describe both protected regions and the generation-gap pattern concretely, including a real example like an abstract base class plus a hand-written subclass.

for a senior

Should articulate the fragility trade-offs of each mechanism (marker corruption vs required language support) and recognize that these only solve the model-to-code direction of round-tripping.

for a principal

Should reason about which mechanism to standardize on across a codebase or organization given the target languages in use, and understand why full bidirectional round-trip engineering remains largely unsolved and why that limits how aggressively a team should rely on regenerate-and-merge workflows.

## The problem the term names **Round-trip engineering** names the general problem of keeping two representations, a model and its generated code, consistent as both change over time, ideally in both directions: model changes should regenerate code, and in some visions, code changes should be reflected back into the model. In practice, most tooling only solves half of that problem well: preserving hand-written additions when the model-driven side regenerates. That narrower problem is what shows up constantly in real projects, and it has two dominant solutions, each with a different mechanism and a different failure profile: - **protected regions** - the **generation-gap pattern** ## Protected regions Protected regions work by convention inside a single generated file. The generator emits a file as usual, but wraps designated insertion points with recognizable markers, for example `// BEGIN USER CODE` and `// END USER CODE`, often keyed by a stable identifier tied to the model element that produced that region. On the next generation run, before writing the new file, the generator (or a merge step): 1. first reads the existing file on disk, 2. extracts whatever text currently sits between each pair of markers, 3. and splices that text back into the newly generated content at the matching markers, overwriting everything else. This requires the generator to essentially parse its own previous output, at least well enough to find marker pairs reliably, which is why marker syntax has to be something the generator can locate unambiguously (a comment format the target language will never itself produce incidentally). Tools like the old Eclipse EMF generator's `@generated NOT` javadoc tag work on a related principle: a method tagged `@generated` is safe to overwrite, but if a developer edits the method body and the tool detects the tag was manually changed to `@generated NOT`, it treats the whole method as hand-owned and skips regenerating it. ## The generation-gap pattern The generation-gap pattern takes a structurally different approach: instead of merging text inside one file, it splits ownership across two files so there is nothing to merge. The generator fully owns one artifact, commonly an abstract base class or one half of a partial class (in languages that support partial classes, like C#), regenerating it wholesale every time with no preserved content. Hand-written logic lives in a second file that the generator never touches, which extends, implements, or is the other half of the generated artifact. Because the two files have disjoint, non-overlapping ownership, regeneration is trivially safe: delete and rewrite the generated file completely, and the hand-written file is simply never opened by the generator. This is generally considered more robust than protected regions because it does not depend on marker-parsing correctness, but it does require the target language and the project's structure to support the split (partial classes, or a base-class-plus-subclass convention that the team consistently follows). ## The trade-off: fragility versus flexibility The trade-off between the two is fragility versus flexibility. - **Protected regions** let hand-written code live physically close to, or interleaved with, generated code, which some teams find more convenient to navigate, but the mechanism is fragile: a marker accidentally deleted, renamed, or duplicated during a manual edit silently breaks the merge, and the next regeneration either destroys the hand-written code (if the marker is gone) or leaves stale duplicate content (if the merge misfires). - **File splitting** is more robust and less error-prone, but it forces a specific file/class layout on the codebase and, in languages without partial classes, means every generated method a developer wants to override must have been designed as an overridable extension point ahead of time, which the generator's author has to anticipate. ## Where it shows up The production failure that both mechanisms exist to prevent, and that still happens when a team skips them entirely, is straightforward and painful: a developer opens a generated file, adds real business logic directly into a generated method because there is no marker or split file offering a safe place to do it, the model changes for an unrelated reason weeks later, someone reruns the generator, and the hand-written logic is silently gone, discovered only when tests fail or, worse, in production. A concrete, widely known real-world instance of these mechanisms is JPA/Hibernate entity generation from a database schema or UML model: generators commonly emit an abstract `BaseCustomer` class regenerated on every schema change, plus a `Customer extends BaseCustomer` file created once and never regenerated, letting developers freely add business methods to `Customer` with zero risk from future regenerations, this is the generation-gap pattern in its most common industrial form.

  • Why is the generation-gap pattern generally considered more robust than protected regions?
    Protected regions depend on marker text surviving intact through manual edits, tool reformatting, and version control merges, any of which can corrupt or delete a marker and silently break the next regeneration. The generation-gap pattern has no markers to corrupt because the generated and hand-written content live in physically separate files with disjoint ownership, so regeneration can safely be a full overwrite every time.
  • What has to be true about the target language or project structure for the generation-gap pattern to work well?
    The language needs some mechanism for one artifact to cleanly extend or complete another without copying code, such as inheritance (abstract base class plus subclass), interfaces, or a genuine partial-class feature. If the language or team convention cannot express 'this file is definitely never touched by the generator, and this other file definitely always is,' the pattern collapses back into ad hoc file organization that developers can still violate by accident.
  • Does solving 'preserve hand-written code across regeneration' also solve full bidirectional round-trip engineering?
    No. Preserving hand-written code across regeneration only protects the model-to-code direction of consistency; it says nothing about propagating a hand-written code change back into the source model. True bidirectional round-tripping, where editing generated code updates the model automatically, is a much harder, largely unsolved problem in general-purpose tooling and is usually only approximated in narrow, tool-specific cases.

Protected regions are like editing a shared document where certain paragraphs are locked as 'do not overwrite' by a bracket comment, and the next auto-formatter run has to respect those brackets. The generation-gap pattern is like keeping your personal notes in a separate notebook that references the textbook chapter by page number, so reprinting a new edition of the textbook never touches your notebook at all.

saying these in an interview costs you the question

  • Suggests just re-running the generator and manually diffing every file forever
  • Does not know generators can parse their own prior output to preserve marked regions
  • Thinks protected regions and file splitting are the same mechanism
  • Assumes round-trip engineering is fully solved by any mainstream tool
  • Has no answer for what happens when a marker is accidentally deleted

context