Skip to content

Vendor export column changes and how to find them before a report breaks

Unified dataUpdated 2026-08-187 min read

In short

A per source column manifest is the defence against vendor export column changes: a stored list of every column name and type in a feed, diffed against each new file on arrival. The diff goes to a named person before the transform runs, because a rename that nulls a join produces no error anywhere.

The exhibitor lead report for the March show goes out on the Monday after close, and every exhibitor on it has zero leads. Not a few. All 1,120 of them. The scan file is fine, the exhibitor list is fine, and the query has not been touched in two years.

What happened is that the vendor renamed exhibitor_id to exhibitorId in a release six days earlier, and vendor export column changes of that kind produce no error message anywhere in the chain. The join stopped matching. The left join kept every exhibitor row, filled the right side with nulls, and the report rendered on time.

The rename that nulls a join

Take the arithmetic, because it explains why nobody caught it in review.

The scan file holds 47,300 rows. The exhibitor dimension holds 1,120 rows. Before the rename, 46,180 of the 47,300 scans joined to a known exhibitor, which is 97.6 per cent, and the 1,120 that did not were the usual mess of test scanners and staff badges. That 97.6 per cent had been stable for four editions, so nobody thought to check it.

After the rename, the loader wrote a column called exhibitorId into a table whose exhibitor_id column now held nothing, and the join matched 0 of 47,300. The report showed 1,120 exhibitors with zero leads each, which is a number so obviously wrong that a human caught it in a day. That was lucky. Had the rename hit a secondary field, say the column carrying the exhibitor's product category, the report would have shown every exhibitor in an uncategorised bucket and could have run like that for a year.

The general shape is worth naming. A rename produces a plausible output, and plausible outputs survive review.

What does a column manifest actually hold?

Store, per source, one row per column: the name, the type, and whether the loader requires it. That file lives next to the loader in the same repository, and it changes only when a person changes it.

For a 61 column exhibitor export that is 61 rows of text. On arrival, the incoming header is compared against it and the difference is expressed in three lists: names present in the file and absent from the manifest, names present in the manifest and absent from the file, and names in both whose type has moved. A rename shows up as one entry in each of the first two lists, which is exactly the pair a person needs to see to recognise it as a rename.

The reason to write it as a manifest rather than to infer it from the previous file is that the previous file is not a promise. It is a sample. If the vendor sent a broken file yesterday, comparing today against yesterday tells you today looks fine. Comparing today against a manifest a person wrote tells you today is what you agreed to receive. That distinction is the whole argument for keeping the shape in version control with a name on the commit.

A manifest is also a document you can hand to somebody. When the platform is replaced, and it will be, the manifest is the thing that says what the next platform has to produce. Sanderson, Freeman and Schmidt, in their 2025 O'Reilly book on data contracts, frame this class of failure as pipelines breaking and consumers losing trust in the data, with version control among the components of the architecture they set out. The manifest is the smallest version of that idea that a two person data team can maintain.

Why did nobody read the release note?

Because the release note went to a different person.

Registration and lead retrieval platforms publish change notes, and they go to the account administrator, who is usually in operations or marketing. The loader is owned by whoever does the data work. Those are rarely the same person, and the note that says field naming has been standardised across the API means nothing to the first and everything to the second.

That is a routing problem with a boring fix. Someone owns reading the change notes for each platform, once a month, and the job takes about fifteen minutes per platform. Four platforms is an hour a month. If nobody has that hour, the diff on arrival becomes your only warning, which is survivable but leaves you finding out at 06:00 on the morning the change lands rather than three weeks earlier.

Ask for the notes by email as well. A vendor who publishes to a portal you have to log into has effectively published nothing.

Asking a vendor for a version number

The software industry solved the labelling half of this in 2013, and event vendors have mostly not picked it up for their file exports.

Semantic Versioning 2.0.0, the specification Tom Preston-Werner published and which was tagged at version 2.0.0 in June 2013, says to increment the MAJOR version when you make incompatible API changes, the MINOR version when you add functionality in a backward compatible manner, and the PATCH version when you make backward compatible bug fixes. Three integers, and the first one tells a consumer whether to worry.

Most registration platforms version their REST API and ship their CSV and Excel exports unversioned, which means the export, the artefact your reporting actually depends on, is the part with no compatibility promise attached. That is worth raising at renewal, in one sentence: we need export changes labelled as breaking or non breaking, and we need the label before the file.

You will not always get it. A vendor with most of the market in your vertical has no reason to agree to anything. When that happens, the manifest is doing the vendor's job for them, and you should size the work accordingly instead of pretending the promise exists. What to put in writing when they will sign is a longer conversation about the agreement itself, which K27 takes on.

What the diff should do when it fires

Route it to a person, and do it before the transform runs.

A diff that lands in a log is a diff nobody reads. A diff that lands in the channel where the data person already works, with the three lists laid out and the file held rather than loaded, gets a decision in minutes. Most of those decisions are one word. New column, nothing reads it, accept. Type moved from integer to string on a field you never join on, accept. Column gone that four reports reference, hold the file and call the vendor.

Detecting the shape change at all is a separate mechanism from deciding what to do about it, and the cheapest detector is a hash of the ordered column list, which K21 sets out. Deciding which of the three lists deserves an interruption at two in the morning is a grading question that K23 answers with the set of columns your reports actually reference.

Where the manifest stops

A manifest describes shape. It is silent on meaning, and meaning is where the expensive failures live.

The vendor keeps registration_type as a string column and changes the value it writes for a complimentary badge. Every column matches the manifest, the diff is empty, and your comp badge count goes to zero. No shape check catches this, and the only thing that does is a check on the distinct values of the categorical columns, which will also fire every time a show adds a legitimate new registration type. That trade is real, and I would still run the value check on the four or five columns your reports filter on, because those are the ones where a silent recode is expensive.

The second limit is timing. A manifest diff fires when the file arrives, and the file arrives after the change has already been made. It shortens the gap between change and discovery from weeks to hours. It does not give you a say in the change, and no amount of engineering on your side will, which is why the fifteen minutes a month spent reading release notes buys something the manifest cannot.

Start by generating the manifest from a file you already have. Take the most recent export from your most important source, write out the column names and types as they stand today, commit it, and add one comparison step to the loader that prints the three lists and stops on anything in the second or third. That is an afternoon, and it turns the next rename into a message instead of a March report full of zeroes. Everything downstream in unified data depends on the feed still having the shape you built against.

Questions people ask about vendor export column changes

Why do vendor export column changes break reports silently?
Most warehouse loaders read by column name and most reporting joins are left joins. A renamed key column therefore produces no error. The join simply matches nothing, the left side survives, and every figure that depended on the right side becomes zero or null while the report continues to render and to be circulated.
What is a column manifest?
It is a stored record of every column a source feed sends, with the type of each one, kept next to the loader and versioned in the same repository. Each new file is compared against it on arrival. The manifest is a description of what you were promised, which makes any difference between promise and delivery visible.
How much notice should a registration vendor give before changing an export?
Long enough for your team to absorb the change before the next reporting deadline, which for most event teams means a minimum of 30 days. The number matters less than having one agreed in writing, and than the vendor labelling which changes are incompatible so you can tell an addition from a removal without reading the file.

Related reading

All data quality articles