Migrating off Stainless: a week-one checklist for your generated SDKs
If your SDKs were generated by Stainless, the most useful thing you can do this week is not choosing a new generator. It is making sure that whatever you choose, you can prove your users' code still works afterwards. This post is a five-day checklist for that first week: what to inventory, what to snapshot, which credentials to find, and which test to write before anyone regenerates anything.
Some context, briefly. On 18 May 2026 Stainless announced it is joining Anthropic and winding down its hosted products, including the SDK generator, with new signups, projects and SDKs closed the same day. The SDKs you already generated are yours. What stops is regeneration.
We have written about this three times already, and this post deliberately does not repeat them:
- The Stainless SDK generator wind-down is the full account: what changed, how to evaluate the options, and a key-by-key
stainless.ymlmapping. - Stainless alternatives compares the generators you can move to, including where each one beats us.
- How to migrate from Stainless to Scalar is the step-by-step guide if you have already picked Scalar.
This one sits before all of them. Nothing on this list depends on which generator you pick, and most of it gets harder the longer you wait.
The week at a glance
| Day | Goal | Output you should have by the end of the day |
|---|---|---|
| 1 | Inventory | One table listing every SDK, package, repository and owner |
| 2 | Snapshot | A committed record of each SDK's public surface, tagged |
| 3 | Credentials and access | A list of every token, app and workflow that publishes your SDKs |
| 4 | Compatibility harness | A test suite that exercises every public method of the current SDK |
| 5 | Guardrails and communication | A CI check on API changes, a decision date, and a note for your team |
You can compress this into two days on a small API or stretch it over two weeks on a large one. The order matters more than the pace.
Day 1: Inventory what you actually ship
Most teams know roughly which SDKs they have. Fewer can list, without looking, every package name in every registry, which repository each one is built from, and who last touched it. Start there, because every later step refers back to this table.
Open your stainless.yml. Its targets block lists every language you generate. For each target, record:
| Field | Where to find it |
|---|---|
| Language and package name | targets in stainless.yml, then the registry page (npm, PyPI, Maven Central, RubyGems, Go module path) |
| Production repository | The repository your releases are published from |
| Current published version | The registry page |
| Date of the last regeneration | The most recent generated commit in the production repository |
| Files with hand-written changes | Commits in the repository that were not made by the generator |
| Owner | A named person, not a team alias |
| Known heavy users | Your largest customers or internal services that depend on the SDK |
Two rows people forget. First, targets: terraform and targets: sql: if you generated either, flag them now, because they narrow your options more than anything else. Second, docs: if you used the Stainless Docs Platform, add a row for that repository too, and note that it lives in the stainless-sdks GitHub organisation rather than yours.
The "files with hand-written changes" column deserves real time. Stainless merged custom code into generated files, so your edits are interleaved with generated code rather than sitting in a separate folder. Run git log on each production repository, filter out the generator's commits, and list every file a human changed. You will need this list on day 4 and again on the day you regenerate.
Day 2: Snapshot the public surface
Your public surface is every class, method, parameter and type your users can import. It is the thing a migration must not break, and today is the last day it is guaranteed to match what is installed in the wild. Record it now, in a form a machine can compare later.
Start with what you already have. Stainless SDK repositories include an api.md that lists every method grouped by resource. Copy it somewhere safe and tag the commit. That is your human-readable baseline.
Then add a machine-readable one per language. These are the tools we would reach for; any equivalent works.
# API Extractor writes a report of every exported declaration.
# Run init once, point it at your built .d.ts entry point, then run.
npx @microsoft/api-extractor init
npx @microsoft/api-extractor run --local
git add etc/*.api.md
git tag sdk-surface-baseline
# Griffe dumps the public API and can check a later version against it.
pip install griffe
griffe dump your_package > api-baseline.json
git tag sdk-surface-baseline
# Later: griffe check your_package --against sdk-surface-baseline
# apidiff compares the exported API of two versions of a package.
go install golang.org/x/exp/cmd/apidiff@latest
apidiff -w baseline.export github.com/your-org/your-sdk-go
git tag sdk-surface-baseline
For Java, Kotlin, Ruby and the rest, the principle is the same: produce a file that lists every public symbol, commit it, and tag the commit. If there is no tool you trust, a script that imports the package and walks its exports is fine.
Also capture the things a signature report misses:
- The client class name and constructor options.
- The environment variables the client reads for credentials and base URL.
- Default timeouts, retry counts and which status codes trigger a retry.
- How pagination is exposed: auto-iterating lists, page objects, cursors.
- Error class names and their hierarchy.
These are behaviours users depend on without ever writing them down. Write them down now.
Day 3: Find every credential, app and workflow
Today is about control. When you move, you need to be able to publish new versions of the same packages from a pipeline you own. Find out now whether you can.
Work through the inventory table and, for every package, answer:
- Who owns the package in the registry? On npm,
npm owner ls <package>lists maintainers. On PyPI, check the project's collaborators and any trusted publishers. On Maven Central and RubyGems, check the namespace and gem owners. If the only owner is a person who has left, fix that this week. - What publishes it today? Read every workflow in
.github/workflowsin the production repository. Note each secret it references and where the secret is stored. - Which GitHub Apps can write to the repository? Check the organisation's installed apps and note what the Stainless app can access. Our migration guide covers removing it; for now, the goal is to know its scope and to make sure nothing breaks when it goes.
- Is anything pointing back at Stainless infrastructure? Search the repositories for
stainlessin workflow files, scripts and READMEs.
The output is a short list: package, owner, publishing workflow, secret names, and any access that depends on Stainless. If a row has a gap, that is your first real task, and it is much easier to close while nothing is on fire.
Day 4: Build a compatibility harness
The snapshot from day 2 tells you if a signature changed. It does not tell you whether a user's code still runs. For that, you want a small test suite that uses the current SDK the way your users do, and that you can later point at any regenerated SDK unchanged.
Keep it simple:
- One call per public method. Walk
api.mdand write a test that calls each method with realistic arguments. Assert on the shape of the result, not on exact values. - Run it against a mock server. Your OpenAPI document already describes every response, so a mock server can answer these calls without touching production. The Scalar mock server is one option; any OpenAPI mock server works.
- Cover the behaviours from day 2. A test that pages through a list, a test that triggers a retry on a
429, a test that catches the typed error on a404, and a test that constructs the client from environment variables alone. - Compile your README snippets. Copy every code sample from your README and docs into the test suite. They are the code your users copied first, so they are the most likely to break.
Run the suite against the current published version and commit it green. When you evaluate a generator, you install its output in place of the current SDK and run the same suite. A failure is a breaking change you found before your users did.
This is also where the day-1 list of hand-edited files pays off. Every hand-written helper should have at least one test in the harness, because it is the code most likely to be lost in regeneration.
Day 5: Put guardrails on the API and tell your team
Nothing breaks until your API changes. So the last step is to make sure the next API change cannot ship quietly while the SDKs are frozen.
Add a CI check on the OpenAPI document. Diff the document on every pull request against the one your SDKs were last generated from. Any change to a path, parameter, or schema should fail the check, or at least require an explicit approval that says "the SDK will not reflect this yet". This turns an invisible gap into a visible decision.
Set a decision date. The real deadline is not Stainless's; it is your next API change that users need in the SDK. Put a date on the calendar that is comfortably before it.
Write down how you will evaluate options. The wind-down page has the long version. The short version is one question for every vendor: how does my existing public surface survive? You now have the tools to check the answer yourself: the snapshot from day 2 and the harness from day 4.
Tell your team. A short internal note is enough:
Our SDKs were generated by Stainless, which has stopped accepting new work. Published SDKs keep working. Until we move, SDK changes are frozen and API changes that affect the SDK need sign-off from [owner]. We have a surface snapshot and a compatibility test suite, and we will decide on a replacement by [date].
You do not need to tell customers anything yet, unless your API is about to change in a way the SDK must reflect. When you do migrate, the goal is that there is nothing to tell them: same package names, same imports, same method names, a normal minor version bump.
What not to do in week one
- Do not regenerate from the OpenAPI document alone into your main branch. Any generator will produce a working SDK with a different public surface. Try it on a branch if you like, then run the day-4 harness and read the failures. It is an instructive afternoon.
- Do not rename packages. A new package name forces every user to change their install and their imports.
- Do not delete
stainless.yml. It holds your resource tree, method names and pagination rules, and some generators, including ours, read it directly. - Do not remove the Stainless GitHub App before you can publish another way. Remove it once the new pipeline has shipped a release.
- Do not let the freeze become permanent. Every week of API changes you defer is another week of drift to reconcile later.
Where to go next
If you have finished the checklist, you are in a better position than most teams to evaluate options, because you can test the claims. Start with the wind-down write-up for the evaluation criteria, then Stainless alternatives for the field. If you choose Scalar, the migration guide picks up exactly where this list ends: import your OpenAPI document and stainless.yml, then diff the generated api.md against the snapshot you took on day 2.
Frequently asked questions
Do my Stainless-generated SDKs stop working?
No. Published packages keep working and the code is yours. What stops is regenerating them when your API changes.
What should I do first when migrating off Stainless?
Inventory every SDK you ship and snapshot its public surface. Both are independent of which generator you choose, and both are easier to do before anything changes.
How do I check that a new generator did not break my SDK?
Compare the new SDK's public surface with your snapshot, then run a compatibility test suite written against the current SDK. Signature diffs catch renamed methods; the tests catch changed behaviour such as pagination and retries.
Can I keep the same package names after migrating?
Yes. The packages and registries are yours. Keeping the names is the most important thing you can do for your users, and any serious migration should support it.
Is there a deadline for leaving Stainless?
Stainless's announcement does not give an end date for existing projects, and we had not seen one published when this post was written. Your practical deadline is your next API change that your SDK users need.
Related
- Learn: Generate an SDK from OpenAPI · Build vs buy an SDK
- Docs: How to migrate from Stainless to Scalar
- Product: SDK generator — reads your
stainless.ymlso your users' call sites keep working
Stainless facts in this post come from Stainless's own announcement and documentation, as of 26 September 2026. If Stainless publishes a timeline or anything here is out of date, please open an issue and we will correct it.