Most migration failures don't happen because of Shopify. They happen because of what Magento 1 does to a single product record before it ever reaches Shopify.
Magento 1 stores products using an Entity-Attribute-Value (EAV) model. Instead of one row holding one product, a single product's data is fractured across multiple tables based on data type, catalog_product_entity_varchar for text attributes, catalog_product_entity_int for integers, catalog_product_entity_decimal for prices and weights, catalog_product_entity_text for long descriptions, and so on. To reconstruct one product, you have to join across all of them, correctly, using the right attribute IDs and store-view scoping.
Shopify works the opposite way. It's a denormalized, API-first flat model, a product is a single JSON object with variants, options, and metafields nested directly inside it.
Here's what that looks like in practice. Take a single product with entity_id 1042. In Magento's catalog_product_entity_varchar table, its color attribute might exist as two separate rows, attribute_id 73 storing "Blue" and attribute_id 91 storing "Grey" as a duplicate attribute set.
Its price lives elsewhere, in catalog_product_entity_decimal, as attribute_id 12 = 49.99. Its parent-child relationship to two child SKUs lives in yet another table, catalog_product_relation, linking parent_id 1042 to child_id 1043 and 1044. To reconstruct this one product correctly, all of these rows have to be joined, deduplicated, and normalized before they mean anything to Shopify.
Once that normalization happens, the same product becomes a single, unified Shopify JSON object: one product titled appropriately, with a variants array holding two entries , one for the "Blue" option tied to SKU-1043 at $49.99, and one for the "Grey" option tied to SKU-1044 at the same price. Everything that was scattered across five Magento tables now lives in one flat, self-contained record.
When a basic import tool or a raw script skips the normalization step and dumps EAV query results straight into Shopify, the result is predictable: corrupted variant arrays where "Grey" and "GY" become two separate options instead of one, orphaned SKUs that lose their parent-child relationship, and custom attributes that simply disappear because they don't map to a Shopify field. This is the single most common root cause of a messy post-migration Shopify catalog, and it's entirely preventable if it's caught before extraction, not after import.