Bidirectional transformation frameworks (e.g., QVT, or lens-based approaches) aim to keep a model and a derived view (or two related models) consistent automatically in both directions. What makes writing a correct bidirectional transformation harder than writing two independent one-directional transformations, and when would a team be better off NOT attempting full bidirectionality?
answer
- lens laws: GetPut & PutGet
- QVT relations enforce both directions
- lossy views can't satisfy PutGet
- two hand-written functions ≠ true inverse
- silent data corruption on edge cases
basics
~20 sA good two-way sync tool must guarantee that going model→view→model (or the reverse) lands back exactly where you started, which is much harder to prove than just writing 'convert A to B' and separately 'convert B to A' by hand, because the two halves can quietly disagree. If changes are rare or one side is clearly authoritative, it's often not worth the complexity.
solid answer
~50 sWriting two independent one-directional transformations (A→B and B→A) only requires each function to be individually correct; nothing forces them to be inverses of each other, so composing them can lose or distort information. A true bidirectional transformation (as formalized by lenses, or implemented in QVT) must satisfy round-trip laws—typically that applying the forward then backward transformation returns the original source unchanged (GetPut) and that applying backward-then-forward on an updated target reproduces that update consistently (PutGet)—a much stronger correctness property to design and verify, especially when the transformation involves aggregation, filtering, or lossy projections where information genuinely doesn't fit both directions. Teams should avoid full bidirectionality when one side is clearly authoritative and changes rarely flow the other way, when the transformation is inherently lossy (a true round-trip is mathematically impossible), or when the volume/complexity of changes doesn't justify the cost of a correct bidirectional tool versus simpler one-way generation plus manual reconciliation.
go deeper
Should grasp intuitively that syncing changes in both directions is harder than syncing in just one, without needing to name lens laws.
Should recognize that two separately hand-written transformations aren't guaranteed to be true inverses of each other and can silently lose information.
Should describe the lens/GetPut/PutGet framing (even informally) and identify at least one scenario (lossy projection) where true bidirectionality is impossible without an arbitrary policy.
Should make the build-vs-skip call explicitly—weighing authoritative-side clarity, lossiness, and edit-volume economics—and recognize when investing in BX tooling/expertise is proportionate versus simpler one-way generation.
## What a bidirectional transformation is A bidirectional transformation is a mechanism that keeps two related artifacts—typically a source model and a derived view, or two models representing the same information at different abstraction levels—consistent with each other regardless of which side changes. The ambition is stronger than round-trip engineering's protected-region approach: rather than carving out zones a generator won't touch, a bidirectional transformation (BX) framework tries to compute, given an edit on either side, the minimal consistent update to the other side automatically. ## The lens laws The formal foundation most commonly cited is the 'lens'—a pair of functions, `get` (source → view) and `put` (view, source → source), satisfying two laws. - **GetPut** says that if you take a view via `get` and immediately put it back unchanged, you get the original source back—editing nothing and syncing shouldn't corrupt anything. - **PutGet** says that if you put an updated view back into the source, then get a fresh view from the result, you should see exactly the update you made—your edit should actually take effect, not be silently dropped or altered. A concrete industrial example is **QVT** (Query/View/Transformation, an OMG standard), whose QVT Relations language lets you declare a relation between two models and have the engine enforce consistency in either direction when one side changes, rather than hand-writing two separate transformation functions. ## Why two one-directional functions are weaker Writing two independent one-directional transformations is easier but weaker because nothing forces the two functions to actually be inverses of each other. Each can be individually 'correct' in the sense of producing plausible output, while together they violate GetPut or PutGet: - round-tripping A→B→A might silently drop a field B doesn't represent; - B→A→B might invent a default value for information A never had, quietly fabricating data that looks legitimate. Writing a transformation that provably satisfies both laws—especially once the mapping isn't a trivial structural copy but involves filtering, aggregation, computed fields, or many-to-one collapsing—is substantially harder, because every non-trivial operation on one side needs a well-defined, non-ambiguous 'undo' on the other, and for many realistic transformations no single sensible undo exists. ## The trade-off This is precisely the trade-off: true bidirectionality gives strong, provable consistency guarantees and removes the entire class of drift bugs caused by asymmetric hand-written transformations, but at a real engineering cost—designing, implementing, and verifying lens laws requires specialized tooling and expertise most teams don't have on hand, and the technique fundamentally cannot work when the transformation is lossy by nature. A projection that drops information (e.g., a summary view showing only a customer's name and total order count, discarding order line items) cannot satisfy PutGet in general, because an edit made purely on the summary view has no unambiguous way to map back to a consistent change in the underlying orders—do you invent a new order, scale existing ones, or reject the edit? Any answer is an arbitrary policy decision, not a mathematically forced one, and has to be designed and justified by a human, which is exactly the complexity BX frameworks are trying to formalize but cannot eliminate. ## When not to attempt it Teams should not attempt full bidirectionality when: - one side is clearly and durably authoritative and edits genuinely only originate there (in which case one-directional forward engineering with no backward path is simpler and equally correct); - the transformation is inherently lossy in a way that makes a consistent `put` undefined without arbitrary policy choices best made explicitly by a human reviewer; - or the volume and frequency of cross-directional edits is low enough that occasional manual reconciliation is cheaper than building, verifying, and maintaining specialized BX tooling and staff expertise in it. ## The failure mode The predictable failure mode when a team attempts bidirectionality without the rigor lenses require is a transformation pair that appears to work in common cases during development, then corrupts data on an edge case in production—for instance, a hand-rolled 'sync both ways' script between a legacy database schema and a newer domain model that silently zeroes out a field during a backward sync because the forward direction's default-value logic wasn't designed as a true inverse, discovered only when a customer's data is quietly wrong weeks later.
- What are the GetPut and PutGet laws in lens-based bidirectional transformations, in plain terms?GetPut says extracting a view and putting it straight back with no changes must reproduce the original source exactly—syncing with no edits shouldn't corrupt anything. PutGet says that after putting an edited view back into the source, extracting a fresh view from the result must show exactly the edit you made—your change has to actually take effect and be visible, not get silently dropped or altered.
- Why can't a summary view that drops detail (e.g., showing only a total instead of individual line items) generally satisfy a bidirectional transformation's laws?Editing the summary doesn't uniquely determine how the underlying detailed data should change—you could invent a new line item, scale existing ones, or reject the edit—and any choice is an arbitrary policy decision, not one forced by the data itself, so a general-purpose put can't be defined without a human-designed, scenario-specific rule.
- When is it reasonable to skip full bidirectionality and just use one-directional forward engineering instead?When one side is clearly and durably authoritative and edits genuinely only ever originate there, a backward path adds engineering cost for a scenario that structurally can't occur, so one-directional generation with no attempt at reverse sync is simpler and just as correct in practice.
It's like a two-way translator between English and a language with no word for a concept you just used—you can force some translation, but there's no principled way to get your original meaning back, and pretending otherwise just hides the loss until someone notices the meaning changed.
saying these in an interview costs you the question
- Thinks writing A→B and B→A separately is equivalent to a true bidirectional transformation
- Can't explain why a lossy/summarizing view breaks round-trip guarantees
- Assumes bidirectional tooling can always be made to work with enough effort regardless of the transformation's nature
- Never mentions any correctness property (even informally) that a real BX approach must satisfy
- Recommends full bidirectionality for every model/view pair regardless of authority or edit frequency