CelinQ Insights · No. 42
From File Synchronization to Semantic Synchronization in Enterprise Architect
A byte-for-byte comparison can tell you that two repositories differ. It cannot tell you what actually happened, and that difference decides whether synchronisation is trustworthy.
Imagine handing two versions of an Enterprise Architect repository to a tool that only understands bytes, or rows in a database table, and asking it what changed. The tool can absolutely tell you that row 4,821 in the object table now reads differently than it did before. It can tell you that a row disappeared from the connector table and a new one appeared with a different primary key. What it cannot tell you, because the information simply is not encoded in bytes and row identifiers, is whether that disappearing connector was deleted and a genuinely new one created in its place, or whether it was the very same logical relationship, quietly reassigned a new internal key by some internal EA housekeeping operation, still meaning exactly what it meant before. Those are two completely different events with completely different consequences for anyone trying to merge one person's changes with another's, and a tool working at the level of raw storage cannot distinguish them, because at that level they look identical.
This is the fundamental limitation of file-level or row-level synchronisation, and it is worth being precise about why it is a limitation rather than just an inconvenience. It is not that byte comparison is unsophisticated or that a cleverer diff algorithm would fix it. It is that the information needed to answer the question — was this a rename, a move, a genuine deletion, a coincidence — does not exist at the level bytes operate on. You cannot infer intent from storage format any more than you can infer what a letter says by weighing the envelope. The intent lived in the editor, in the moment the change was made, and if nothing captures it there, no amount of clever comparison afterwards will recover it.
Why binary and file-level sync falls short
The obvious first approach to keeping two repositories in step is to treat the whole file as an opaque blob and synchronise it the way you would synchronise any document: whoever saved most recently wins, and their version simply replaces whatever was there before. This is straightforward to implement and catastrophic to depend on. Two people working on genuinely different parts of a large model — one refining a data-flow diagram in a technology layer, the other tidying business capability definitions three packages away — have made no conflicting decisions whatsoever, and yet a whole-file replacement strategy would force one of their afternoons to simply vanish, because only one file can be "the current one" at a time. This is not a synchronisation strategy; it is a coin flip dressed up as one, and any team that has lived under it can describe exactly how it feels to lose work this way — quietly, without warning, discovered only when someone goes looking for a change that used to be there.
A slightly more sophisticated approach compares individual database rows rather than the whole file, and this genuinely is progress: at least now two edits to different rows can both be kept. But it runs immediately into the identity problem described above. EA's internal storage does not guarantee that a logically unchanged entity keeps a stable row forever under every kind of structural operation, and even where an identifier is stable, a row-level diff still has no concept of what a field means. It sees that a text value changed from one string to another. It has no way to know that the field is a name, that changing it is a rename rather than a semantic redefinition, and that a rename should be treated completely differently — both for merge purposes and for the audit trail — from, say, changing an element's type or its lifecycle status. Row comparison narrows the blast radius of a bad merge. It does not remove the underlying blindness to meaning.
What "semantic" actually adds
Semantic synchronisation starts from a different question. Instead of asking "what bytes changed," it asks "what operation did the architect actually perform," and it captures the answer at the moment the operation happens, inside the tool, rather than trying to reconstruct it afterwards from a static comparison. When an architect renames an element in Enterprise Architect, CelinQ's client-side change detection does not see two unrelated strings and infer a rename from context. It observes the actual event: this specific, stably identified entity had its Name field changed from one value to another. That is recorded as exactly what it is — a rename — and it travels through the whole synchronisation pipeline as a rename, not as an anonymous difference that some downstream process has to guess the meaning of.
The distinction between a rename and a raw row difference matters most exactly where it is easiest to get wrong: at merge time. If two people are working from the same starting point and one renames an element while the other, independently, changes that element's stereotype, a semantic system sees two operations on two different fields of the same stable identity and combines them without hesitation — there is no meaningful sense in which these two edits conflict. A row-level or file-level system, lacking any concept of "field" as a meaningful category, may see two rows that both differ from the base and treat the situation as ambiguous, or worse, silently prefer one wholesale over the other. The semantic approach earns a materially better merge outcome, not just a nicer explanation after the fact, because it is working from more accurate information in the first place.
The same reasoning separates a move from a delete-and-recreate. Moving a package to a different parent in the tree is, semantically, a single change to one field: the parent reference. Nothing about the package's identity, its children, or its own content changes. A system that only sees storage rows may observe that a package which used to sit under one parent now appears somewhere else in a positional listing and cannot cleanly tell whether that is a move of the original or the deletion of one package and the creation of a coincidentally similar new one elsewhere. Get that classification wrong during a merge and the consequences are not cosmetic — a move misclassified as a delete-and-recreate can sever every relationship, tagged value and diagram placement the original package legitimately still owns, because the system believes the old package no longer exists.
A rename is not "the text changed." A move is not "the position changed." Both are specific, well-defined operations on a specific field of a specific, stably identified entity, and a synchronisation system that cannot represent that distinction is working with strictly less information than the person who actually made the change had in their head at the time.
Tagged values, connectors, and diagrams each need their own vocabulary
Semantic capture is not a single trick that applies once and covers everything; it has to be extended, deliberately, to each distinct kind of model content, because each kind carries different structure that a generic "field changed" record would flatten and lose. Tagged values are not one field but a map of keys to values attached to an entity, and the meaningful unit of change is a single key, not the whole map. If one architect adds a tagged value under one key and another architect, working from the same base, adds a different tagged value under a different key, those are two additions to two distinct entries in the same map, and there is no real disagreement between them at all — they should simply both survive. A system that treated the whole tagged-value collection as one indivisible unit would see two different collections and have no principled way to combine them, when in truth combining them is trivial once the map is respected as a map of independent entries rather than a single opaque blob.
Connectors carry their own kind of structure worth naming explicitly: a relationship between two things, with the identity of each end — its source and its target — being every bit as meaningful as any scalar property on the connector itself. Changing a connector's name or its stereotype is a different kind of event from changing which element it actually connects to, and the two need to be distinguished, because reassigning an endpoint is a structural, topological change with implications for everything else that depends on the relationship, while a label change has none. Treating both the same way — as generic "the connector row changed" — collapses a distinction that genuinely matters when two people have touched the same connector for different reasons.
Diagrams introduce yet another shape of change: geometry. A diagram object's position and size behave less like a name than like a composite transform, and two edits to the same object's geometry can be entirely compatible with each other — one person moving an object, another independently resizing it — because a move and a resize commute. A generic system that only knows "the geometry field changed" cannot see that decomposition, and would treat any two geometry edits on the same object as a conflict, even the overwhelming majority of cases where they plainly are not. A system that understands geometry as a translation component and a size component separately can combine a move on one side with a resize on the other automatically, and reserve the genuine conflict case — two people moving the same object to two different places, an operation that does not commute — for the rare occasion it actually happens. This is developed further in the article on merge atoms, which goes through exactly which unit of a model each kind of edit is tracked against and why the unit has to be this fine to be useful.
Revision semantics: state versus story
A related distinction sits at the level of the whole model rather than any one entity: whether synchronisation transmits a state, or a story. A state-based approach periodically compares "what the model looks like now" on each side and reconciles the difference — it can, in principle, work, but it discards the sequence of events that produced the current state, and with it, information that occasionally turns out to matter: whether two changes were part of one coherent intentional action, what order independent changes happened in, and what the actual causal relationship was between a change and the state it was made against.
A story-based, or operation-based, approach transmits the actual sequence of semantic operations as they happen — this rename, then that tagged-value addition, then that move — each one declaring the specific version of the entity it was made against. This is what makes a genuine three-way merge possible at all: with a recorded operation and a recorded base version, the reconciliation engine on the server can classify every incoming change precisely, as either sequential (nothing else happened to this entity since the change's author last saw it) or concurrent (something did, and a real merge is required). A purely state-based comparison, working only from two snapshots with no operation log between them, has no equivalent concept of a base to compare against; it has to infer one, and inference is exactly the weak point where meaning gets lost. The operation journal that makes this possible, and how it survives long offline periods without losing any of its ordering guarantees, is covered in the piece on offline consistency.
It is worth walking through a concrete example to see how much this changes in practice. Suppose an architect spends a morning working through a package of business capabilities: renaming three elements to match a newly agreed taxonomy, adding a tagged value recording the responsible domain owner on each, and moving two of them under a different parent package to reflect a reorganisation the team agreed on last week. Under a state-based comparison, all of that morning's work collapses into a single, undifferentiated difference between "the package before" and "the package after" — a difference a reviewer, or a merge engine, would have to re-derive meaning from by inspection, guessing at which parts were renames, which were relocations, and which were new content. Under semantic, operation-based capture, that same morning arrives as eight or nine distinct, individually labelled operations, each one exactly what it says it is: three renames, three tagged-value additions, two moves. A colleague reviewing the revision history sees precisely that story, not a blob of differences they have to reconstruct the narrative of by hand. And if a colleague working elsewhere in the same package happened to touch one of those same three elements that same morning — adding a relationship, say — the reconciliation engine has exact, typed operations to compare on both sides rather than two ambiguous snapshots, and in the overwhelming majority of such cases it can tell, correctly and automatically, that the two mornings' work simply does not collide.
Why the canonical state has to live on the server
None of this works if "the model" is a fiction distributed loosely across however many local copies happen to exist at a given moment, each equally entitled to call itself correct. Semantic synchronisation depends on there being one canonical, authoritative sequence of revisions that every local repository's operations are ultimately reconciled against — not because a central server is philosophically superior to a distributed one, but because the specific guarantee that matters here, deterministic and reproducible merge outcomes, requires a single point where concurrent operations are actually compared, classified and resolved in a well-defined order. If two clients each tried to decide independently how to reconcile a shared entity, they could easily reach two different, individually reasonable conclusions, and now the team has two more diverging copies instead of one converged model — the opposite of what synchronisation is supposed to achieve.
CelinQ's canonical state lives in the workspace on the CelinQ Server, which an organisation runs on infrastructure it controls. Every local Enterprise Architect repository remains the fast, fully-functional working copy — nothing about the canonical server changes how quickly an architect can read or write locally, because local editing never waits on it. What the server provides is the single point of truth that makes deterministic reconciliation possible: the place where a base version is recorded, where concurrent changes are actually merged rather than merely compared, and where the result becomes the next canonical revision everyone else's synchronisation converges toward. This is what makes the difference between "the two repositories were compared and someone had to decide what to do" and "the two repositories converged, automatically, because the system had a well-defined and provably correct way to combine them." The mechanics of that reconciliation — the three-way merge, the specific rules that let disjoint changes combine automatically, and the smaller set of genuine collisions that still need a person — are the subject of the conflict-resolution pillar article, which this piece sets the stage for.
The shift from file synchronisation to semantic synchronisation is a change in what the system captures in the first place, not a matter of degree — not a slightly smarter diff algorithm bolted onto the same basic approach. File sync captures an outcome and tries to infer intent from it afterwards, which is sometimes possible and sometimes not. Semantic sync captures the intent directly, at the moment it happens, as a specific named operation on a specific stable identity, and everything downstream — merging, auditing, explaining a decision to a sceptical reviewer — gets to work with that intent rather than reconstruct a guess at it.
What this does not solve on its own
It would be a disservice to present semantic capture as though it makes every disagreement disappear, because it does not, and it was never meant to. Semantic synchronisation tells the system precisely what happened on each side of a divergence. It does not manufacture agreement where two people genuinely wanted different things. If one architect renames an element to reflect a new understanding of its role, and another architect, working independently, renames the same element to reflect a different new understanding, semantic capture correctly identifies this as two changes to the same field of the same entity — and correctly refuses to guess which rename should win. That is the approach doing exactly its job: telling the difference between a genuine disagreement and a false one, and escalating only the former to a human. Manufacturing a resolution for a real disagreement would be worse than admitting one exists, because a quiet, wrong guess is much harder to notice and undo than an honest, visible conflict.
There is also a cost to capturing operations this precisely, and it is worth naming rather than glossing over: every kind of model content EA supports has to be deliberately taught to the system as its own category, with its own notion of what a meaningful unit of change looks like, rather than relying on one generic mechanism that covers everything by accident. Scalar fields, tagged-value maps, connector endpoints, tree parentage, and diagram geometry are each handled as their own case, tested on its own terms. This is more engineering effort than treating the whole repository as an undifferentiated blob of bytes, and it is precisely the effort that has to be spent for the resulting merges to be trustworthy rather than merely plausible-looking. A synchronisation approach that skipped this work would be faster to build and immeasurably riskier to depend on, and the risk would not announce itself until the day it silently discarded or misattributed something that actually mattered.
Put honestly: semantic synchronisation does not make collaboration effortless. What it does is narrow collaboration's one unavoidable hard part — deciding what to do when two people genuinely disagree — down to the only part that still requires a person, by correctly and automatically handling the much larger number of cases where two people never actually disagreed: they simply worked on different things that a cruder comparison would have mistaken for conflict. That is the entire value proposition of moving from bytes to meaning, and it is worth insisting on precisely because the alternative fails silently rather than loudly, which is the worst way for a synchronisation system to fail.