Put The Translation Where Someone Owns It

When an outbound file needs a value in a different shape from the one you store, the right home for the translation is wherever a person owns it and can see it go wrong. That is a more useful question than "which layer is cleanest?", and the answer is less obvious than it sounds. Technically, the translation can live in three places. In practice, only one of them tends to survive staff turnover, a second customer and a Friday afternoon change.
The problem looks like a formatting bug
Picture an exporter that produces billing files for a downstream receiver. Its behaviour is beautifully simple: it takes a code configured against a cost centre and writes it into the file unchanged. One site stores long, complete codes. Another site stores short ones, a few letters that are perfectly meaningful to the people there. The receiver expects the long form. The first site already satisfies it. The test files for the second do not, and the receiver quite reasonably asks why the two look different.
The immediate request is easy to grant: reissue the files with the one field corrected and move on. The real question arrives straight after, and it is the one worth slowing down for. What happens next month, when the same field turns up in a file for a third site?
There are three honest answers. You can fix the data, so the stored value is the complete code and the exporter keeps passing it through. You can fix the exporter, keeping the short values and adding a permanent translation step. Or you can leave both alone and let whatever sits between you and the receiver do the translating, an offer that is often made generously by the team running that middleware.
Why the pass-through exporter is worth defending
My bias, and I hold it loosely, is toward fixing the data. A pass-through exporter is easy to reason about. What you configured is what was sent. If a file is wrong, there is exactly one place to look, and it is a visible value on a configuration screen that a person can read.
The moment the exporter starts translating, it becomes a second source of truth. The value in the configuration is no longer what leaves the building. Someone looking at the configuration sees a short code and believes that is what the receiver gets. Someone looking at the file sees a long code and cannot find where it came from. Each of them is right about the part they can see, and the system is wrong in the gap between them.
I've come to treat that gap as the real cost of translation logic. Not the lines of code, which are trivial, but the fact that one fact now lives in two places that must be kept in agreement by memory.
The middleware option hides best
Letting the receiving side's integration layer fix it up is tempting, because it costs your team nothing today. It is also the version I trust least. The translation becomes invisible to the people who produce the data and the people who read the files, and it is owned by a team whose priorities are somebody else's problem. When the rule changes, or the layer is replaced, or a new site bypasses it, nobody on your side will know until a file is rejected or, worse, accepted and misapplied.
A rule that sits between two organisations needs a named owner on at least one side, and it needs to be written somewhere both can find it. Without that, it is a favour with a half-life.
The honest cost of fixing the data
Fixing the data is not free either, and it would be dishonest to pretend otherwise. It moves the work from engineering to whoever maintains the configuration, and it only works if that person understands what the receiver expects and has a way to check. If the long form has a rule behind it, such as a site prefix followed by a specialty, then somebody has to know the rule, apply it in every environment, and keep test and production aligned. Mappings that drift between environments are a classic way to produce a test that passes and a production file that does not.
So the job is not finished when the stored values change. It is finished when three things are true. A person is named as the owner of those values. The expected form is written down somewhere the receiver can also read. And there is a cheap check that flags a value that does not match the pattern before a file is built. A validation at configuration time, one that refuses a short code where a long one is required, is worth more than any amount of careful translation downstream. It turns a silent difference into an immediate, understandable error, at the moment the person who can fix it is already looking.
When translation in the exporter is the right call
There are cases where I'd put the translation in the exporter and accept the second source of truth. If the stored value serves other purposes, such as appearing on screens, in reports or in other integrations, then rewriting it to suit one receiver would be a mistake. The short code is the right code for the people who use it every day. In that case the exporter's translation should be explicit, small, tested, and visible. A preview of the outgoing value beside the stored one removes most of the confusion, because the gap stops being invisible.
The same goes for a receiver that will change its mind. If you expect to support several receivers with different expectations, a mapping owned by the integration is a sensible home, provided it is treated as data with an owner and not as an incidental line in a function.
The question that settles it
What I keep coming back to is plain: if this value is wrong at three in the afternoon, who finds out, and how? If the answer is "the person who configured it, when validation rejects it", the design is healthy. If the answer is "the receiving team, after a file bounces, and they will email us", then the translation is in the wrong place, however tidy it looks on a diagram.
There is a commercial side to this too. Every integration rule that nobody owns eventually becomes a support cost, and support cost is the one that never shows up in the estimate. The cheaper design is not the one with the least code. It is the one where the next change is a small, visible, boring edit made by someone who knows why it matters.
None of this is glamorous. It is a field with a few extra letters on the front. But a surprising amount of reliable integration work comes down to that sort of decision: not how to make the mapping work, but where to put it so that, long after the people in the original conversation have moved on, somebody still knows it is theirs.


Share your thoughts