Compatibility layers  / field guide
Practitioner field guide · 17 September 2026

Emulate, then own: ten years of Microsoft leaving its own compatibility layers

Between 2016 and 2026 Microsoft shipped three clever compatibility layers, operated each one at scale, and then replaced it with the real system it had been faking: a virtual filesystem for Git, a Linux system-call translator, and a compiler hosted on a JavaScript virtual machine. This guide reconstructs all three from the repositories, design documents, release notes and issue threads they left behind, and extracts the condition that decides, before you build, whether a shim is an abstraction or a debt.

31 primary sources 4 programmes traced 8 defect and regression threads Evidence through September 2026 Read: 34 min
01

The territory

One problem, stated without naming any technology: you have to run somebody else's thing on your substrate, and you can either fake its interface or host its implementation.

Every architect meets this fork. Another system has semantics you need and you cannot adopt it directly, because it is too slow at your scale, or it does not run where you need it, or it belongs to a vendor whose release cadence is not yours. So you build a layer that presents the same interface and does something cheaper underneath. The layer ships in months, works immediately for the common case, and is the reason your project is viable at all. Then you spend years discovering that the interface you promised is larger than the interface you implemented, and the difference belongs to somebody else's roadmap.

Microsoft ran that experiment three times in public between 2016 and 2026, on three unrelated parts of its developer platform, and it ended the same way each time. The evidence for all three sits in repositories anybody can read, which is the only reason this guide exists: the network policy in force for this research reached github.com and nothing else, so every claim below comes from a README, a design document, a release note, an issue thread or a commit, and none of it comes from a blog post, a talk or a conference paper.

3.5M
files per commit in the repository shape that broke ordinary Git, with 500K directories
20×
faster unpacking a tarball on WSL 2 than on the translation layer it replaced, and 2 to 5× on clone and install
11×
slower writing to a Windows drive from WSL 2 than from WSL 1, measured by a reporter at 40.4 against 442 MB/s
7 yrs
that the two defects created by the new boundary have stayed open on the tracker
The finding that surprised me

None of the three replacements removed the cost. Each one moved it to a new boundary and made it measurable, and in two of the three cases the superseded shim is still shipping because it is still the better answer for one specific workload. Microsoft's own documentation tells readers to choose WSL 1 over WSL 2 when their files must live on the Windows filesystem, and microsoft/git still carries the GVFS protocol that Git rejected, seven Git releases after Scalar landed in the upstream product.

Figure 1 · Three programmes, one shape

upstream features
land, 2019 to 2022

full syscall
compatibility

structural port
announced 2025

Git at scale, 2017
VFS for Git projects
a fake full checkout

Scalar configures
real Git; ships inside
core Git 2.38

Linux on Windows
WSL 1 translates
system calls

WSL 2 runs a real
kernel in a VM;
source public 2025

TypeScript
tsc self-hosted on
a JavaScript runtime

TypeScript 7 ships
as a native binary,
2026

upstream features
land, 2019 to 2022

full syscall
compatibility

structural port
announced 2025

Git at scale, 2017
VFS for Git projects
a fake full checkout

Scalar configures
real Git; ships inside
core Git 2.38

Linux on Windows
WSL 1 translates
system calls

WSL 2 runs a real
kernel in a VM;
source public 2025

TypeScript
tsc self-hosted on
a JavaScript runtime

TypeScript 7 ships
as a native binary,
2026

Each lane starts with a layer that imitates a system and ends with the real system hosted directly. Dates are from the artefacts themselves: Scalar's absorption is recorded in the Git 2.38 release notes, the WSL source publication in the repository's first open source commit, and the compiler port in discussion 411.
Diagram source

Scope. This guide covers Microsoft's developer-facing runtimes and tooling: Git at monorepo scale, the Linux subsystem, the TypeScript compiler, and the Mono runtime as a fourth and shorter case. It does not cover Azure's internal infrastructure, Windows itself, Microsoft 365, or the serving architecture behind its AI products, because none of those have a comparable public repository record. It also does not cover the commercial arguments; the sources here are engineering artefacts, and they are silent on revenue.

02

How a compatibility layer is actually built

The common shape across the three programmes, with the two components that every account underplays: the protocol you accidentally own, and the tail you never implement.

Put the three side by side and the same four parts appear. There is an interception point, placed as low as the platform allows, because the lower it sits the more software it fools: a filesystem projection driver in the kernel for VFS for Git, a system-call translation layer for WSL 1, and for the compiler the JavaScript runtime itself, which is an interception point Microsoft did not choose so much as inherit from the decision to self-host. There is an emulated core, the portion of the target's behaviour that is both common and cheap. There is a tail, the portion that is neither, which the shim does not implement and documents its way around. And there is, usually unplanned, a protocol or format you now own, because faking an interface efficiently nearly always requires inventing a side channel.

That last part is the one that turns a shim into a programme. VFS for Git could not fetch objects one at a time over ordinary Git transport and stay interactive, so Microsoft specified the GVFS protocol: four operations on three endpoints, including a GET /gvfs/prefetch that streams packfiles and a POST /gvfs/sizes that exists purely so the projected filesystem can report file sizes without downloading content. Read the protocol document and you are looking at a second Git wire protocol, with a cache-server tier behind it, owned and versioned by a team whose product was supposed to be a filesystem driver.

Figure 2 · The four parts of a compatibility layer

Unmodified workload
written for the real system

Interception point
kernel driver, syscall table,
host runtime

Emulated core
common, cheap behaviour

The tail
rare, expensive, someone
else's semantics

Protocol or format
you now own and version

Acceleration tier
cache servers, prefetch

The real system
on the far side

Documented workaround:
tell users to avoid it

Unmodified workload
written for the real system

Interception point
kernel driver, syscall table,
host runtime

Emulated core
common, cheap behaviour

The tail
rare, expensive, someone
else's semantics

Protocol or format
you now own and version

Acceleration tier
cache servers, prefetch

The real system
on the far side

Documented workaround:
tell users to avoid it

The two boxes on the right are the ones that decide whether the layer is an abstraction or a programme. Reconstructed from the GVFS protocol document and Microsoft's description of WSL 1 as a translation layer built by the WSL team.
Diagram source

The replacement architecture has a different shape, and it is worth naming precisely because it is not simply "use the real thing". In all three cases what shipped was the real implementation plus a narrow bridge at exactly one boundary, plus an explicit compatibility ledger of behaviour that was dropped. WSL 2 runs a real Linux kernel, built by Microsoft from the stable branch and published as its own kernel tree, and bridges to Windows files across the virtual machine boundary. Scalar runs stock Git and bridges to the object store through a promisor remote. The Go compiler runs as a native binary and bridges to the JavaScript ecosystem over a protocol rather than in process, which the port's own authors flagged as the weak point of the language choice.

Scalar is the cleanest instance of the pattern, because Microsoft wrote the policy down. The philosophy document says it in two sentences: "Scalar intends to do very little more than the standard Git client. We actively implement new features into Git instead of Scalar, then update Scalar only to configure those new settings." What had been a filesystem driver became a list of configuration defaults, and the feature list is a roll call of upstream Git work: partial clone, background prefetch, sparse-checkout, the filesystem monitor, the commit-graph, the multi-pack-index and incremental repack.

Figure 3 · The same command, before and after

Scalar on core Git: change the tool

git status

FSMonitor daemon

Sparse index
O(Populated), not O(HEAD)

Promisor remote
partial clone

Any Git host

VFS for Git: fool the tool

git status

Projection driver
in the OS kernel

Hydrate on access

GVFS cache server

Azure Repos

Scalar on core Git: change the tool

git status

FSMonitor daemon

Sparse index
O(Populated), not O(HEAD)

Promisor remote
partial clone

Any Git host

VFS for Git: fool the tool

git status

Projection driver
in the OS kernel

Hydrate on access

GVFS cache server

Azure Repos

The work did not get smaller, it moved out of the kernel and into the index format and the transport. The upper path is described in the GVFS protocol; the lower one in the sparse-index design document and the Scalar feature list.
Diagram source

Interception point

Chosen for reach, paid for in blast radius. A kernel-resident driver fools every tool on the machine, and it also panics every tool on the machine, which is what the macOS issue threads record.

Evidence: VFS for Git issue 328, issue 340

The protocol you did not plan to own

The side channel that makes the emulation fast becomes a versioned interface with servers, clients and a compatibility promise. Nine years on it is the reason the Git fork still exists.

Evidence: GVFS protocol v1, microsoft/git README

The compatibility ledger

The artefact that makes leaving possible. The Go port shipped a file listing every behaviour it deliberately does not reproduce, which is what converted an unbounded rewrite into a bounded one.

Evidence: typescript-go CHANGES.md

03

The decisions that matter

Six forks in the road, each with what was chosen, what was rejected, the reason stated in the sources, and the condition that flips it.

Decision: fool the tool, or change the tool?

Chosen, 2017
  • VFS for Git: project a virtual filesystem so that unmodified Git believes it has a full checkout.
  • It works with the Git you already have, on the repository you already have, with no upstream negotiation.
Rejected then, chosen later
  • Change Git: partial clone, sparse-checkout, sparse index, a filesystem monitor, background maintenance.
  • Rejected because none of it existed, and shipping it meant years inside another project's review process.
Flips when
  • You can land the feature upstream on a timescale shorter than the shim's maintenance horizon.
  • Microsoft's own verdict, written into the VFS for Git README: for new deployments "we strongly recommend you consider Scalar instead".

Decision: translate the interface, or host the real implementation?

Chosen, WSL 2
  • Run a real Linux kernel in a managed virtual machine.
  • The stated reason is not speed. It is that "any updates to the Linux kernel are immediately ready for use", with no waiting on the WSL team.
Rejected
  • Continuing to grow the syscall translation layer that the WSL team wrote and owned.
  • Every new kernel interface was a Microsoft work item, and the compatibility gap never closed.
Flips when
  • Your workload crosses the boundary constantly. Microsoft still recommends WSL 1 when the files must live on the Windows filesystem.
  • Read that as a rule: virtualisation converts a semantics problem into a data-movement problem, and if you were already data-movement bound you lose.

Decision: rewrite the compiler, or port it?

Chosen, 2025
  • A structural port to Go, described by Ryan Cavanaugh as "more as a port that maintains the existing behavior and critical optimizations".
  • Go was picked because "idiomatic Go strongly resembles the existing coding patterns of the TypeScript codebase", with control of memory layout and a garbage collector.
Rejected
  • A clean-sheet rewrite, and languages whose idiom would have forced the code to be restructured during the move.
  • Also accepted as a cost: "Go's in-proc JS interop story is not as good as some of its alternatives".
Flips when
  • The existing implementation is not the specification. A port only works when bug-for-bug fidelity is the goal and the old code is the only written-down truth.
  • If you have a real spec and a test suite that encodes it, a rewrite becomes the cheaper option.

The remaining three decisions are smaller but carry most of the day-two consequences. The first is what to do with the protocol the shim left behind. Microsoft's answer is visible in the fork's README, and it is unusually candid: the GVFS protocol "is not appropriate to include in the core Git client because partial clone is the official version of that functionality". The protocol lives on because Azure Repos speaks it, so the fork lives on too, and is still cutting releases in 2026 against upstream Git 2.55. A shim's protocol outlives the shim by as long as its servers do.

The second is who owns the defaults after you move upstream. When Derrick Stolee opened a series to audit and document Scalar's configuration in November 2025, the framing was that "the Scalar config options could use some documented justification", and the review that followed pulled in Junio Hamano, Patrick Steinhardt and Johannes Schindelin before the series was taken. Stolee also reported that "while working to justify each config option, I found some stale or incorrect config options". This is the hidden price of hosting the real thing: your tuning becomes a public argument, conducted on someone else's schedule, and some of your settings turn out to have been wrong for years.

The third is the one most teams never make explicitly, and it is the one that decides whether the escape is fundable. The Go port shipped a changes file that enumerates what the new compiler deliberately does not do: Closure header files and most Closure-specific features, a set of JSDoc tag behaviours, and constructor-function expando declarations. The document's own justification is that trimming "makes the implementation much simpler and more like TypeScript". You do not get to leave the compatibility layer while still promising everything the compatibility layer promised.

Figure 5 · The question to answer before you build the layer

yes

no

no

yes

no

yes

Do you control the
interface you are imitating?

It is an ordinary abstraction.
Build it.

Can you name, in writing,
the part you will never implement?

The work has no defined end.
Host the real thing now,
or do not start.

Does the workload cross
the new boundary constantly?

Host the real implementation
behind one narrow bridge.

Ship both, scoped by workload,
and document which is which.

yes

no

no

yes

no

yes

Do you control the
interface you are imitating?

It is an ordinary abstraction.
Build it.

Can you name, in writing,
the part you will never implement?

The work has no defined end.
Host the real thing now,
or do not start.

Does the workload cross
the new boundary constantly?

Host the real implementation
behind one narrow bridge.

Ship both, scoped by workload,
and document which is which.

Derived from the three programmes rather than reported by any of them: the branch that decides everything is whether you can write down the part you will never implement, as the Go port did in its changes file.
Diagram source
DecisionChosenRejectedBecauseFlips whenEvidence
Interception point for Git at scaleKernel filesystem projectionUpstream Git featuresThey did not exist yetUpstream cadence beats your maintenance horizonVFS for Git README
Scope of the replacement toolConfiguration onlyA second Git-like product"We actively implement new features into Git instead of Scalar"The setting you need cannot be upstreamedScalar philosophy
Fate of the private protocolKeep it in a forkPropose it upstream"Partial clone is the official version of that functionality"Your servers can speak the standardmicrosoft/git README
Linux compatibility on WindowsReal kernel in a VMGrowing the syscall translatorKernel updates arrive without a Microsoft work itemCross-boundary file traffic dominatesWSL version comparison
Compiler moveStructural port to GoClean-sheet rewriteExisting behaviour is the specificationYou have a real spec and conformance testsDiscussion 411
Compatibility surface of the new implementationNarrow it, in writingFull fidelitySimpler implementation, fewer inherited odditiesThe dropped tail has no migration pathCHANGES.md
04

What broke, in four classes

There are no published Microsoft incident reports in this corpus, for the reason given in section 1. What there is instead is better in one respect: defect threads and release notes, with dates, states and the maintainers arguing in the open.

Sorted by mechanism rather than by product, the failures fall into four classes, and each class attaches to a different part of Figure 2. The first two are failures of the shim. The third is a failure of the escape. The fourth is the failure of never escaping at all.

Class A: the surface you do not own

Defect thread

The projection driver panics the machine

AssumptionA filesystem shim can be made reliable by making the shim correct.
What happenedOn macOS, VFS for Git ran as a kernel extension. Its vnode and file-operation handlers hit intermittent I/O failures, "failed assertions on reading file attributes" and "panics in file close events". A separate thread tracks the extension colliding with an endpoint protection product, and another a kernel panic when touching a FUSE mount.
Blast radiusThe whole machine, not the repository. Issue 328 was filed on 2018-10-03 and is still open at the date of this guide.
FixThe assignee's framing was to handle every failure case "gracefully in the kext instead of crashing the system". The structural fix arrived later and elsewhere: the replacement runs entirely in user space.
Design ruleIf your compatibility layer lives inside another vendor's kernel, your reliability is the product of their driver model and every security product your users install. Price that in before you choose the interception point.
Defect thread

The emulation's reach is its platform's reach

AssumptionA tool that fools Git will be portable, because Git is portable.
What happenedThe projection mechanism was tied to a specific operating system generation from the first weeks of the public repository. Issue 13, filed on 2017-02-06, is a user objecting that the tool required Windows 10. The macOS port then needed a second, entirely different kernel mechanism, with its own defect class.
Blast radiusAdoption, not availability. Each new platform meant a new driver, not a new build target.
FixScalar works anywhere Git works, because it is Git configuration. The Scalar documentation notes the filesystem monitor is configured "if using macOS or Windows", which is a capability statement rather than a port.
Design ruleCount the interception points you will need: one per platform, each with independent failure modes. That number, not the feature list, is the shim's true size.

Class B: the cost moves to the new boundary

Defect thread

Cross-boundary file access is 11 times slower, seven years on

AssumptionReplacing translation with a real kernel makes everything faster, because the documented speedups are large.
What happenedInside the virtual machine's own filesystem it is faster, up to 20 times on tarball extraction by Microsoft's own figures. Reaching a Windows drive now crosses the virtual machine boundary per operation. The reporter measured 40.4 MB/s against 442 MB/s on the same drive under WSL 1.
Blast radiusEvery developer whose source tree lives on the Windows side. Filed 2019-06-19, still open at the date of this guide.
FixThere is no fix, there is an instruction: keep the files on the same side as the tools. The documentation promotes it to a reason to keep using the old shim.
Design ruleMeasure cross-boundary operations per second before you migrate, not bytes per second. Virtualisation prices the boundary crossing, and a workload made of many small file operations pays that price on every one.
Defect thread

The guest holds memory the host thinks it has

AssumptionA managed virtual machine is an implementation detail the user never has to think about.
What happenedThe virtual machine's page cache is not returned to Windows until the instance shuts down. A reporter measured the VM process holding 7 GB of 16 GB. Microsoft's documentation, dated November 2024, still describes this as tracked work and links to the same 2019 issue.
Blast radiusMachines with strict memory budgets. Filed 2019-06-17, still open at the date of this guide.
FixConfiguration knobs and shutting the instance down. The documentation lists strict memory requirements as another reason to prefer WSL 1.
Design ruleA virtual machine boundary is a resource-accounting boundary. Whatever the guest caches is invisible to the host's scheduler, so plan capacity for the sum of two systems that each believe they are managing the memory.
SourceWSL 4166

Figure 4 · Where the cost went when the shim left

Windows filesystemVM boundarychannelWSL 2 Linux kernelLinux processWindows filesystemVM boundarychannelWSL 2 Linux kernelLinux processreporter measured 40.4 MB/s here,against442 MB/s through the old translationlayerwrite() on /mnt pathforward the operationround trip per operationcompletioncompletionreturn
Windows filesystemVM boundarychannelWSL 2 Linux kernelLinux processWindows filesystemVM boundarychannelWSL 2 Linux kernelLinux processreporter measured 40.4 MB/s here,against442 MB/s through the old translationlayerwrite() on /mnt pathforward the operationround trip per operationcompletioncompletionreturn
The same write, after the translation layer was replaced by a virtual machine: the semantic translation disappeared and a per-operation boundary crossing appeared. Throughput figures from WSL issue 4197.
Diagram source

Class C: the escape breaks the thing it improves

Release notes

Three sparse-index defects in one Git release

AssumptionMoving your optimisation into the upstream product de-risks it, because more people review it.
What happenedThe sparse index changes the on-disk index format so that whole directories outside the checkout cone are single entries. Git 2.34's release notes carry three separate fixes: index corruption from "stale and/or uninitialized data", broken rejection of paths with trailing slashes, and any use of index_name_pos() silently expanding the sparse index and breaking code that walks the cache tree. Git 2.35 records another, in the path-matching code that decides what is inside the cone.
Blast radiusEvery Git user on those releases, not only monorepo users, because the format and the code paths are shared.
FixThe design document had already planned for it: a four-phase rollout, with commands declared sparse-index clean one at a time. Git 2.35's notes announce fetch and pull as newly clean.
Design ruleWhen you push a performance change into a shared substrate, your rollout risk becomes everybody's. The mitigating pattern is the one Git used: an explicit allow-list of call sites that have been audited, and a silent fallback to the slow path everywhere else.
Defect thread

The native compiler inherits a different memory problem

AssumptionLeaving the JavaScript runtime leaves its memory behaviour behind.
What happenedLarge-monorepo users filed memory consumption reports against the native compiler's editor path, including issue 2780 on a large pnpm monorepo. The port swapped one garbage collector for another, with different tuning and a different working-set shape.
Blast radiusEditor sessions in large repositories. The issue is closed, but it is one of several in the same area during the preview period.
FixHandled within the port. The structural point is that the port's status table kept the programmatic API marked "not ready" long after type checking was marked done.
Design ruleA rewrite moves your resource profile, it does not shrink it. Budget a preview period long enough for someone else's largest repository to find the new shape.

Class D: the reimplementation that never ends

Retirement notice

Twenty-three years, and the exit was a handover

AssumptionA reimplementation of somebody else's runtime can be retired once you have your own.
What happenedMono reimplemented .NET, and Microsoft inherited it with Xamarin in 2016. The last major release was July 2019; patch releases continued until February 2024. Microsoft moved the workloads it cared about to a fork inside dotnet/runtime, which now covers mobile, browser and WASI targets.
Blast radiusFive years of patch-only maintenance on a codebase whose replacement already existed.
FixStewardship was handed to the WineHQ organisation, with binaries promised for up to four years. The compatibility layer's final owner is another compatibility project.
Design ruleA reimplementation has no natural end of life, because its users are defined by the behaviour you copied rather than by the product you sell. Plan the handover or the archive at the start; it will not arrive on its own.
Source

The fork that outlived its reason

AssumptionOnce the upstream project adopts your feature, your fork can be retired.
What happenedScalar entered core Git in 2.38 and its diagnose command was folded into git bugreport. The fork remained, because Azure Repos speaks the GVFS protocol and core Git will not. Releases continue against upstream 2.55, including security merges.
Blast radiusA permanent merge-and-release obligation, with every upstream security fix arriving through a rebase.
FixNone available to the client team. The exit condition sits on the server side, in whether the hosting service adopts the standard.
Design ruleRetiring a client-side compatibility layer requires a server-side migration you may not control. Sequence it the other way round: move the servers to the standard first, and the fork retires itself.
05

Numbers you can plan against

Everything quantitative in the corpus, with its context and its date. The gaps are listed too, because they are where the risk is.

MetricValueWhereContextAs ofKindSource
Repository shape that motivates partial clone3.5M files, 500K dirsGit projectFiles in every commit; the example given for narrow cone checkoutscurrent docReporteddesign notes
Full clone cost at that shapehours to days, 100+GiBGit projectBefore partial clone, stated as the motivating problemcurrent docReporteddesign notes
Index cost model after sparse indexO(HEAD) to O(Populated)Git projectComplexity of status and add in a sparse checkoutcurrent docReporteddesign document
WSL 2 speedup, Linux-side filesystemup to 20×MicrosoftUnpacking a zipped tarball, initial versions2024-11-19Vendor claimWSL docs
WSL 2 speedup, developer commands2 to 5×Microsoftgit clone, npm install, cmake on various projects2024-11-19Vendor claimWSL docs
WSL 2 write throughput to a Windows drive40.4 MB/sReporter1 GB write test on a mounted drive2019-06-19Measured, single reporterissue 4197
WSL 1 write throughput, same drive442 MB/sReporterComparison run in the same thread2019-06-19Measured, single reporterissue 4197
Guest memory retained by the VM7 of 16 GBReporterPage cache not returned until shutdown2019-06-17Measured, single reporterissue 4166
Age of both WSL boundary defects7 yearsDerivedJune 2019 to this guide's research date, both still open2026-09-17Derived4166
Sparse-index defects in one release3Git projectCorruption, path validation, silent index expansionGit 2.34Reportedrelease notes
GVFS protocol surface4 operations, 3 endpointsMicrosoftThe private transport the shim requiredprotocol v1Reportedprotocol
Attention the retired shim still holds6.1k starsGitHubVFS for Git, not archived, superseded by recommendation2026-09-17Measuredrepository
Mono patch-only periodJul 2019 to Feb 2024MicrosoftLast major release to last patch release before handovernotice currentReportedREADME notice
WSL source publication2025-05-15MicrosoftFirst open source commit in the repository2025-05-15Reportedcommit history
Read these carefully

The two WSL speedup figures are Microsoft's own, undated in origin and carried forward in a document last revised in November 2024; treat them as claims about initial versions, not as a current benchmark. The three throughput and memory figures come from single reporters on single machines, which is exactly the evidence quality you would have about your own users, and the reason they are worth reading is that they were never contradicted in seven years. Four numbers an architect would want do not exist anywhere in this corpus: how many engineers used VFS for Git, what its cache-server tier cost, how long a hydration stall lasted at the ninety-ninth percentile, and the headline speed multiple for the native TypeScript compiler, which lives in an announcement post this session could not reach.

06

The evidence wall

Every source behind this page, graded. There are no blog posts, talks or papers in it, because the network policy for this research reached only one host; that absence is itself worth knowing when you judge the claims.

Source Microsoftchecked 2026-09

microsoft/git, "Why is this fork needed?"

The fork's own statement of purpose, and the clearest sentence anywhere about why the private protocol could not go upstream: partial clone is "the official version of that functionality".

Carry forwardA private protocol is a permanent fork condition until its servers migrate.
github.com/microsoft/git
Decision record Microsoftchecked 2026-09

The Philosophy of Scalar

A design document that reads as policy: implement features in Git, and keep the local tool to configuration. It also names the single exception, the GVFS protocol, and says it is never intended to reach the standard client.

Carry forwardWrite down which part of your layer is permanent; everything else is a migration plan.
contrib/scalar/docs/philosophy.md
Source Microsoftchecked 2026-09

Scalar: Enabling Git at Scale

The replacement, itemised: partial clone, background prefetch, sparse-checkout, filesystem monitor, commit-graph, multi-pack-index, incremental repack. Every item is an upstream Git feature rather than a Microsoft component.

Carry forwardA good escape plan reads as a configuration list, not a product.
contrib/scalar/docs/index.md
Source Microsoftchecked 2026-09

VFS for Git README

The retirement notice, written as a recommendation rather than a deprecation: new deployments should consider Scalar, which combines "the lessons from operating VFS for Git at scale with new developments in Git". The repository still carries 6.1k stars and is not archived.

Carry forwardSuperseded is not the same as archived; read the README, not the repository state.
github.com/microsoft/VFSForGit
Decision record Microsoftchecked 2026-09

The GVFS Protocol (v1)

Four operations on three endpoints, including a prefetch stream and a sizes call that exists so the projected filesystem can answer stat without fetching content. The side channel that made the emulation viable, specified as a public interface.

Carry forwardEvery efficient shim invents a protocol; count that as part of the build.
VFSForGit/Protocol.md
Decision record Git projectchecked 2026-09

Partial Clone Design Notes

The upstream answer to the same problem, with the motivating scale stated plainly: 3.5M files per commit, clones taking hours or days and 100+GiB. Introduces the promisor remote as the thing that replaces on-demand hydration.

Carry forwardWhen upstream adopts your problem, its vocabulary becomes the one your team must use.
Documentation/technical/partial-clone.adoc
Decision record Git projectchecked 2026-09

Git Sparse-Index Design Document

States the cost model change, from O(HEAD) to O(Populated), and admits in advance that sparse directory entries "violate expectations about the index format". The four-phase plan is the rollout mechanism for a change that cannot be feature-flagged away.

Carry forwardPhase a format change by auditing call sites, with a silent fallback to the slow path.
Documentation/technical/sparse-index.adoc
Postmortem Git projectGit 2.34

Git 2.34 release notes, sparse-index fixes

Three defects in one release: index corruption from uninitialised data, broken rejection of paths with trailing slashes, and index_name_pos() silently expanding the sparse index and breaking cache-tree walks.

Carry forwardSilent fallback to the slow path is a correctness risk as well as a performance one.
RelNotes/2.34.0.adoc
Postmortem Git projectGit 2.35

Git 2.35 release notes, pattern matching fix

The sparse feature had a bug in the code deciding which path is inside the checkout cone. The same release declares fetch and pull sparse-index clean, which is the audit advancing one command at a time.

Carry forwardTrack "audited for the new format" per call site, and publish that list.
RelNotes/2.35.0.adoc
Source Git projectGit 2.38

Git 2.38 release notes, scalar in core

"The 'scalar' addition from Microsoft is now part of the core Git installation", and the diagnose command is lifted out of Scalar into git bugreport. The end state of a five-year upstreaming effort, recorded in two lines.

Carry forwardSuccess looks like your tool disappearing into somebody else's product.
RelNotes/2.38.0.adoc
Source Git projectGit 2.31

Git 2.31 release notes, maintenance and fsmonitor

Background maintenance, one of Scalar's original jobs, becomes a Git command with a scheduler; fsmonitor integration begins. The migration of a private tool's features into the public one, visible release by release.

Carry forwardUpstream adoption arrives in pieces; track it as a burn-down of your own features.
RelNotes/2.31.0.adoc
Decision record Git / GitGitGadget2025-11

Pull request 2010, "Audit and document Scalar config"

Derrick Stolee audits Scalar's settings for upstream, finding "some stale or incorrect config options" along the way, and is reviewed by Junio Hamano, Patrick Steinhardt and Johannes Schindelin before the series is taken.

Carry forwardAfter you move upstream, your defaults become a public argument on somebody else's calendar.
gitgitgadget/git pull 2010
Source Git / GitGitGadget2026-06

Pull request 2101, path-walk and filter integration

Monorepo-scaling work still in flight in 2026, from the same authors. Note the methodological trap: GitGitGadget closes a pull request when the series is integrated by way of the mailing list, so a closed and unmerged state is not a rejection.

Carry forwardRead a project's real review venue before inferring anything from GitHub's merge state.
gitgitgadget/git pull 2101
Vendor doc Microsoft2024-11-19

Comparing WSL Versions

The clearest statement of the thesis, by the vendor: WSL 1 "used a translation layer that was built by the WSL team", WSL 2 ships a kernel, and kernel updates are "immediately ready for use" without waiting on Microsoft. The same page marks cross-OS filesystem performance as a WSL 1 advantage.

Carry forwardThe argument for hosting the real thing is cadence, not throughput.
MicrosoftDocs/WSL compare-versions.md
Defect thread Microsoft / community2019-06-19

WSL issue 4197, filesystem performance in /mnt

40.4 MB/s against 442 MB/s on the same drive under the old shim, reported the month WSL 2 previewed, still open seven years later. The canonical example of a cost moving to the new boundary rather than disappearing.

Carry forwardBenchmark the boundary crossing, not the engine, when you virtualise.
microsoft/WSL issue 4197
Defect thread Microsoft / community2019-06-17

WSL issue 4166, memory not returned to the host

The guest's page cache stays in the guest until shutdown; a reporter measured 7 GB of 16 GB held. Microsoft's documentation still points at this issue as the tracking item five years later.

Carry forwardSize the host for the sum of two memory managers that cannot see each other.
microsoft/WSL issue 4166
Defect thread Microsoft2018-10-03

VFS for Git issue 328, sporadic vnode I/O errors

Failures in the kernel extension's vnode and file-operation handlers, with failed assertions and panics in close events, and a maintainer goal of failing "gracefully in the kext instead of crashing the system". Still open.

Carry forwardA kernel-resident shim's blast radius is the machine, not the feature.
microsoft/VFSForGit issue 328
Defect thread Microsoft2019-03-13

VFS for Git issue 354, kernel extension collision

The projection kernel extension colliding with an endpoint protection product. The compatibility surface is not just the emulated system; it is every other driver your users install.

Carry forwardInventory the other kernel-resident software in your users' estate before choosing the kernel.
microsoft/VFSForGit issue 354
Defect thread Microsoft2018-10-26

VFS for Git issue 340, kernel panic on a FUSE mount

Touching an unrelated FUSE mount panics the machine while the projection driver is loaded. Filed within a year of the macOS effort starting.

Carry forwardTwo filesystem extensions on one machine is an integration you must test, not an edge case.
microsoft/VFSForGit issue 340
Decision record Microsoft2025-03-05

typescript-go discussion 411, "Why Go?"

Ryan Cavanaugh frames the work as a port rather than a rewrite, and picks the language for structural resemblance, memory layout control, garbage collection and concurrency. The stated weakness, in-process JavaScript interop, is the one the ecosystem later felt.

Carry forwardChoosing a language for resemblance is how you keep a port from becoming a rewrite.
typescript-go discussion 411
Source Microsoftchecked 2026-09

typescript-go CHANGES.md

The compatibility ledger: Closure header files and most Closure features removed, constructor-function expando declarations no longer supported, JSDoc tag behaviour narrowed, all justified as making the implementation "simpler and more like TypeScript".

Carry forwardName the behaviour you will stop supporting, in a file, before you start the port.
typescript-go/CHANGES.md
Source Microsoftarchived 2026-09

typescript-go README and status table

The staging repository declares the port complete and schedules its own archive. The status table is the interesting part: type checking, emit and build are done, while the programmatic API is "not ready", which is precisely the in-process interop the language choice traded away.

Carry forwardThe last thing to land in a port is whatever your ecosystem links against in process.
github.com/microsoft/typescript-go
Defect thread Microsoft / communitypreview period

typescript-go issue 2780, memory in a large monorepo

Memory consumption reported against the native compiler's editor path in a large pnpm monorepo. Evidence that the resource profile moved with the port rather than shrinking.

Carry forwardRun the preview against somebody else's largest repository, not your own.
typescript-go issue 2780
Case study Microsoft / WineHQchecked 2026-09

mono/mono retirement notice

Stewardship from the 2016 Xamarin acquisition, a last major release in July 2019, patch releases until February 2024, then handover to WineHQ while Microsoft's own fork inside dotnet/runtime takes the workloads.

Carry forwardA reimplementation ends by being given away, archived, or carried forever. Pick one deliberately.
github.com/mono/mono
Source Microsoftchecked 2026-09

dotnet/runtime, src/mono

What survived: the Mono runtime as the implementation for mobile, browser WebAssembly and WASI workloads inside the main .NET repository. The reimplementation became a component of the thing it once imitated.

Carry forwardAbsorption is a legitimate exit for a compatibility layer, if you own both sides.
dotnet/runtime src/mono
Source Microsoft2025-05-15

microsoft/WSL, first open source commit

"Initial open source commit for WSL", on the developer documentation file, dated 2025-05-15. The contributing guide now solicits changes to the product's source, and the build instructions are in the repository.

Carry forwardCommit history dates a programme when the announcement is behind a host you cannot reach.
microsoft/WSL commit history
Source Microsoftchecked 2026-09

microsoft/WSL contributing guide and developer docs

Build prerequisites, a configured developer environment, and an invitation to contribute features and bug fixes to the subsystem itself. The emulation-era product is now a codebase outsiders can compile.

Carry forwardOpening the source is the cheapest way to stop being the bottleneck for the tail.
microsoft/WSL CONTRIBUTING.md
Source Microsoftchecked 2026-09

microsoft/WSL2-Linux-Kernel

The kernel tree shipped inside WSL 2, maintained in public and built from the stable branch. The "real thing" in this case is a fork of somebody else's project that Microsoft tracks rather than reimplements.

Carry forwardTracking upstream is work too; it is just bounded work.
microsoft/WSL2-Linux-Kernel
Source Microsoftchecked 2026-09

microsoft/git releases

Still cutting VFS-suffixed releases against upstream Git 2.55, including a merge that addresses a 2026 CVE, and still shipping GVFS-specific configuration. The fork is a standing obligation, not a historical artefact.

Carry forwardEvery fork you keep is a security-merge pipeline you have to staff.
microsoft/git releases
Source Microsoft2017-02-06

VFS for Git issue 13, operating system requirement

The oldest dated artefact in this corpus for the Git programme: a user objecting in February 2017 to the Windows 10 requirement. It fixes the start of the decade and it shows the platform coupling from week one.

Carry forwardThe earliest issues in a tracker are the cheapest way to date a programme.
microsoft/VFSForGit issue 13
Source Microsoftchecked 2026-09

TypeScript releases

TypeScript 7.0.2 is the current release at the date of this guide, which is the native compiler shipping under the product's own version number. The announcement text it links to is on a host this research could not reach.

Carry forwardA release list is a dateable fact even when the release notes are not reachable.
microsoft/TypeScript releases
07

Build a shim, then measure its tail

Six rungs. The first three are an evening each and teach you what the shim costs; the last three are what it takes to know whether you can ever leave.

Project a directory you do not own

Write a user-space filesystem over a remote or synthetic source, so that ls and cat work but content is fetched on access. Keep it to a few hundred lines.

Done when: an unmodified tool traverses the tree without knowing.  Teaches: why interception is seductive, and how little code buys how much compatibility.

Count the interface you actually promised

Log every operation the kernel or runtime sends your layer during one real workload. Sort by frequency, then by whether you implement it faithfully.

Done when: you can state the ratio of calls you emulate correctly to calls you approximate.  Teaches: the tail is measurable on day one, and almost nobody measures it.

Write the compatibility ledger

Produce the file that lists what you will never implement, in the style of the Go port's changes document. Circulate it to the people who will depend on the layer.

Done when: someone objects to an entry, and you either implement it or record the objection.  Teaches: the difference between an abstraction and an open-ended promise.

Stand the real thing up beside it

Run the workload against the real system directly, behind a flag, with no emulation. Measure both, including the new boundary you just created.

Done when: you have per-operation costs for both paths on the same workload.  Teaches: that the replacement wins on some axes and loses on one, and which one.

Land one change upstream

Take the smallest behaviour your layer provides that the real project lacks, and propose it there. Track the calendar time from first message to released version.

Done when: you can put a number of months on "we could upstream this instead".  Teaches: the exchange rate between your maintenance horizon and somebody else's release cadence, which is the whole decision.

Run both paths for a quarter, then retire one on evidence

Keep the shim and the direct path in production behind a per-user switch, with the boundary metrics on one dashboard, and a documented rule for which workload gets which.

Done when: the retirement decision cites the dashboard rather than a preference.  Teaches: that the honest end state is often both, scoped by workload, exactly as Microsoft's own documentation ended up recommending.

The lesson to carry into your own design

A compatibility layer is cheap where the workload is common and unbounded where it is rare, because the rare part is defined by somebody else's system and changes on their schedule. So the question to answer before you build one is not "can we emulate this", which is nearly always yes, but "can we write down the part we will never emulate, and will our users accept that list". If you can write the list, you have an abstraction with an exit. If you cannot, you have taken on a permanent obligation to track another organisation's roadmap, and the three programmes traced here all show how that ends: you eventually host the real thing, you pay a new and smaller cost at a narrower boundary, and you keep the old layer alive for the one workload it still wins.

08

Keep hunting

What actually worked, given a corpus of one host. These are repository queries rather than search-engine queries, which is the technique this guide was forced into and would now use anyway.

Dating a programme from artefacts

  • github.com/<org>/<repo>/commits/<branch>/<path>
  • raw.githubusercontent.com/<org>/<repo>/HEAD/README.md
  • repo:<org>/<repo> is:issue sort:created-asc

Finding the decision, not the feature

  • path:Documentation/technical <mechanism>
  • path:docs/adr OR path:**/DESIGN.md <mechanism>
  • repo:<org>/<repo> "philosophy" OR "alternatives considered"
  • repo:<org>/<repo> is:discussion "why"

Release notes as an incident archive

  • raw.githubusercontent.com/git/git/master/Documentation/RelNotes/<version>.adoc
  • <feature> "has been corrected" OR "broke" in:file path:RelNotes
  • repo:<org>/<repo> is:pr "regression" sort:created-desc

Finding the tail that was never implemented

  • repo:<org>/<repo> is:issue sort:reactions-+1-desc is:open
  • repo:<org>/<repo> is:issue label:"needs-author-feedback" slower OR "not supported"
  • CHANGES.md OR BREAKING.md OR "intentional changes" in:path

One caution learned the hard way in this research: a pull request shown as closed and unmerged on GitHub may have been integrated through a mailing list, which is how the Git project works. Check where the project actually reviews code before you read a merge state as a verdict.

09

References

  1. microsoft/git, "Why is this fork needed?" Microsoft. Checked 2026-09-17.
  2. The Philosophy of Scalar Microsoft, in microsoft/git. Checked 2026-09-17.
  3. Scalar: Enabling Git at Scale Microsoft, in microsoft/git. Checked 2026-09-17.
  4. microsoft/git releases Microsoft. Checked 2026-09-17.
  5. VFS for Git Microsoft. Checked 2026-09-17.
  6. The GVFS Protocol (v1) Microsoft. Checked 2026-09-17.
  7. VFS for Git issue 13 Filed 2017-02-06. Checked 2026-09-17.
  8. VFS for Git issue 328, sporadic vnode I/O errors Filed 2018-10-03, open. Checked 2026-09-17.
  9. VFS for Git issue 340, kernel panic on a FUSE mount Filed 2018-10-26. Checked 2026-09-17.
  10. VFS for Git issue 354, kernel extension collision Filed 2019-03-13. Checked 2026-09-17.
  11. Partial Clone Design Notes Git project. Checked 2026-09-17.
  12. Git Sparse-Index Design Document Git project. Checked 2026-09-17.
  13. Git 2.31 release notes Git project. Checked 2026-09-17.
  14. Git 2.34 release notes Git project. Checked 2026-09-17.
  15. Git 2.35 release notes Git project. Checked 2026-09-17.
  16. Git 2.38 release notes Git project. Checked 2026-09-17.
  17. Pull request 2010, "Audit and document Scalar config" GitGitGadget, opened 2025-11-26. Checked 2026-09-17.
  18. Pull request 2101, path-walk and filter integration GitGitGadget, closed 2026-06-02. Checked 2026-09-17.
  19. Comparing WSL Versions Microsoft, document date 2024-11-19. Checked 2026-09-17.
  20. WSL issue 4197, filesystem performance in /mnt Filed 2019-06-19, open. Checked 2026-09-17.
  21. WSL issue 4166, memory not returned to the host Filed 2019-06-17, open. Checked 2026-09-17.
  22. microsoft/WSL, developer documentation commit history First open source commit 2025-05-15. Checked 2026-09-17.
  23. microsoft/WSL contributing guide Microsoft. Checked 2026-09-17.
  24. microsoft/WSL2-Linux-Kernel Microsoft. Checked 2026-09-17.
  25. typescript-go discussion 411, "Why Go?" Ryan Cavanaugh, 2025-03-05. Checked 2026-09-17.
  26. typescript-go CHANGES.md Microsoft. Checked 2026-09-17.
  27. typescript-go README and status table Microsoft, archived September 2026. Checked 2026-09-17.
  28. typescript-go issue 2780, memory in a large monorepo Microsoft. Checked 2026-09-17.
  29. TypeScript releases Microsoft. Checked 2026-09-17.
  30. mono/mono retirement notice Microsoft and WineHQ. Checked 2026-09-17.
  31. dotnet/runtime, src/mono Microsoft. Checked 2026-09-17.