Why

A plain (flat) install writes files straight into the install directory. To update it you must overwrite the running files — which fails when they're locked, and can't be a delta. The side-by-side layout installs each version into its own folder and points a current junction at it; an update materializes a new version beside the old one and flips the junction. The running version is never written to.

Enable it

Set SideBySide in the project. Flat install remains the default, so nothing changes for apps that don't opt in.

{ "AppName": "MyApp", "AppVersion": "1.2.0", "SideBySide": true,
  "AppUpdatesURL": "https://updates.example.com/stable/feed.json" }

The layout

C:\Program Files\MyApp\        (the base — what the user chose)
   ├─ current  ──►  app-1.2.0   (a junction shortcuts and ARP point at)
   ├─ app-1.2.0\   MyApp.exe, …, _payload-manifest.json, update-settings.json
   └─ app-1.3.0\   (materialized by the next update, then current flips here)

How it comes together at install time (all no-ops for a flat install):

PieceWhat it does
InstallContextBuilderSets InstallPath = <base>\app-<version>, LaunchPath = <base>\current, and the SideBySide flag. For a flat install every path is equal.
JunctionCreateStepPoints current at the installed version (a junction — no elevation needed) and stamps the runtime InstallRoot into the shipped update-settings.json.
ShortcutCreateStepTargets the launch path (current), so a version flip is transparent to shortcuts.
ExtractSolidShips _payload-manifest.json beside the files so a delta update can diff against what's on disk.
UninstallStepRemoves the whole product-owned base tree (all app-* folders + the junction).

How an update applies

The Updates domain's SideBySideApplier stages into <base>\.staging-<ver>, verifies every blob's SHA-256 as it writes (a mismatch aborts before any flip), promotes to app-<ver>, then flips current. A crash before the flip is harmless (the junction still points at the old version and stale staging is swept next time); Rollback flips back; RetireOldVersions deletes all but current + previous.