Migrating from Synapse to Microsoft Fabric: What Maps Cleanly, What Does Not, and How to Sequence It
A component-by-component migration map from Synapse to Fabric, the T-SQL and workload management gaps that bite, and the order I move things in to keep risk low.
By Dinesh Chandra
Table of contents
- The component map, honestly
- Start with shortcuts, because they cost nothing
- Spark and pipelines: the easy ports
- Where the dedicated SQL pool migration gets hard
- Capacity: the cost model changes shape
- The sequence I use
- Pitfalls
- FAQ
- Is Synapse being deprecated?
- Can Fabric and Synapse coexist on the same data?
- What happens to my dedicated SQL pool stored procedures?
- Does dbt work against Fabric?
- How do I size the F SKU?
- Is Direct Lake actually as fast as import mode?
- What this means for your pipelines
The Synapse-to-Fabric conversation usually starts in the wrong place. Someone reads that Fabric is the future of Microsoft analytics, opens a migration guide, and starts by asking how to move the dedicated SQL pool. That is the hardest component, the one with the most surface area, and the one that will make the whole project look impossible in week two.
I have run this migration for two organizations now, and the thing that made both of them work was inverting that instinct. Fabric is not a platform you cut over to. It is a workspace you can point at data you already have, one workload at a time, with OneLake shortcuts making the storage layer a non-event. The dedicated SQL pool can keep running for a year while everything around it moves.
This post is the component map — what ports cleanly, what needs a rewrite, what has no equivalent — and the sequence I use. If you want the broader Azure context first, the Azure data engineering overview covers how these products relate.
The component map, honestly
Synapse Analytics was four largely independent products sharing a workspace. Fabric decomposes them differently, which is why a one-to-one mapping does not exist.
| Synapse component | Fabric equivalent | Migration difficulty |
|---|---|---|
| Spark pools and notebooks | Fabric notebooks on Spark | Low |
| Synapse pipelines | Fabric Data Factory pipelines | Low |
| Serverless SQL pool | SQL analytics endpoint on a Lakehouse | Medium |
| ADLS Gen2 storage | OneLake, or a shortcut to ADLS | Very low |
| Dedicated SQL pool | Fabric Warehouse | High |
| Synapse Link | Fabric mirroring | Medium |
| Power BI on import | Direct Lake semantic models | Low, high payoff |
That table is the whole project. Five of the seven rows are easy, one row is most of the work, and the sequencing insight is that the easy rows deliver most of the visible value.
flowchart TD
adls["ADLS Gen2 (stays put)"] --> sc["OneLake shortcut"]
sc --> lh["Fabric Lakehouse"]
lh --> sqlep["SQL analytics endpoint"]
lh --> dl["Direct Lake semantic model"]
dl --> pbi["Power BI reports"]
nb["Synapse notebooks"] --> fnb["Fabric notebooks"]
fnb --> lh
dsp["Dedicated SQL pool"] -.->|"last, and only if needed"| wh["Fabric Warehouse"]
Shortcuts first. The warehouse migration is the dotted line you do at the end, not the beginning.
Start with shortcuts, because they cost nothing
A OneLake shortcut is a pointer to data in ADLS Gen2, S3, or another OneLake location. No copy, no sync job, no dual-write window. A Fabric Lakehouse with a shortcut to your existing Delta tables can be queried by Fabric notebooks, the SQL analytics endpoint, and Direct Lake semantic models immediately.
This changes the risk profile of the entire migration. You are not migrating data. You are adding a compute surface over data that stays exactly where it is, still being written by the pipelines you already have.
{
"name": "curated_sales",
"path": "Tables",
"target": {
"adlsGen2": {
"location": "https://acmedatalake.dfs.core.windows.net",
"subpath": "/curated/sales",
"connectionId": "b7e1c8a4-0000-0000-0000-9f2d5c1a3b40"
}
},
"comment": "Read-only pointer. Synapse Spark keeps writing here; Fabric reads it."
}
Two constraints to know before you build on this. Shortcuts to
Delta tables show up as tables only if the folder is a valid Delta
table at the root; a folder of Parquet under a Hive-style
partition layout lands in the Files area instead and needs a
table definition. And writes through a shortcut to ADLS are
supported but they bypass some Fabric-side optimizations, notably
V-Order, so tables written from outside Fabric will not be as fast
in Direct Lake as tables written by Fabric.
That second point matters more than it sounds. V-Order is a write-time optimization on Parquet that makes Direct Lake reads dramatically faster. If your Direct Lake reports are slower than expected on shortcut data, that is usually why, and the fix is either writing from Fabric or accepting a one-time rewrite of the hot tables.
Spark and pipelines: the easy ports
Synapse Spark notebooks move to Fabric notebooks with three categories of change:
- Paths.
abfss://container@account.dfs.core.windows.net/pathbecomes either a OneLake path or, better, a relative path against the attached Lakehouse. - Session config.
%%configureblocks and pool settings map to Fabric environments, which is where you now pin library versions and Spark properties. - Linked service auth. Synapse’s linked services become Fabric connections, and the identity model changes from workspace managed identity to the Fabric workspace identity.
The Spark code itself — the DataFrame logic that is the actual value — does not change. Here is a typical before-and-after in the only part that does:
# Synapse: absolute ABFSS paths, credentials via linked service.
raw = spark.read.parquet(
"abfss://raw@acmedatalake.dfs.core.windows.net/erp/sales_order/"
)
# Fabric: relative to the attached Lakehouse, or OneLake ABFSS.
raw = spark.read.parquet("Files/erp/sales_order/")
# Writing a managed Delta table in the Lakehouse. V-Order is on by
# default in Fabric; leave it on unless you are write-throughput bound.
(
raw.filter("order_date >= '2026-01-01'")
.write.mode("overwrite")
.option("overwriteSchema", "true")
.partitionBy("order_date")
.format("delta")
.saveAsTable("curated_sales")
)
# Maintenance still matters. Fabric surfaces this as table
# maintenance in the UI, but scheduling it in code is more honest.
spark.sql("OPTIMIZE curated_sales VORDER")
spark.sql("VACUUM curated_sales RETAIN 168 HOURS")
Everything you know about Delta still applies — the Delta Lake and lakehouse guide transfers directly, because Fabric’s storage layer is Delta with Microsoft’s V-Order optimization on top. If you are weighing table formats more broadly, Delta vs Iceberg is relevant, though inside Fabric the choice is effectively made for you.
Synapse pipelines port even more cleanly, since Fabric Data Factory is the same engine. The metadata-driven pattern from the ADF patterns post works unchanged; what changes is that activities target Lakehouse and Warehouse items instead of linked services, and there are gaps in activity support you should check against your specific pipeline inventory before promising a date.
Where the dedicated SQL pool migration gets hard
This is the section people need and rarely get straight.
Fabric Warehouse is a new engine. It is not the MPP dedicated SQL pool with a new name, and it does not implement the same T-SQL surface. The things that reliably break:
Distribution and index control is gone. No DISTRIBUTION = HASH(col), no CLUSTERED COLUMNSTORE INDEX declarations, no
REPLICATE for small dimensions. Fabric decides physical layout.
For a well-tuned dedicated SQL pool this is disorienting: your
carefully hash-distributed fact tables lose the thing that made
their joins fast, and you cannot get it back. In practice the
engine handles most cases well, but you will find a query or two
that regresses and there is no distribution knob to turn.
Resource classes and workload management are gone. Dedicated SQL pools let you assign resource classes and workload groups to control memory per query and enforce isolation. Fabric uses capacity units with smoothing and bursting instead. There is no per-query memory grant to configure.
Several DDL features are missing. Identity columns, enforced
constraints, materialized views, and some CREATE TABLE AS
variants either behave differently or do not exist. Temp table
semantics differ. Anything using CTAS with a distribution clause
needs rewriting.
External tables work differently. In Fabric Warehouse, reading
external files is done through the Lakehouse and shortcuts rather
than CREATE EXTERNAL TABLE with an external data source. This is
arguably better, and it is definitely a rewrite.
-- Synapse dedicated SQL pool: physical design is in the DDL.
CREATE TABLE dbo.fact_sales
WITH (
DISTRIBUTION = HASH(customer_key),
CLUSTERED COLUMNSTORE INDEX,
PARTITION (order_date_key RANGE RIGHT FOR VALUES
(20250101, 20250401, 20250701, 20251001))
)
AS SELECT * FROM staging.sales;
-- Fabric Warehouse: none of that is expressible. This is the port.
CREATE TABLE dbo.fact_sales (
sale_key BIGINT NOT NULL,
customer_key BIGINT NOT NULL,
order_date_key INT NOT NULL,
amount_cents BIGINT NOT NULL
);
INSERT INTO dbo.fact_sales
SELECT sale_key, customer_key, order_date_key, amount_cents
FROM staging.sales;
-- MERGE is supported, which covers most incremental patterns.
MERGE dbo.fact_sales AS t
USING staging.sales_delta AS s
ON t.sale_key = s.sale_key
WHEN MATCHED THEN UPDATE SET amount_cents = s.amount_cents
WHEN NOT MATCHED THEN INSERT (sale_key, customer_key, order_date_key, amount_cents)
VALUES (s.sale_key, s.customer_key, s.order_date_key, s.amount_cents);
Before you commit to a date, run an inventory. Script every object in the dedicated SQL pool, grep for the constructs above, and count. That number — not the data volume — is your migration estimate. On the last one I did, 380 objects produced 41 that needed real work and about 12 that needed a design decision.
The other question worth asking honestly: does the warehouse need to move at all? A Lakehouse with a SQL analytics endpoint serves read workloads over Delta tables perfectly well, and many dedicated SQL pools exist only to serve Power BI. If Direct Lake over a Lakehouse covers the reporting need, you may be able to retire the warehouse rather than migrate it. That is the best outcome available and it is worth an afternoon of analysis.
Capacity: the cost model changes shape
Dedicated SQL pools bill DWUs, which you scale and pause explicitly. Fabric bills capacity units on an F SKU, shared across every workload in the capacity: notebooks, pipelines, warehouse queries, Power BI rendering, everything.
Three behaviors that will surprise you:
Smoothing. Fabric averages your consumption over a window rather than billing instantaneous peaks. A burst that would have required a bigger dedicated pool can be absorbed. This is genuinely good and it makes capacity planning less about peak sizing.
Throttling. When you consistently exceed the capacity, Fabric does not fail queries immediately — it delays and eventually rejects interactive operations. The experience is “everything got slow” rather than “a query failed,” which is harder to diagnose. The Capacity Metrics app is the only place the truth lives; install it on day one.
One capacity, many neighbors. A runaway Spark notebook can degrade Power BI reports in the same capacity. On a dedicated SQL pool, the warehouse had its own compute. Plan for either separate capacities per domain or a genuine chargeback conversation, and put it in your data contracts if you have them.
For sizing: start smaller than the equivalent DWU math suggests, because smoothing changes the calculation, and watch the metrics app for two weeks before resizing. F SKUs can be paused and resized, so being wrong is cheap and reversible in a way DWU sizing never quite was.
The sequence I use
Six phases, ordered so that each one delivers value and none of them is a cutover.
Phase 1 — Shortcut and observe. Create the workspace, shortcut ADLS into a Lakehouse, install the Capacity Metrics app. Nothing in production changes. You now have a Fabric surface over real data and a baseline for capacity.
Phase 2 — Power BI to Direct Lake. Convert the highest-value import-mode semantic models to Direct Lake against the Lakehouse. This eliminates refresh windows and is the change business users actually notice. It also stress-tests your V-Order situation early, while there is time to react.
Phase 3 — Spark workloads. Port notebooks. Run them in parallel with Synapse for two weeks and compare outputs row for row, not just counts. This is where you find the environment and library differences.
Phase 4 — Pipelines. Move orchestration to Fabric Data Factory once the notebooks it calls are already there. Keep the control tables; they are portable.
Phase 5 — Serverless SQL consumers. Repoint anything querying serverless SQL pool views at the SQL analytics endpoint. The T-SQL is close enough that most views port with path changes only.
Phase 6 — Dedicated SQL pool. Last, with the object inventory from earlier as the plan, and only after asking whether it can be retired instead. Run both in parallel through at least one full month-end close before decommissioning.
Every one of those phases is independently valuable and independently reversible. That is the property that gets a migration funded and finished, as opposed to a big-bang plan that gets cancelled in month five.
Pitfalls
Starting with the warehouse. It is the hardest component, the least reversible, and the one with the fewest visible wins. Doing it first is how migrations lose sponsorship.
Copying data into OneLake when a shortcut would do. Copies need sync jobs, and sync jobs need reconciliation, and now your migration includes building a dual-write pipeline nobody asked for.
Assuming Fabric Warehouse is dedicated SQL pool T-SQL. Run the object inventory before you commit to an estimate. The gap is real and it is concentrated in exactly the objects your best engineers wrote.
Ignoring V-Order on shortcut data. Direct Lake performance over externally written Parquet can disappoint. Test with your largest table before you promise executives faster dashboards.
One capacity for everything. Spark, pipelines, warehouse, and Power BI competing for the same CUs means an engineer’s backfill becomes a business user’s outage. Separate capacities or explicit scheduling.
No parallel run through month-end. Financial reporting has edge cases that only appear at close. Two weeks of parallel running that excludes a month-end has tested nothing that matters.
FAQ
Is Synapse being deprecated?
Microsoft has been clear that Fabric is the strategic direction and equally clear that Synapse workspaces continue to be supported. There is no forced migration date to plan against today. What is true is that new capability lands in Fabric first, so the gap widens over time rather than the platform disappearing.
Can Fabric and Synapse coexist on the same data?
Yes, and that is the recommended approach. Shortcuts let Fabric read ADLS Gen2 that Synapse Spark is still writing. Both engines reading the same Delta tables works, with the usual caveat that concurrent writers to one Delta table need coordination.
What happens to my dedicated SQL pool stored procedures?
Most port with edits. The failures cluster around distribution
hints in CTAS, identity columns, temp table behavior, and
anything using dynamic SQL to build distributed DDL. Inventory
them first; the count of procedures is a much better estimate of
effort than the size of the database.
Does dbt work against Fabric?
Yes, there is a Fabric adapter for both Warehouse and the SQL analytics endpoint, and moving transformation logic into dbt before you migrate is one of the best things you can do — incremental models in particular give you a portable definition of your loads instead of platform-specific procedures.
How do I size the F SKU?
Do not translate DWUs directly. Start with a mid-range F SKU, enable the Capacity Metrics app, and run a representative workload for two weeks. Smoothing means the peak-based math from dedicated pools overestimates. Resizing is fast and pausing non-production capacities overnight is real money.
Is Direct Lake actually as fast as import mode?
For well-maintained V-Ordered Delta tables, close enough that the elimination of refresh windows wins easily. For tables with many small files or no V-Order, it falls back to DirectQuery behavior and gets noticeably slower. Table maintenance is not optional in Direct Lake — it is a reporting performance feature.
What this means for your pipelines
Treat Fabric as an additive compute surface, not a destination you migrate to. Shortcut your existing lake, put a Lakehouse over it, and let Fabric prove itself on Power BI and Spark workloads while every production pipeline keeps running exactly as it does today. That framing turns a scary platform migration into six independent projects, any of which can be paused without leaving you stranded.
Spend your real engineering effort on the dedicated SQL pool inventory, and spend it early even though the migration happens last. The count of objects using distribution hints, identity columns, and materialized views is the only honest estimate you will get, and knowing it in month one prevents the commitment you regret in month six. While you are in there, ask whether the warehouse needs to exist at all after Direct Lake — retiring a component beats porting it every time.
The broader lesson is the one that applies to every platform migration I have been part of: the transferable assets are the ones written as data and text. Control tables, dbt models, Delta files, and notebook logic all moved with minor edits. Stored procedures, distribution designs, and anything encoded in a proprietary engine’s physical layer did not. Build the next thing accordingly, because Fabric will not be the last platform decision this organization makes.
Enjoyed this post?
Get the next one in your inbox — one email a week, no spam.
Newsletter signup is not live yet. Use the contact form if you want to be notified.