← Back to Blog

MIGRATION · April 24, 2026 · 13 min read

Cutting SharePoint migration time in half: what faster cutovers mean for your business

A migration window is not an abstract benchmark. It is the hours your team cannot edit documents, the evening your admins spend babysitting a progress bar, and the risk that a Sunday cutover bleeds into Monday morning with users waiting. When a SharePoint migration completes in about half the time of a single-threaded transfer, the benefit lands on the calendar first and in the engineering notes second.

This post walks through what a faster cutover actually changes for the business, then explains why SharePoint migrations on MigrationFox complete in the time they do — what is happening underneath, and why site migrations typically land in about half the wall-clock time of a single-threaded baseline, with governance scans 60–70% faster.

What a shorter migration window means for you

The user-visible difference between a long migration and a short one is almost never about throughput numbers. It is about:

The headline number matters less than the calendar math. “Half the wall-clock time” is the difference between a cutover ending at 2am and a cutover ending at midnight. For the admin on call, those two hours are not equivalent.

Why site migrations spend most of their time on small requests

To explain why MigrationFox is fast, it helps to be honest about where the time actually goes. For file-heavy migrations (SMB-to-Blob, large document libraries) the bottleneck is bytes per second — straightforward network transfer speed. For SharePoint site migrations it is only half the story.

A typical SharePoint site migration includes:

Most of the wall-clock time is spent on small requests, not on file bytes. A 5 GB document library can finish the byte transfer in five minutes but still spend twenty-five minutes waiting on 12,000 sequential Graph calls to tag each file with its content type and custom columns. Migrations run faster because the product optimizes the small-request path — HTTP round trips, serialization, ordering — not just the bytes-per-second number.

Graph $batch for the metadata path

Microsoft Graph’s /$batch endpoint accepts up to 20 sub-requests in a single HTTP round trip. For metadata-heavy phases — reading content types on a site, or applying column metadata across a list — that is a 20x reduction in network round trips.

Architecturally, content-type, site-column, and list-view enumeration phases are batched in groups of 20. On a site with 40 lists, that drops 120 round trips to 6. Each Graph round trip is typically 120–400ms, so the wall-clock difference on a mid-size site is roughly 30 to 45 seconds of dead air the customer does not have to sit through — multiplied across every phase of the migration.

Item writes follow a different pattern. Item creation in Graph has enough quirks (per-item field-stripping for invalid columns, per-item retry on 429, progressive fallback when a field fails) that $batch’s sub-request semantics (partial success, shared throttling bucket) are more hindrance than help. Writes take a different route.

Bounded parallelism with back-pressure

There are two naive approaches to parallel item writes. Fully sequential is safe and slow. Fully parallel with Promise.all is fast and prone to 429 storms. Neither is what a production migration needs. The pattern MigrationFox uses is bounded concurrency with back-pressure.

Every parallel phase runs with per-phase concurrency caps:

On top of the cap, the runner watches the Retry-After header from every 429. If throttling kicks in, the affected phase pulls its effective concurrency down and waits for the retry window before resuming. If the tenant is healthy, the cap stays at the configured value. No manual tuning.

The subtle benefit is not top-end speed — it is consistency. Fully-parallel implementations hit 429 storms that take 30–90 seconds to drain; bounded-parallel implementations barely see 429s at all. Migrations finish faster on average because they do not blow themselves up and have to back off.

Connection reuse and HTTP/2

Every HTTP library has a default connection pool. Default agents often create a new TCP + TLS connection per request unless keep-alive is explicitly enabled, and even then the pool defaults are modest. For a process making thousands of requests to graph.microsoft.com in quick succession, that TLS handshake overhead adds up fast.

MigrationFox’s Graph client runs with:

On a 5,000-item list, connection reuse accounts for roughly 25% of the phase wall-clock time. The first couple of requests still pay TCP+TLS, but everything after that reuses the established connection. Across a 40-minute site migration, that is meaningful time off the cutover window.

Request caching in the governance scanner

This one lives in the Copilot Readiness scanner rather than the migration path, but it is the same pattern: remove redundant Graph calls.

A governance scan runs six modules (Purview, Identity, SharePoint, Teams, OneDrive, Power Platform). The SharePoint module needs the sites list. The Teams module needs the sites list. The Identity and OneDrive modules each need the users list. A request cache scoped to a single scan run sits in front of the Graph client, keyed on the normalized URL. The first module to ask for /users?$select=id,userPrincipalName pays the API cost; every subsequent module gets the cached response. No Graph calls were removed from scan logic; they just stopped happening two or three times.

On a 1,200-user tenant, a full six-module scan completes in about 1m50s instead of 5m30s. The cache is discarded at the end of each scan, so the next run still reflects live tenant state.

The long tail of smaller optimizations

Not every architectural choice deserves its own section. A few worth mentioning because they are generally useful patterns:

None of these is dramatic in isolation. Together they are the difference between a migration that finishes sometime tonight and one that is done by the time you get back from lunch.

What this looks like on real tenants

On three tenants where reliable timing data is available, typical migration windows look like this:

WorkloadWall-clock time
Mid-size SP site (40 lists, 12k items, 8 GB)~52m
Small SP site (6 lists, 800 items, 400 MB)~4m 10s
Governance full scan (1,200 users)~1m 50s
Azure Blob ingest (SMB, 1 TB)~3h 15m

The Azure Blob ingest number is dominated by the bytes-per-second of the network link, not per-request overhead, so the same architectural patterns matter less there. Architecture helps less when the wall clock is already set by hardware.

On Graph-based site migration, the per-request cost is the limiting factor, and that is where the architecture earns its keep. Migrations complete in about half the time they would on a single-threaded transfer.

Patterns that do not help

A few approaches look good on paper but do not deliver:

Related reading

Get started

Every workspace runs on this fast path by default — nothing to configure. Start a free SharePoint migration at app.migrationfox.com/register and watch your first pre-flight report come back in seconds rather than minutes.

Migrate on the fast path

Free account. Shorter cutover windows by default.

Start Free →