Blog

Sitecore to Optimizely: The Quiet Breakages

· 6 min read · By the Replatform Radar team

The things that break in a Sitecore-to-Optimizely migration are almost never the things you demoed. Templates map. The homepage renders. Everyone claps. Then someone clicks into a three-year-old case study and the hero image is a broken icon, the "related products" block is empty, and the internal link that used to send people to a live page now 404s into the void. Nothing crashed. It just quietly stopped pointing at anything. That gap between "the site loads" and "the site actually works" is where these projects lose their weekends.

Here is the mental model that explains most of it. Sitecore is a building where every door is labelled with a random 32-character serial number instead of a room name. Inside the building, everyone navigates fine, because everything refers to everything else by serial number. Then you move to a new building (Optimizely), the movers assign fresh serial numbers to every door, and suddenly every instruction you owned that said "go to door {A7F3...}" is pointing at a room that no longer has that number. The furniture arrived. The map didn't.

Why the map is the hard part

Sitecore stores internal links as GUIDs, not as URLs. When an author links to another page, or drops a datasource into a rendering, Sitecore saves the item's globally unique identifier and resolves it to a URL at render time. That is genuinely elegant. It means you can rename or move a page and every link to it follows automatically.

It is also exactly what breaks. The moment you export content into Optimizely, those GUIDs either change or stop being the thing Optimizely uses to resolve links. Rich-text fields full of ~/link.aspx?_id=... references, image fields pointing at media library GUIDs, multilist fields wiring products to categories: all of it is written in a private code that only the old building understood. If your migration script does not translate every GUID reference into the new content's identifier or a real URL, you get pages that render perfectly and reference nothing.

The media library is its own trapdoor

Sitecore serves images through a media handler, so asset URLs look like /-/media/... (or /~/media/... on older instances) with a GUID and often a hash. Optimizely serves assets from its own media structure with entirely different paths. Every one of those image references, in fields and buried in rich text, has to be rewritten. Miss them and you don't get an error. You get a live page with a polite little broken-image placeholder, which nobody notices until a customer emails.

While we're here: presentation details. In Sitecore, a page's layout, its renderings, and its datasource wiring live in the __Renderings field, not in the content. Optimizely composes pages from content types and blocks. That is not a field mapping, it is a rebuild, and it is the single most under-scoped part of these projects.

What maps, what rewrites, what you rebuild

Sitecore conceptOptimizely equivalentReality at go-live
Data templates + fieldsContent types + propertiesMaps cleanly, mostly mechanical
GUID-based internal linksContent references / URLsMust be translated or they die silently
Media library (/-/media/)Optimizely media assetsEvery path rewritten, including in rich text
Presentation details / renderingsBlocks + content areasRebuilt, not migrated
xDB / xConnect personalizationOptimizely visitor groupsRules rebuilt by hand; history rarely moves
Aliases + 301 moduleOptimizely URL redirectsExported and re-imported, or lost
Item versions + languagesContent versions + languagesMaps, but publish state often resets

Personalization is a rebuild wearing a migration costume

If your Sitecore instance ran XP with real personalization, brace yourself. The rules that decided which banner a returning visitor saw live in a completely different engine than Optimizely's visitor groups. You are not migrating personalization. You are re-authoring it, and the behavioural data that made it work in the first place usually stays behind. Scope that as new work or it will ambush your timeline.

How to catch it before go-live, not after

The honest answer is that you catch this by inventorying both sides before anyone writes a mapping script, and by diffing them after. Crawl the live Sitecore site and capture every published URL, every asset path, and every internal link relationship while it still resolves. That gives you the ground truth of what "working" looked like. Then crawl the staged Optimizely build and compare: which URLs exist, which assets return a real file, which internal links resolve to a 200 instead of a 404. The breakages announce themselves as diffs. This is the sort of thing a pre-migration crawl is built to surface, so orphaned assets and dead GUID references show up on a spreadsheet in week two instead of in a customer complaint in week twelve.

Do the redirect export early too. Sitecore aliases and any 301 module entries are content, not config, and they do not ride along automatically. Pull them, and build your redirect map from the old published URL set, not from the CMS tree. The migration plan should treat every old URL as a promise you made to Google and to every human who bookmarked it.

So what would I actually do?

I'd spend the first sprint building the two inventories and nothing else, before I let a single content type get modelled. Not because it's glamorous (it is the opposite of glamorous), but because every expensive surprise in a Sitecore-to-Optimizely move traces back to a reference that pointed somewhere in the old building and points nowhere in the new one. Fix the map first and the furniture takes care of itself. Skip it, and go-live morning you'll be standing in a beautiful new lobby, clicking doors with serial numbers on them, wondering why none of them open.

Questions and discussion

Have a question about this article, or a migration you’re planning? Ask below. We read and answer every one.

Loading discussion…

Planning this exact move? Read the in-depth Sitecore to Optimizely migration guide.

Scan your site →