Ask most systems for a bill of materials and you get a list of lines. It looks like data. It behaves like data. And in a product with variants, effectivity or any history at all, it is not data — it is an unevaluated expression.
What a line actually contains
A single BOM line can carry several things that are not a quantity and a part number.
It can carry an effectivity range: this component applies from March to September, or from serial 1400 onward. It can carry option membership: this line exists only if the 240-volt variant was selected. It can carry a floating revision reference, meaning "whatever is currently released", which resolves to different things on different days by design.
So the honest description of a structure is not a list. It is a function. Give it a date, a configuration and a revision, and it returns a set of lines. Give it nothing and it returns nothing meaningful, though most systems will happily return something.
The default that is never right
What most systems return when you do not specify is the stored lines — every line, unevaluated, with the conditions attached as extra columns if you are lucky.
The consequence is that whoever receives it has to evaluate the conditions themselves. And they will, because they need an answer. They will write the evaluation in SQL, or in a spreadsheet formula, or in the reporting layer, and they will write it slightly differently from the person who did it last quarter, and neither of them will have the variant rules.
The observable result is a cost model that counts both the 120-volt and the 240-volt power supply, because nothing told it a unit has exactly one. The number lands close enough to be believed and wrong enough to matter, and nobody notices until somebody compares it against the PLM directly — which typically happens about a year in, during a planning cycle, at the worst possible moment.
The failure is not that the data was wrong. It is that the data was conditional and arrived somewhere with no engine to evaluate the conditions.
Export resolutions, not rows
The fix is to stop exporting the function and start exporting its output.
A structure leaving the system — to a warehouse, to an ERP, to a report — should be a resolution under stated conditions, with the conditions on every row. As of this date, for this configuration, at this revision, these are the lines. An analyst querying that gets the same answer the compare screen gives, because it is the same answer, produced by the same engine.
This costs something real: you have to decide what to ask for, which means somebody has to think about it rather than accepting a default. That friction is the point. An unparameterised request is almost always a question the requester had not finished forming, and refusing it with a message naming what is missing is cheaper than answering it wrongly.
Store the question with the answer
If you keep a resolved structure — in a warehouse, in a report, in a baseline — keep the conditions alongside it.
A stored resolution without its conditions is a number nobody can reproduce. And reproducing it is exactly what somebody will need to do, eighteen months later, when a customer asks why the unit they received does not match a document you sent them.
This is also what separates a baseline from an export. An export is a copy of an answer. A baseline stores the answer and the conditions that produced it, which means the answer can be re-verified by re-running the resolution — and comparison between two baselines is like-for-like rather than a diff between two snapshots taken under conditions nobody recorded.
Most "the BOMs do not match" investigations end in the discovery that one side asked for today and the other asked for the release date. Both were right. Neither wrote down what they asked.
What this means at the ERP seam
Your ERP does not want a conditional structure and mostly cannot represent one. What it wants is the resolved line set for the thing it is about to plan or purchase, which is convenient, because that is what it should be sent.
Two details make the difference between an integration that holds up and one that drifts.
The first is which date. The publication should carry the effectivity date, not the approval date — otherwise the ERP starts building the new configuration the day the change was signed rather than the day it was supposed to take effect.
The second is partial success. A structure that publishes ninety-nine lines out of a hundred, because one item is missing from the ERP item master, is not a success with a caveat. It is a bill of materials that is silently missing a component, in a system that will now plan against it. Partial publications are the classic half-failure: no dashboard shows them, no person owns them, and they surface weeks later as a stock discrepancy that gets written off as a counting error.
The reframe
None of this requires sophisticated tooling. It requires treating a structural read as a question rather than a lookup.
Ask for a date. Ask for a configuration. Refuse to answer when they are missing, rather than guessing. Carry the conditions with the answer wherever it goes. And when two systems disagree about a bill of materials, check what each one asked before checking what each one holds — because most of the time, that is where the disagreement already was.