Part 2: The Claude Code Prompt I Use to QA an Optimizely CMS 13 Upgrade

Apr 17, 2026

Claude logoIn Part 1, I shared the Claude Code prompt that kicks off a CMS 12 to 13 upgrade - analysing the codebase, planning the migration steps and doing the heavy lifting on the code changes. That gets you a long way. But the upgrade isn't fully done when the site starts.

The gap between "it compiled" and "it's actually correct" is where things go wrong in production and that's where upgrades earn their reputation. Manually comparing two environments across dozens of URLs is tedious, inconsistent, and easy to rush. So here's the second prompt in the workflow: the one I use to systematically QA the upgraded site against the reference.

The approach

The idea is straightforward. You have two environments: the reference site (the known-good CMS 12 baseline) and the upgraded site (your CMS 13 candidate). The prompt treats these as a pair — crawling the reference site two hops from the homepage, deriving the equivalent URLs on the upgraded site, and testing each pair against a defined set of checks.

Two hops is a deliberate choice. It's deep enough to exercise most of your template types — homepages, landing pages, product listings, content pages — without going so deep that you're waiting an hour for results or racking up unnecessary token usage.

Here's the prompt:

Claude QA Prompt

You are a QA engineer testing an Optimizely CMS 13 site upgrade. Your job is to compare a reference site (the known-good baseline) against an upgraded site and report differences.

Reference: https://<reference-domain>/
Upgrade:   https://<upgrade-domain>/

================================================================================
STEP 1 — BUILD THE URL LIST
================================================================================

Crawl the reference site to 2 hops from the homepage (i.e. follow links on the homepage, then follow links found on those pages — do not go deeper). Collect only:
- Internal URLs with a path (skip #fragment, mailto:, tel:, javascript:)
- Pages that return HTTP 200 anonymously
- Deduplicate and sort

Skip these URL patterns — they require a session or produce dynamic-only content:
- /cart, /checkout, /wishlist, /my-account, /order-, /login, /register, /password
- Any URL with a query string (e.g. ?q=, ?page=)

For each reference URL, derive the upgrade equivalent by substituting the domain.
Example:
https://<reference-domain>/path/to/page-a/ → https://<upgrade-domain>/path/to/page-a/

================================================================================
STEP 2 — TEST EACH URL PAIR
================================================================================

For every (reference URL, upgrade URL) pair, fetch both and check:

CHECK                 | PASS CONDITION
-----------------------|-------------------------------------------------------
HTTP status            | Upgrade returns same status code as reference
                         | (both 200, or both 301/302 to same slug)
Page renders           | Upgrade response body is not an error page, exception dump, or blank
Translation keys       | No raw Optimizely translation key paths visible in the body (pattern: text matching /[A-Z][a-z]+/[A-Z][a-z]+/ inside visible text content)
Key content present    | Major headings, product names, or section titles visible on reference are also present on upgrade (allow for minor wording differences)
No missing renderers   | No [BlockType] placeholder text or Razor exception fragments in the body
Navigation present     | <nav> or equivalent header/footer elements exist

ACCEPTABLE DIFFERENCES — do not flag as FAIL:
- Exact product counts, prices, or stock status (live data)
- Timestamps, "X days ago", recently-added items
- Image URLs or CDN paths
- Minor whitespace or HTML attribute ordering
- Content intentionally different between environments (e.g. environment banners)

================================================================================
STEP 3 — REPORT
================================================================================

Produce a per-URL summary:

PASS   /path/to/page-a/
PASS   /path/to/page-b/
FAIL   /path/to/page-c/   — HTTP 500 on upgrade (200 on reference)
FAIL   /path/to/page-d/   — Raw translation key "/Section/Key/Label" visible in body

Then group failures by category:

HTTP errors        — 4xx/5xx on upgrade
Translation keys   — Optimizely localisation not wired up (raw key paths in body)
Missing content    — Section present on reference, absent on upgrade
Rendering errors   — Exception text, missing block output
Wrong content      — Significantly different body text (excluding acceptable differences)

Finally give totals:   X PASS, Y FAIL out of Z URLs tested

What Claude Code does with this

One thing worth noting: Claude Code handles structured prompts like this really well. The numbered steps, the check table, the acceptable differences list — that level of instruction is exactly what it needs to stay on task across what can be a long running operation crawling and comparing dozens of URLs. It won't drift, summarise early, or skip checks because it got bored halfway through. Give it structure and it follows it.

The report format at the end is also deliberate. PASS/FAIL per URL means you can scan the output quickly, and the grouped failure categories tell you immediately what kind of problem you're dealing with rather than leaving you to triage a wall of text.

The translation key issue it caught in the wild

Translation key failures are a classic upgrade gotcha and easily missed in manual testing because you have to be looking at the right page at the right moment. On a recent upgrade run, the QA prompt flagged this in the report:

🟡 Site-wide — Raw translation keys in login/account form overlay

Strings like /Login/Form/Label/Email and /Shared/Address/Form/Label/FirstName render verbatim. Missing XML translation resource entries for the login/account/address components.

The XML translation files for those components hadn't been picked up in the upgraded project. It's the kind of thing that looks fine in a quick smoke test but you'd have to actually interact with the login or account overlay to spot it. The prompt caught it because it's specifically scanning the page body for that key path pattern on every URL it tests.

What it won't cover

To be clear about the limits: this is an anonymous crawl, so anything behind authentication won't be tested. It's also not a visual regression tool, it won't catch a layout that's broken but technically returning content. And if you have JavaScript-heavy interactions or client-side rendering, the body it fetches won't reflect what a real browser renders. You still need a human for all of that. Think of this as the systematic baseline check that clears the obvious failures before you spend time on the nuanced ones.

Conclusion

Used together, the migration prompt from Part 1 and this QA prompt cover the full upgrade arc from codebase analysis through to verified, production-ready output. It's the fastest I've seen a CMS 13 upgrade come together.


Running a CMS 13 upgrade? I'd love to hear what you're finding — find me on LinkedIn or X.

This blog represents personal views and experiences. For official Optimizely documentation, visit docs.developers.optimizely.com.


Comments