# SiteGenesis to SFRA Migration: A Technical Guide for SFCC Teams

URL: https://www.ozanaltinbas.com/en/blog/sitegenesis-to-sfra-migration
Author: Ozan Altinbas (Ozan Altınbaş), SFCC Technical Architect
Published: 2026-11-12
Category: Architecture | Tags: SFCC, SiteGenesis, SFRA, Migration, Cartridges

---

SiteGenesis is the storefront framework B2C Commerce sites ran on before Salesforce launched the Storefront Reference Architecture (SFRA) as its successor in 2018. Moving from one to the other is not an upgrade: the two share the platform, the data and the ISML template language, but almost none of the storefront code.

This guide covers Salesforce's actual position on SiteGenesis support, the three realistic routes, the code inventory, how each SiteGenesis concept maps to SFRA, and how to protect SEO and cut over in a way you can reverse. For the platform itself, start with the [SFCC guide](https://www.ozanaltinbas.com/en/sfcc).

---

## Is SiteGenesis end of life?

No. Salesforce has announced no end-of-life date for SiteGenesis or for pipelines:

- A Salesforce knowledge article published in December 2025 states there is [no plan to deprecate B2C Commerce pipelines](https://help.salesforce.com/s/articleView?id=000391508&language=en_US&type=1) and require JavaScript controllers only.
- The [SFRA FAQ](https://developer.salesforce.com/docs/commerce/commerce-solutions/guide/sfra-faq.html), written when SFRA launched, says SiteGenesis merchants are not required to move and that SiteGenesis keeps receiving high-priority bug and security fixes. That launch-era position has not been withdrawn, but it carries no date.

What has changed is everything around SiteGenesis:

- Salesforce's [storefront chooser](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/which-product.html) lists Storefront Next (recommended), SFRA, a hybrid of the two and a custom build. SiteGenesis is not an option.
- [Salesforce Payments](https://help.salesforce.com/s/articleView?id=cc.b2c_cartridges.htm&language=en_US&type=5) ships as an SFRA plug-in cartridge; on SiteGenesis the documented workaround is to add it to the cartridge path as a library, which Salesforce notes doesn't work in all instances.
- [Legacy jobs](https://help.salesforce.com/s/articleView?language=en_US&id=cc.b2c_legacy_jobs.htm&type=5), which run pipelines, are deprecated and listed under **Administration > Operations > Jobs (Deprecated)**.

The real question is what standing still costs.

---

## SiteGenesis vs SFRA: what actually differs

"SiteGenesis" covers two code bases. The original, which Salesforce's documentation calls SiteGenesis Pipeline Processor (SGPP), handles requests in **pipelines**: XML flows edited in UX Studio, an Eclipse plug-in, whose nodes call pipelets and scripts and render templates. The later **SiteGenesis JavaScript Controllers** (SGJC) edition uses CommonJS controllers in `app_storefront_controllers`, layered on `app_storefront_core`.

| Area | SiteGenesis | SFRA |
|---|---|---|
| Request handling | Pipelines (SGPP) or JavaScript controllers (SGJC) | Controllers whose routes are registered through the `server` module |
| Base code | `app_storefront_core`, plus `app_storefront_controllers` in SGJC | `app_storefront_base`, plus the `modules` cartridge that holds `server` |
| Customisation | Override cartridge in front of the base | Overlay cartridges using `server.append`, `prepend` or `replace` |
| Template data | Typically Script API objects in `pdict` | Plain JSON view models from `cartridge/models` |
| Request and session logic | `OnRequest` and `OnSession` pipelines or controllers | `dw.system.request.onRequest` and `onSession` hooks |
| Front end | SiteGenesis markup, styles and scripts | Bootstrap 4 and jQuery, built with `sgmf-scripts` |
| Add-on features | Plug-in cartridges are hard to overlay | Base, plug-in, LINK and custom layers on one path |

Two platform behaviours matter during a migration ([SFRA features and components](https://developer.salesforce.com/docs/commerce/sfra/guide/b2c-sfra-features-and-comps.html)):

- B2C Commerce treats SGJC and SFRA controllers as equals and runs the first controller with the requested name on the cartridge path. If none matches, it runs the first matching **pipeline**, so a SiteGenesis cartridge left on an SFRA site's path keeps its old endpoints reachable.
- SFRA's base cartridge and `server` module are guaranteed backward compatible between point releases only while you leave them unedited. Customise in your own `app_custom_*` overlays; the [cartridge development guide](https://www.ozanaltinbas.com/en/blog/sfcc-cartridge-development) covers the pattern.

---

## Choosing a migration route

### Route 1: rebuild the storefront on SFRA

Start from `app_storefront_base`, rebuild the design in overlay cartridges and port SiteGenesis features one at a time. The SFRA FAQ is explicit that existing data, merchant rules and back-end integrations are unaffected; only the storefront code base changes.

### Route 2: migrate one site at a time

The FAQ confirms SiteGenesis and SFRA can run on different brands or regions at the same time, at the cost of extra operational and code-management complexity. Cartridge paths are set per site and a code version is a folder of cartridges, so one code version can carry both stacks. Migrate the smallest site first.

Salesforce does not describe mixing SiteGenesis and SFRA on **one** site's cartridge path. Both ship a `Cart` controller and the first match wins, so within a single site, plan one storefront cut-over.

### Route 3: skip SFRA and go headless

Salesforce's [Storefront Next Migration Tool](https://developer.salesforce.com/docs/commerce/sfnext/guide/sfnext-migration-tool.html) is an AI-assisted CLI that analyses a live SiteGenesis, SFRA or PWA Kit storefront and generates a Storefront Next scaffold route by route. It is a developer preview, it needs read-only OCAPI and WebDAV access for SiteGenesis, and it doesn't evaluate pipelines: pipeline and custom server-side work stay manual. Salesforce documents Hybrid Auth for running Storefront Next beside a live SFRA or SiteGenesis site. The architectural trade-offs are in [SFRA vs PWA Kit](https://www.ozanaltinbas.com/en/blog/sfra-vs-pwa-kit).

| Situation | Route |
|---|---|
| One site, redesign due, Script API and ISML team | Rebuild on SFRA |
| Several sites on one realm | SFRA, site by site, smallest first |
| React team with a long-term headless plan | Storefront Next, with pipeline logic budgeted as manual work |
| Heavy pipeline logic, many partner cartridges with UI | SFRA first; headless later if still wanted |

---

## Step 1: inventory the SiteGenesis code base

Estimate from an inventory, not from a page count. Per site, record:

| Item | What to record | Why it matters |
|---|---|---|
| Cartridge path | Every cartridge, in order (**Administration > Sites > Manage Sites**) | Separates your code, SiteGenesis base code and partner code |
| Pipelines | Start nodes, the pipelets they call, interaction continue nodes | Each becomes a route, a script or a job step |
| SGJC controllers | Every public `Controller-Function` | Becomes an SFRA route or a base-route extension |
| Scripts | `.ds` and `.js` modules, and their pipelet calls | Script API code ports; pipelet calls need replacing |
| ISML templates | Pages, includes, slot rendering templates | Rewritten for SFRA's models and layouts |
| Form definitions | Each XML file, per locale folder | Format unchanged; the plumbing changes |
| Jobs | Legacy pipeline jobs and job-framework steps | Legacy jobs move to the job framework |
| Hooks | Every `hooks.json` entry | Most port; diff them against SFRA's own |
| Integrations | Vendor, version, SFRA availability | Often the critical path |
| Inbound endpoint names | Names used in static mappings, URL rules, content assets, emails and partner callbacks | Each must still resolve after cut-over |

The last row is the one most often missed: search the content library export, email templates and partner settings for endpoint names, not just the code. For LINK cartridges, the FAQ told teams at SFRA's launch to check for SFRA-certified versions. That still applies: confirm each vendor publishes an SFRA cartridge, and treat the switch as a new integration with its own install guide and regression tests.

---

## Step 2: map SiteGenesis concepts to SFRA

### Pipelines and SGJC controllers become SFRA routes

There is no converter. Each pipeline start node or SGJC public function becomes a route registered with `server.get` or `server.post` in `cartridge/controllers`. Middleware such as `server.middleware.https` takes over the checks a pipeline made first, models replace the pipeline dictionary, and `res.render` or `res.json` replaces template rendering.

Interaction continue nodes need special care. An interaction continue node renders a template, typically a form, and the same pipeline continues on the next request, so one flow spans several requests. Split each into a GET route that renders the form and a POST route that handles it.

The Script API's [`dw.system.Pipeline.execute()`](https://salesforcecommercecloud.github.io/b2c-dev-doc/docs/current/scriptapi/html/api/class_dw_system_Pipeline.html) was added to ease migration to controllers, but it only runs pipelines without user interaction. Salesforce's [controller guidelines](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-working-with-controllers.html) strongly discourage calling pipelets from controllers and call calling pipelines "highly discouraged" outside existing LINK cartridges. Treat it as a short-lived bridge at most, and replace pipelet calls with Script API methods wherever an equivalent exists.

### Forms: same definitions, new plumbing

SFRA reads the same XML form definitions. Salesforce's [forms documentation](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-forms.html) still uses SiteGenesis's `contactus.xml` as an example, and locale variants still live in `forms/default` and per-locale folders. What changes: `server.forms.getForm()` builds a JSON form object, templates use standard HTML forms, submitted values arrive in `req.form`, and persistence goes in a `route:BeforeComplete` handler. SFRA ships no dynamic, multi-part, embedded or nested forms.

```javascript
// app_custom_mybrand/cartridge/controllers/Feedback.js
'use strict';

var server = require('server');
var URLUtils = require('dw/web/URLUtils');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');

// GET: replaces the pipeline branch that rendered the empty form
server.get('Show', server.middleware.https, csrfProtection.generateToken, function (req, res, next) {
    var feedbackForm = server.forms.getForm('feedback'); // forms/default/feedback.xml
    feedbackForm.clear();

    res.render('feedback/form', {
        feedbackForm: feedbackForm,
        actionUrl: URLUtils.url('Feedback-Submit').toString()
    });
    next();
});

// POST: handles the submit the interaction continue node used to continue from
server.post('Submit', server.middleware.https, csrfProtection.validateAjaxRequest, function (req, res, next) {
    var feedbackForm = server.forms.getForm('feedback');

    if (!feedbackForm.valid) {
        res.json({ success: false });
        return next();
    }

    this.on('route:BeforeComplete', function () {
        // Persist here (custom object, service call) inside a Transaction
    });

    res.json({ success: true });
    return next();
});

module.exports = server.exports();
```

### OnRequest and OnSession become hooks

SFRA doesn't use the `OnRequest` and `OnSession` handlers SiteGenesis implemented as pipelines or controllers; Salesforce says to implement them as hooks, which have no access to `req` and `res`. Register the [`dw.system.RequestHooks`](https://salesforcecommercecloud.github.io/b2c-dev-doc/docs/current/scriptapi/html/api/class_dw_system_RequestHooks.html) extension points in the hooks file that the cartridge's `package.json` points to (`"hooks": "./hooks.json"`). Script paths are relative to that file:

```json
{
  "hooks": [
    { "name": "dw.system.request.onSession", "script": "./cartridge/scripts/hooks/request/onSession.js" },
    { "name": "dw.system.request.onRequest", "script": "./cartridge/scripts/hooks/request/onRequest.js" }
  ]
}
```

Each script exports `onSession` or `onRequest` and returns a `dw.system.Status`; without `req` and `res`, it works with the global `request` and `session` objects.

### Templates, client-side code and content slots

ISML is the same language, but templates are rewritten: SFRA templates read JSON models, sit in SFRA's Bootstrap 4 layouts and register CSS and JavaScript through the `assets` module (`assets.addCss`, `assets.addJs`). Client-side code is SFRA's jQuery-based scripts built with `sgmf-scripts`; SiteGenesis client scripts don't carry over. Resource bundles in `templates/resources` carry over as files, but must be merged into SFRA's keys.

Business Manager reads slot IDs and templates from the templates on the cartridge path ([content slot processing](https://help.salesforce.com/s/articleView?id=cc.b2c_content_slot_processing.htm&language=en_US&type=5)). Slot configurations survive as data, but every `<isslot>` ID and rendering template they reference must exist in the new templates, or that content stops appearing.

### Jobs move to the job framework

Legacy pipeline jobs are deprecated whichever storefront you choose. A `steptypes.json` in the cartridge root can declare a `pipeline-step` that runs an existing process pipeline as an interim measure; chunk-oriented steps can't use pipelines ([custom job steps](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-custom-job-steps.html)). The end state is a CommonJS `script-module-step`:

```json
{
  "step-types": {
    "pipeline-step": [
      {
        "@type-id": "custom.LegacyStockExport",
        "@supports-parallel-execution": "false",
        "@supports-site-context": "true",
        "@supports-organization-context": "false",
        "description": "Interim step: runs the SiteGenesis process pipeline unchanged",
        "pipeline": "StockExport-Start",
        "status-codes": {
          "status": [
            { "@code": "ERROR", "description": "The step failed." }
          ]
        }
      }
    ],
    "script-module-step": [
      {
        "@type-id": "custom.StockExport",
        "@supports-parallel-execution": "false",
        "@supports-site-context": "true",
        "@supports-organization-context": "false",
        "description": "Replacement step written as a CommonJS module",
        "module": "int_stock_export/cartridge/scripts/steps/stockExport.js",
        "function": "execute",
        "transactional": "false",
        "timeout-in-seconds": "900",
        "status-codes": {
          "status": [
            { "@code": "ERROR", "description": "The step failed." },
            { "@code": "OK", "description": "The step finished successfully." }
          ]
        }
      }
    ]
  }
}
```

Each cartridge holds only one `steptypes.json` or `steptypes.xml`; job parameters and the other options follow Salesforce's [steptypes.json example](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-step-types-json-example.html). A pipeline step needs the pipeline's cartridge on the cartridge path, which is one more reason to retire these steps quickly.

### Hooks and integrations

Hooks in `hooks.json` are platform extension points, not SiteGenesis code, so most port with little change. The exception is anything SFRA already implements. SFRA's base cartridge ships its own basket calculation hook script, `scripts/hooks/cart/calculate.js`, which its [JSDoc](https://salesforcecommercecloud.github.io/b2c-dev-doc/docs/current/sfrajsdoc/js/server/module-calculate.html) describes as letting the storefront and OCAPI share one cart calculation, so diff a customised `dw.order.calculate` hook against it instead of copying it over. Service configurations in Business Manager stay; only the code that calls them moves.

---

## What carries over unchanged

Per the SFRA FAQ, adopting SFRA leaves existing data, merchant rules and back-end integrations unaffected. In practice:

| Carries over as is | Needs review |
|---|---|
| Catalogues, price books and inventory | Content assets using SiteGenesis CSS classes or linking to SiteGenesis endpoints |
| Customers and order history | Slot configurations: their slot IDs and rendering templates must exist in SFRA |
| Promotions, campaigns and customer groups | Custom attributes and site preferences that only SiteGenesis code reads |
| URL rules, static mappings and redirects, as configuration | Rule and mapping targets that name SiteGenesis-only endpoints |
| Service configurations | Legacy pipeline jobs, and jobs whose steps come from SiteGenesis cartridges |

SFRA keeps `Link-Category`, `Link-Page` and `Link-Product` for source-code redirects, plus `Link-CategoryProduct` to support legacy content assets that link to specific pipelines ([SFRA JSDoc](https://salesforcecommercecloud.github.io/b2c-dev-doc/docs/current/sfrajsdoc/js/server/Link.html)). For custom endpoints, the cheapest fix is often to give the SFRA controller and route the SiteGenesis name, so every rule, mapping and link that uses it keeps working.

---

## SEO continuity

The storefront changes; the URLs should not. Most of the work is configuration that already exists:

- **URL rules stay.** Category, product and content URLs come from the site's URL rules (**Merchant Tools > Site > SEO & Discoverability > URL Rules**), provided SFRA templates build links with `URLUtils` to the standard routes. Crawl both storefronts and diff the URL sets anyway.
- **Pipeline URLs need checking.** URL rules also cover pipeline URLs. The [URL Archive](https://help.salesforce.com/s/articleView?language=en_US&id=cc.b2c_using_the_seourls_url_archive_tab.htm&type=5) redirects outdated mappings to current ones, and its cleanup search finds mappings that reference no valid category, folder or pipeline. Run it after cut-over.
- **Static mappings target endpoints by name.** Every [static mapping](https://help.salesforce.com/s/articleView?language=en_US&id=cc.b2c_static_mappings.htm&type=5) target must exist in SFRA. Mappings require a hostname alias, replicate from staging to production, and are limited to 2 MB in Business Manager (larger sets go through site import).
- **Diff the rendered head.** For the top URLs in Search Console, compare `<title>`, meta description, canonical, `hreflang` and structured data between the two renders. Anything SiteGenesis templates hard-coded is missing until you port it.

```text
! legacy URL -> endpoint that exists in SFRA's app_storefront_base
/summer-sale p,,,Link-Category,,cgid,summer-sale
/about-us p,,,Link-Page,,cid,about-us
```

The [Commerce Cloud SEO guide](https://www.ozanaltinbas.com/en/blog/commerce-cloud-seo-guide) covers the platform settings, and the [Commerce Cloud migration guide](https://www.ozanaltinbas.com/en/blog/commerce-cloud-migration) covers redirect mapping and Search Console monitoring.

---

## Testing and cut-over

### What to test

- **URL replay.** Request every URL in the inventory (Search Console exports, static mappings, sitemaps, email links) against the SFRA staging site and assert the status code and final URL.
- **Checkout matrix.** Every payment and shipping method, guest and registered, in every locale.
- **Jobs and integrations.** Each job on staging with production-like data; each partner integration end to end.
- **Performance.** SFRA sets the page cache per route with middleware: confirm cacheable pages are cached, and compare field data for revenue templates.

### Cut-over on one code version

[Code deployment](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-code-deployment.html) gives you the building blocks. An instance runs one active code version; best practice is to upload to an inactive version on staging, test it, and replicate it to production. Activation clears cached items, and **Rollback** reactivates the previous version if it is still installed. Cartridges are assigned per site under **Administration > Sites > Manage Sites**.

An approach built on those facts (rehearse it on staging; it is not a Salesforce-prescribed procedure):

1. Build one code version that contains both the SiteGenesis and the SFRA cartridges. Activating it changes nothing for shoppers while the site's cartridge path still lists SiteGenesis cartridges.
2. Replicate the URL rules, static mappings and other configuration the SFRA storefront needs.
3. At the agreed time, switch the site's cartridge path to the SFRA stack and invalidate the page cache.
4. To roll back, switch the path back; no code deployment is needed.
5. Once the site is stable, drop the SiteGenesis cartridges from later code versions so no forgotten pipeline stays reachable.

---

## A phased plan

| Phase | Work | Exit criterion |
|---|---|---|
| 0. Decide | SFRA or headless; site order on multi-site realms | Agreed route and first site |
| 1. Inventory | The step 1 table, per site | Every endpoint, job, hook and integration has an owner and a target |
| 2. Foundation | SFRA base and `modules`, `app_custom_*` skeleton, SFRA partner cartridges, `sgmf-scripts` build | SFRA running on a sandbox with your data |
| 3. Port | Routes, models, templates, forms, hooks, design | Feature parity list closed |
| 4. Jobs and integrations | Legacy jobs moved to the job framework; regression tests | Every job runs on staging |
| 5. SEO | URL rules, static mappings, head-tag diff | URL replay passes |
| 6. Rehearse | Cut-over and rollback on staging | Timed runbook; rollback proven |
| 7. Launch | Cartridge-path switch, hypercare | Errors, conversion and crawl stats steady |
| 8. Decommission | SiteGenesis cartridges removed; URL Archive cleaned | No SiteGenesis code in the active version |

---

## Key takeaways

- SiteGenesis has no announced end of life, and Salesforce has no plan to deprecate pipelines. The pressure is that new features such as Salesforce Payments target SFRA, and the storefront chooser omits SiteGenesis.
- The migration is a storefront rewrite on unchanged data. Catalogues, customers, orders and Business Manager configuration stay; controllers, templates and client code are rebuilt.
- Inventory endpoints, not pages. Every pipeline or controller name used by URL rules, static mappings, content or partners must resolve after cut-over.
- Pipelines become GET and POST route pairs, `OnRequest` and `OnSession` become hooks, legacy jobs move to `steptypes.json`, and forms keep their XML definitions.
- One code version holding both stacks turns cut-over and rollback into a cartridge-path change. If the long-term target is headless, evaluate Storefront Next first; its migration tool leaves pipeline logic as manual work.

For a migration assessment or an SFRA rebuild plan, see the [SFCC consulting services](https://www.ozanaltinbas.com/en/service) or [get in touch](https://www.ozanaltinbas.com/en/contact).

## Further reading

- [SFRA Frequently Asked Questions](https://developer.salesforce.com/docs/commerce/commerce-solutions/guide/sfra-faq.html): Salesforce's position on SiteGenesis support, platform data and LINK cartridges.
- [Is there a timeline for deprecating pipelines?](https://help.salesforce.com/s/articleView?id=000391508&language=en_US&type=1): the knowledge article confirming there is no plan to deprecate pipelines.
- [SFRA Features and Components](https://developer.salesforce.com/docs/commerce/sfra/guide/b2c-sfra-features-and-comps.html): cartridge layers, the `server` module, controller resolution and `OnRequest`/`OnSession` hooks.
- [Migrate Your Storefront to Storefront Next](https://developer.salesforce.com/docs/commerce/sfnext/guide/sfnext-migration-tool.html): the migration tool, its SiteGenesis requirements and its limits.
- [Code Deployment](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-code-deployment.html): code versions, activation, rollback and replication.
- [Static Mappings for B2C Commerce](https://help.salesforce.com/s/articleView?language=en_US&id=cc.b2c_static_mappings.htm&type=5): syntax and limits for legacy URL mappings.
