The task · 13 min read
Why does a systems model need entity resolution?
Hardly anything a team builds carries the keys from its systems model. What does it take to find the counterparts of each element of the systems model anyway, and why is that job never quite finished?
Roar Elias Georgsen, 24 September 2026
Part 1 of 6 in Automating traceability.
Say a team of systems engineers has built a systems model of a search pipeline before the pipeline itself exists. Queries come in at one end and results go out at the other, through five stages. The second stage, parse, has the key PIPE-S2, and the systems model gives it a capacity of 1,200 queries per second. A requirement with the key PIPE-R1.2 asks parse to handle 1,500, so on paper parse falls short.
Now suppose a second team has built the pipeline, and one of its engineers sends me a list of what they think belongs to parse in the running system:
- a resource in the OpenTofu code that deploys the pipeline, at
module.pipeline.aws_ecs_service.stage["parse"] - a service called
query-parserin the OpenTelemetry traces the system sends while it runs - a check called
parse-sustained-qpsin Checkly, which tests the live system on a schedule - a Go test called
TestParserRejectsEmptyQuery
Before reading on, decide for yourself which of these you’d link to PIPE-S2.
The parse stage and four made-up counterparts. The systems model holds the key, and the built system holds only names that point towards it.
Here’s how I’d call it. None of the four says PIPE-S2, and each needs a different kind of judgement. The OpenTofu address has the stage’s own name in it, which is about as good as evidence gets without a key. query-parser looks right, but I’d want to know what the service actually does before trusting a resemblance. The check measures sustained throughput, so it says more about the requirement than about the part, and I’d rather link it to PIPE-R1.2. The test could be about the parse stage or about the query language, and only its code will tell.
Each of those calls rested on names chosen by people who weren’t thinking about the systems model at all. A real system turns a list like this into thousands of entries. Every element of a systems model, whether a part such as parse or a requirement such as PIPE-R1.2, has counterparts in several tools, and every tool renames things on its own schedule. A list somebody typed up is out of date the first time one of them does.
If you come from software or data, a systems model is a different animal from the models you may know. A data model or a database schema describes the shape of records, and a machine learning model is something trained on data. A systems model, or systems engineering model in full, describes the system being engineered, usually long before that system exists. It names the parts, says what each of them must do and records how they fit together, and in SysML v2 it does all of that in a form a program can read.
It’s rarely the only description of that system, though. Whoever builds the system writes several more descriptions of it without ever meaning to. The infrastructure code says which servers to start. The running services report their own names in their telemetry, and the checks and tests that watch over the system carry names of their own. All of them describe the same system, and hardly any of them mention the systems model.
The links between the systems model and those other descriptions are called trace links, and maintaining them is what engineers mean by traceability. With them, you can start from a failing check and find the requirement at risk, or start from a requirement and find everything that shows it’s met. Without them, the systems model slowly turns into a nicely drawn picture of a system nobody can point at.
Maintaining trace links by hand works for a while. I know, because in an earlier series I built a demo that serves a SysML v2 systems model to other tools, and then gave the demo a systems model of its own. I added every link between that second systems model and the rest of the demo myself. It’s a fine way to spend an evening and a poor way to run an engineering organisation. The parse stage comes from the first of those two systems models, an example the demo serves. It only ever existed there, so I’m free to imagine the rest. This series is about finding trace links automatically, and maintaining them while the systems model and the built system both change underneath them. Before it ends, I’ll put a resolver to work on the demo’s own systems model and check what it says. You won’t need the earlier series to follow along.
Entity resolution
Key term Entity resolution
Deciding that two records held in different places describe the same thing. In this series one record is an element of the systems model. The other is an object in the built system, such as an OpenTofu resource, a traced service, a check or a test. Resolving them tells you where a trace link belongs.
The problem is far older than software. Getoor and Machanavajjhala trace the first work on it back to the 1950s. They also list some of the names it has gone by since, from record linkage and deduplication to reference reconciliation. Software engineering has its own name for the wider job of finding trace links after the fact. Guo and colleagues call it trace link recovery, the task of identifying the relations within a set of artefacts that already exist. I’ll use both terms, with “resolve” as the verb.
The two sides of the problem aren’t equal. On one side, the systems model holds the keys, short names such as PIPE-S2 that its author picked so that tools could refer to each element. On the other, the built system holds names that each builder picked for their own purposes. Addresses, service names, tags, check names and test names all point somewhere, and resolving the parse stage means deciding which of them point at PIPE-S2.
There’s a bridge here to the earlier series. The demo joins several GraphQL services into one graph, a technique called federation, so that one query can collect its answer from all of them. In a federation, an entity resolver is handed a shared key and returns whatever its service knows about that object. It never has to decide anything, because every service already agrees on the key. Entity resolution is the step before that, working out a shared key for an object that has only keys of its own. Once that’s done, the federation can join the two like anything else, and the links can sit beside the systems model where every tool can reach them. So I’ve come to think of automating traceability and automating federation as one job seen from its two ends.
Why the names don’t line up
You’d think a team that cares about traceability would just put the systems model’s keys everywhere. The demo’s own systems model shows how far that gets you. Some keys were designed in from the start. The requirement SR-22, that an edit made through the graph patches the source text of the systems model it came from, appears as the short name SR-22 and as the longer name SR_22_EditsPatchTheSource. It’s also the start of six Go test names, such as TestSR22_SetAttributePatchesTextAndProjectionTogether. User stories reach Checkly as tags in lower case, so the check for US-04 carries the tag us-04. A test in the demo fails if its systems model and the Go test names drift apart.
Other links I added by hand. The adapter, the service that publishes the example pipeline’s systems model to the graph, runs as the subgraph named model. The demo’s systems model records that name in an attribute nothing checks. And some keys never existed at all. The router that joins the demo’s services is router in the demo’s systems model and sysml-federation-router in the traces it sends, a name the systems model never mentions. I chose both names, and I still managed to make them disagree.
The longer name also shows how a name keeps the context it was chosen in. The router joins every service in the demo and has nothing to do with SysML as such. It was introduced at the same time as a SysML model server, though, and the prefix stuck. If the router ever serves queries for something with no SysML in it, the name will mislead whoever reads it.
Of the 146 top-level Go test functions in the demo’s repository, 69 carry a requirement’s key in their names. The other 77 are invisible to anything that joins on keys.
Each tool also has its own idea of identity, and none of them has any reason to use mine. OpenTofu knows a resource by its address, which the author chooses and which only means something inside that configuration. Rename it and, by default, OpenTofu reads the change as an intent to destroy the old object and create a new one, unless a moved block says otherwise.
OpenTelemetry’s service.name is the service’s logical name, and when nobody sets one it falls back to unknown_service: followed by the name of the executable. Checkly knows a check by its logicalId, and changing that ID tells Checkly that one check was removed and another created.
Even keys that were designed in leak. Rath and colleagues studied six open-source projects that largely followed the practice of tagging each commit with an issue key, and found that on average only 60% of commits were linked. One mistyped key is enough to break a join.
Hardware makes all of this harder, because the counterpart of an element of the systems model is an object on a bench or a wall. The Asset Administration Shell, an industrial specification for describing such assets, notes that one asset can have several identifiers. Among them are a serial number, the manufacturer’s part number, the customers’ own part numbers and an RFID code. None of those is a key from the systems model either. Messages about the asset bring their own naming. MQTT topic names are case-sensitive and never normalised, so in a made-up plant, a subscriber to site1/line2/pumpA/# hears nothing at all from a pump publishing as PumpA. It’s the same problem with fewer clues, and this series stays mostly with software, where the built system can at least be asked questions.
What the links are for
Picture a check on the made-up pipeline failing at two in the morning. Without a link to the systems model, the engineer on call knows that a number fell below a line. With one, they know it’s the throughput PIPE-R1.2 asks of parse, and the systems model shows them everything else that depends on parse. The link turns an alert into a question about the system, which is a much better thing to be woken up by.
That’s the everyday case. Certification is the formal one. Safety standards ask for trace links, and Cleland-Huang and colleagues’ review of the field quotes DO-178C, the standard for airborne software, on tracing source code to low-level requirements to show there’s no undocumented code. The same review found traceability “often conducted in an ad-hoc, after-the-fact manner”, at a cost that “can be extremely high”. It also reports an analysis of submissions to the US Food and Drug Administration. In many cases the trace data was incomplete, incorrect and conflicting, with clear signs that the links had been created at the very end, for certification.
The field has long wanted the opposite. Antoniol, Cleland-Huang, Hayes and Vierhauser restated the goal in 2017 as the grand challenge of traceability that’s “always there, without ever having to think about getting it there”. Defence engineering calls the connected result the digital thread. The US Department of Defense’s digital engineering strategy expects its authoritative source of truth to “provide traceability as the system of interest evolves”.
None of that comes free. A resolver takes effort to build and tune, and a poor one makes confident mistakes at scale. I think the links earn it anyway. If a systems model is going to sit at the centre of an organisation’s engineering, it had better be able to point at what was built.
When links go stale
Much of the thinking about linking records assumes the records sit still. A census return or a published paper is written once and then mostly left alone. A systems model and the system it describes change all the time. Parse might be split in two, query-parser renamed, its test moved to another package and PIPE-R1.2 reworded. A link that was right last month can be wrong today, and nobody has to touch it for that to happen.
The traceability literature calls this decay. Links “become stale when source and/or target artifacts are modified”, as the same review puts it. Mäder and Gotel argue that links need maintaining as the system evolves “in order to prevent their decay”. Rahimi and Cleland-Huang, in the grand challenges report, put the stakes plainly: “outdated trace links invalidate safety-cases”.
So finding the links once isn’t enough. Resolution has to run again whenever either side changes, the way a test suite does. My demo already runs one piece of this on every pull request, a test that fails when a Go test or check named in its systems model disappears. What that test can’t do is find a link nobody recorded.
Suggesting links, or adding them
A resolver can be wrong in two ways. It can make a link that shouldn’t exist, a false positive, or miss one that should, a false negative. Which of the two you’d rather live with decides how the resolver should be put to work.
One way puts a person in front of every link. While an engineer writes the OpenTofu resource for parse, the resolver suggests PIPE-S2, the engineer accepts, and the key goes into the code as a tag. This resolver can lean towards finding everything, because a wrong suggestion costs a moment to reject. Dekhtyar and Hayes make the same point, that “detecting a false positive is much simpler and faster than discovering an error of omission”.
The other way works over what’s already there. The resolver reads the configuration, the traces, the checks and the tests, adds the links it’s confident about, and leaves people to remove the wrong ones when they find them. It’s far faster and cheaper than reviewing every link, and for a system too big to review link by link it’s the only option. It needs the opposite lean, though. A wrong link sits there looking authoritative until someone trips over it, so this resolver has to be careful about what it adds, even if that means missing some.
Rath and colleagues tried both in one study, with one classifier set two ways. Set to suggest, it found about 96% of the true links on average, although only a third of its suggestions were right. Set to add links on its own, more than 89% of what it added was right, and it found half of the links.
Record linkage has a name for the ground in between. In Fellegi and Sunter’s method, as Winkler sets it out, two thresholds divide every pair into a link, a non-link and a possible link held for a person to review. How wide you make that middle band decides which way of working you’re really running.
People reviewing links aren’t a perfect safety net either. When Cuddeback, Dekhtyar and Hayes had analysts vet candidate trace matrices, the analysts moved them towards a balance of about as many wrong links as missing ones, and poor matrices improved sharply. Follow-up studies found the other half of the story, which the review above sums up: “the higher the quality of the starting trace matrix, the worse the decisions the analyst makes”. And decay cuts across both ways of working. The link from PIPE-S2 to its OpenTofu resource goes stale just as fast whether a person confirmed it or a machine added it.
Part 2 takes a single resolution task apart, from what goes in to what comes out, and asks how exactly a task has to be stated before anyone tries to solve it.
Previous: A model of the demo itself, the last part of Federating a systems model · Index: Automating traceability · Next: The parts of a resolution task