Independent IFS Cloud practice · Field notes

Data migration silent killers: the FndMig and DMM failures that never throw an error, and only surface weeks after go-live

Key takeaways

  • The migration loads I remember worst are not the ones that failed. They are the ones that loaded cleanly, reported success, and were wrong in ways nobody could see until stock, cost or a customer order depended on the wrong number.
  • A unit-of-measure conversion error does not stop a load. It produces a quantity that is arithmetically consistent and physically false, and it will not surface until someone tries to reconcile a count or ship an order against it.
  • An orphaned record, one that loads successfully but references a parent that never existed or arrived later, is invisible to referential-integrity checks that only run at load time, not after every subsequent load in the sequence.
  • The Excel patch that fixed one load, applied under deadline pressure and never revisited, turns into a permanent data problem precisely because nobody folds the correction back into the repeatable extract logic before the project team moves on.

I have run enough data migrations into IFS, across Apps 7.5 through 10 and now IFS Cloud, to have stopped being surprised by which loads cause the trouble. It is never the load that fails. A failed load is a good day: the log tells you exactly what broke, and you fix it and rerun it. The loads that cost real money weeks later are the ones that finish, report success, and are quietly wrong. This is a set of field notes on three specific ways a migration goes wrong without ever throwing an error, drawn from patterns I have seen recur across FndMig and DMM projects regardless of module, and what I now build into every migration to catch them before go-live rather than after.

1.Why the standard load validation does not catch this class of failure

FndMig and DMM validate what they can define a rule for: mandatory fields populated, foreign keys resolving to a record that exists at the moment the load runs, values within a domain the target field accepts. Mandatory-field and foreign-key checks catch errors that are structurally invalid outright, at load time, with a log line pointing at the offending row. What it structurally cannot catch is anything that is syntactically and referentially valid but semantically wrong: a quantity that is a legal number, just computed from the wrong conversion factor; a foreign key that resolves correctly today because the parent happened to load in an earlier batch, even though the two were never meant to be linked; a value hand-patched into a staging spreadsheet under time pressure and never checked against the rule it was meant to satisfy.

None of that trips a validation rule, because none of it is invalid data. It is valid data that means the wrong thing, and the tooling has no way to know what the right thing was supposed to be without someone writing and maintaining that rule deliberately, on top of the standard load, not instead of it.

2.Unit-of-measure conversion mismatches

Unit-of-measure conversion mismatches are the failure I see most often and trust least in any migration I inherit rather than build myself. The legacy system holds a quantity in one unit, the legacy source data was extracted with an assumed conversion factor to the target stocking unit, and that factor was either wrong, out of date, or applied inconsistently across the extract. The load itself is completely happy: it sees a numeric quantity, it sees a valid unit of measure on the part record, it writes the value. Nothing about the transaction is invalid.

What actually happens is that the on-hand balance loaded into IFS is now arithmetically consistent and physically false. It will not surface at go-live, because nobody is comparing the freshly loaded balance against a physical count on day one. It surfaces at the first cycle count, or worse, the first time someone tries to ship an order against that balance and the pick fails or, more dangerously, succeeds against stock that is not really there in that quantity. By the time it surfaces, the legacy system is usually decommissioned, the original extract is gone, and reconstructing the correct conversion factor means going back to the part’s technical specification or, in the worst case, physically recounting everything affected.

The check that catches this before go-live is uncomfortably simple and rarely done under deadline pressure: for a sample of parts across every unit-of-measure pairing in the extract, not just the high-value ones, physically verify the conversion factor against the part specification, independently of whatever factor the extract used. If the legacy system and the target system disagree on the factor for even one pairing, assume every part using that pairing is suspect until proven otherwise, not just the one you happened to sample.

3.Orphaned records that load successfully but reference nothing real

A migration runs in a sequence, dependent objects loading after the parents they reference. The failure mode here is subtler than a broken foreign key rejected at load time. It is a foreign key that resolves successfully, at the moment its record loads, to a parent record that exists in the target system for reasons entirely unrelated to being the correct parent: a batch that loaded slightly out of the intended order, or a mid-project dedup pass that consolidated two legacy IDs into one target record without updating every child reference to match.

The record is not invalid, it just does not point at the thing it was supposed to point at, and load validation, which only checks that a referenced key exists at the moment of the load, has no way to know that. It tends to surface in one of two ways: a report that silently pulls the wrong parent’s attributes for months before anyone notices, or a downstream process that fails in a way that looks unrelated to migration at all, which is what makes it expensive to trace back to its actual cause.

The mitigation I now insist on is a post-load reconciliation pass that is separate from load-time validation: after the full sequence completes, re-walk every parent-child relationship that matters for the modules in scope and confirm each child’s parent is the one the source system actually recorded, by comparing legacy identifiers carried through as cross-reference fields, not by trusting that the target key resolving at all means it resolved correctly. This has to run after the whole sequence, not batch by batch, because the specific failure pattern is a batch ordering issue that a single batch’s own validation cannot see.

4.The “temporary” Excel patch that becomes permanent

Every migration I have worked on has had at least one point where the automated extract could not resolve something cleanly under the schedule available, and someone hand-corrected a batch of rows in a spreadsheet before feeding them into the load. This is not a criticism; it is sometimes the only realistic way to hit a cutover date. The failure is that the patch is applied once, works, and then nobody schedules the work to fold the correction back into the repeatable extract logic, because the project has already moved on to the next milestone.

The specific way this bites later is not usually the patched data itself going wrong; a careful hand-correction is often more accurate than the automated logic it replaced, at the moment it was made. It is that the underlying source data changes after go-live, a new batch of the same record type arrives through the same integration or a delta load, and the extract logic that was never fixed reproduces the original error on the new records, because nobody remembers, months later, that this particular field needed a hand adjustment and why. The knowledge lived in a spreadsheet on someone’s laptop, not in the transform logic, and the spreadsheet is gone or forgotten well before the problem resurfaces.

What I do differently now: every hand-patch gets a one-line entry in a migration decisions log, at the moment it is applied, stating which field, which record population, what the automated logic got wrong, and what the correct rule actually is. That log is the input to a follow-up task, tracked separately from go-live, to either fix the transform logic properly or, at minimum, confirm in writing that the condition causing the original error cannot recur post-cutover. A patch without that entry is indistinguishable, eighteen months later, from a deliberate design decision, and someone will eventually build on top of it assuming it was one.

5.What I actually check, post-load

Four checks catch this class of error after any load I am responsible for: conversion-factor consistency, parent-child cross-reference matching, patch-window edits and an independent-source quantity comparison. They are shown here as generic, PL/SQL-flavoured pseudocode using aliases rather than an assertion about a specific release’s exact schema, because the exact objects involved differ by module and by release:

Check What it catches
GROUP BY part_no, uom_pair HAVING COUNT(DISTINCT conversion_factor) > 1 Any part loaded with more than one conversion factor across the extract, a near-certain sign of an inconsistent or wrong factor somewhere in the batch, section 2.
child.legacy_parent_ref <> parent.legacy_id (joined via the target-system foreign key, not the legacy reference) A child record whose resolved parent in the target system does not match the parent its own source data actually recorded, the orphan pattern in section 3.
record.last_modified_by = :migration_analyst_user AND record.last_modified_at > :cutover_freeze_date Records touched by a named individual after the extract was supposed to be frozen, a strong indicator of an undocumented hand-patch, section 4.
SUM(qty) OVER (PARTITION BY part_no) compared against an independent legacy-system extract total, per part A quantity-level sanity check against a second, independent source, catching conversion and rounding errors that a single-source validation cannot see because it only checks itself.

None of these queries are complicated. What matters is that they run as a separate step after the load, not folded into load-time validation, because the point is to catch things that were valid at load time and wrong in a way that only shows up when compared against something outside the load itself.

6.How I build this into a migration project, in order

  1. Sample and independently verify conversion factors before the first mock load. Do this against the part specification, not against the extract's own assumptions, and treat any disagreement as a signal to check the whole unit-of-measure pairing, not just the sampled parts.
  2. Run the post-load parent-child reconciliation after every mock load, not just the final one. Catching an orphan pattern in mock load two, while the extract logic is still being actively worked, is far cheaper than catching it after go-live.
  3. Keep a live migration decisions log from day one. Every hand-patch gets an entry the day it is applied, not retroactively at project close-out when nobody remembers the details.
  4. Assign an owner and a deadline to each logged patch. A decision log nobody is accountable for closing out is just documentation of a problem that still exists.
  5. Run the independent-source quantity check at every mock load and at go-live. Comparing against the legacy system's own totals, not just internal consistency within the new load, is what catches a systematic conversion error rather than a one-off.
  6. Freeze the decisions log and confirm every open item before cutover. An unresolved “temporary” patch at cutover needs an explicit, written decision to accept the risk, made by someone who can actually own that decision, not a default of silence.
  7. Wire the same reconciliation queries as a recurring Custom Event check for the first few months post-go-live. New data flowing through the same integration paths can reproduce a migration-era error long after the migration project itself has closed, and a standing check inside the Extensibility Framework catches that far earlier than the next audit would.

7.Frequently asked questions

Why don't FndMig and DMM catch these errors during the load?

Unit-of-measure conversion errors, orphaned foreign keys and unlogged hand-patches are semantically wrong but syntactically and referentially valid: a legal number computed from a wrong conversion factor, a foreign key that resolves correctly but to the wrong parent, a hand-patched value that was never checked against the business rule it needed to satisfy. Standard load validation checks mandatory fields, valid domains and key resolution, not whether a valid value means the correct thing.

How do you actually catch a unit-of-measure conversion error before go-live?

By independently verifying the conversion factor against the part specification for a sample across every unit-of-measure pairing in the extract, not just high-value parts, and treating any disagreement as reason to check the entire pairing rather than the sampled records alone. A quantity total compared against an independent source, such as the legacy system's own figures, catches what a single-source check cannot.

What makes an orphaned record hard to detect after migration?

An orphaned record's foreign key resolves successfully at load time, just to the wrong parent, often because of batch ordering or a mid-project deduplication pass that consolidated legacy IDs without updating every child reference. Standard load validation only confirms a key resolves, not that it resolves to the record the source data actually intended, so a separate post-load reconciliation pass comparing legacy cross-reference fields is required to catch it.

How do you stop a temporary Excel patch from becoming a permanent problem?

By logging every hand-patch the day it is applied, with the field, the record population and the correct rule stated explicitly, then assigning an owner and a deadline to fold the fix back into the repeatable extract logic. Without that log, the knowledge lives only in a spreadsheet that will not exist by the time the underlying source data changes and reproduces the original error.

8.About the author

Dariusz Myśliwiec: 25+ years in ERP and supply chain, 17+ on IFS (Apps 7.5–10 and IFS Cloud), with hands-on Data Migration Manager, FndMig and Oracle PL/SQL work across those projects. IFS Certified Associate Consultant. PRINCE2® 7. Based in Kraków, delivering remotely across Europe and globally through an independent practice.

Selected clients: Fugro · LGC · BVI Medical · Betafence (PRÆSIDIAD) · Barlinek · NGK Ceramics · Newag · Oleofarm.

IFS is a registered trademark of IFS AB; this practice is not affiliated with IFS AB.

Migrating into IFS Cloud and want a second set of eyes on the load?

If your cutover is close and you want someone who has chased these three failure modes before to sanity-check the extract and the reconciliation plan, a short scoping conversation is the fastest way to find out what is worth checking before go-live rather than after.

Book a scoping conversation See the SCM Automation Pack