Engineering Journal
Ginexys
Ginexys

Marketplace Launch Hottake

2026-05-30

VS Code Marketplace publishing is harder than it should be in 2026

TLDR

Microsoft owns VS Code, Azure DevOps, GitHub, and npm. They built an extension ecosystem that has been the dominant editor tool surface for a decade. And the publishing pipeline still requires you to learn three different name-spacing systems, generate a Personal Access Token from a separate Azure org, debug pnpm workspace symlinks in vsce package, and figure out cross-extension code sharing through undocumented runtime resolution. None of this is hard once you know it. All of it should have been auto-detected by the tooling years ago.

What every other ecosystem figured out

I publish to npm by typing npm publish. The CLI uses my logged-in npm account. Package name uniqueness is checked at the registry. License field has a default. README gets uploaded automatically.

I publish to PyPI by typing twine upload dist/*. License inferred from classifiers. README rendered from the package's own README.md.

I publish to crates.io by typing cargo publish. The Cargo.toml has every field crates.io needs. License is required and validated. No separate PAT.

I publish to RubyGems with gem push. Same story.

I publish to GitHub Packages, Cloudflare Workers, Vercel, Netlify, Render, Fly.io, Railway, AWS Lambda — and most of them figured out the entire publishing story in a single command with one auth step.

The VS Code Marketplace makes you create an Azure DevOps organization just to generate a PAT. The PAT lives in a different system than your publisher account, which lives in a different system than your extension code, which lives in a different system than the tool that uploads it. Four namespaces minimum: GitHub user, Azure org, Marketplace publisher ID, extension name. Each has its own collision rules.

What the tooling should detect

vsce should warn you if your project uses pnpm workspaces and your .vscodeignore doesn't have parent-traversal patterns. The warning could literally be: Detected pnpm-workspace.yaml. Your .vscodeignore should exclude sibling packages: ../sibling1/, ../sibling2/. Adding this check is two lines of code. The error message it would prevent has wasted aggregate weeks of engineer time across the ecosystem.

vsce should detect when your TypeScript output contains require() calls for workspace siblings and warn that this will fail post-install. The compiler already knows which modules are local. This check is one grep away.

The Marketplace should auto-suggest <publisher>-<thing> when you try to register a name field that's globally taken. It currently just says Please use a different 'name' with no guidance about what naming pattern actually works.

There should be a vsce verify command that simulates a fresh install: extract your .vsix to a temp directory, try to require the entry point, exit nonzero if anything fails. This would catch cross-extension import bugs in seconds. Right now you discover them by uploading to the Marketplace and seeing user crash reports.

What people defend about this

I have heard variations of "well, Azure DevOps is the system of record for Microsoft developer identity." Sure. Why does the Marketplace not use a Microsoft account directly? I have one. So do all the publishers I know. The Azure org is a yak shave for the actual auth.

I have heard "the global name namespace is necessary for legacy compatibility." Maybe. But then the tooling should tell you the collision at package time, not after upload. It currently lets you set name: "core" in package.json, generate a .vsix, run vsce publish, hit the rejection, then ask you to figure out what to rename it to.

I have heard "the cross-extension API pattern is documented." It's documented for people who already know about it. The path from "I want my extension to share code with another extension" to "use vscode.extensions.getExtension('publisher.name').exports" is not obvious. The natural instinct is to import like any other JS code. The natural instinct fails post-install.

What I'd build

A vsce init command that walks you through the publisher setup, scoped PAT creation (with a deep link to the right Azure DevOps page with the right scope pre-selected), .vscodeignore generation (auto-detecting pnpm/yarn/npm workspaces), README template with the recommended sections, CHANGELOG template. Five minutes from blank repo to ready-to-publish.

A vsce doctor command that runs every check the tool currently delegates to "the user noticed something was wrong": symlink leaks, cross-extension require calls, missing required package.json fields, name collisions checked against the Marketplace API, icon size compliance.

A first-class section in the docs titled "Shipping a multi-extension suite" that walks through the canonical pattern: extension pack, shared core, runtime API resolution, install order via extensionDependencies. Right now you assemble this from five different doc pages and an old GitHub issue.

What I shipped instead

I shipped my extensions. They work. They're live. New users will not see any of this pain. They will install ginexys.ginexys from the Marketplace, get four working tools, and never know how many hours of yak-shaving stood between source code and that one-click install.

That is the right outcome. The cost of getting there should not have been five hours of my life. It should have been an afternoon. The fact that it wasn't tells you something about who Microsoft is optimizing for, and it isn't first-time publishers.

The bar is not zero friction. The bar is "the friction matches the value." VS Code Marketplace publishing has world-class reach and second-tier developer experience. The reach makes it worth doing once. Doing it twice should be easier.

Read this post in the full Engineering Journal →