Developing the SDKs
Changing the API, regenerating, and keeping CI green.
Changing the API#
- Edit
fern/openapi/openapi.yml. npm run fern:checkto validate it.npm run fern:generateto regenerate every client. Needs Docker.npm testto build and test them.- Commit the contract and
packages/together.
CI regenerates from scratch and fails if the committed output differs, so step 3 is not optional.
Adding an endpoint#
- Give it an
operationId. It becomes the method name in every client. - Give it a
summary. It becomes the docstring and JSDoc comment. - Tag it, so it lands in the right subclient.
- Use
oneOfwith adiscriminatorfor polymorphic payloads, the wayTaskdoes.
Making a change safely#
Adding an optional field is a minor version. Existing requests stay valid, and clients gain the field.
Renaming a field is a major version. Add the new name, keep the old one as an alias for a
full minor release, then remove it in the next major. id to name, and tasks to results,
were done exactly this way.
Adding an endpoint is a minor version.
Tightening validation is a major version. Rejecting a request that used to succeed breaks callers who never find out until they deploy.
Keeping the docs in step#
The reference pages are written by hand, so they can drift from the contract. Two checks keep that visible:
npm run docs:buildfails ifdocs.config.jsonsays a different version than the contract.node scripts/check-docs.mjsfails on broken internal links and on pages missing a title or description.
If you add a field, update the type reference and the relevant guide in the same commit.
What CI runs#
| Job | Checks |
|---|---|
Contract |
fern check |
Generate SDKs |
regenerates, then fails on drift |
TypeScript, Python, Ruby, Rust, Java, .NET |
build and test each client |
Docs site |
builds the site, checks links |
Regenerate SDKs |
opens a pull request when packages/ is missing or stale |
Release |
the full pre-publish gate, then publishes and attaches artifacts |
Without Docker#
npm run fern:generate needs Docker because Fern runs each generator in a container. If you
cannot run it locally, push the contract change: the Regenerate SDKs workflow does it on a
runner and opens a chore: regenerate SDKs pull request containing packages/. Merge it and
the drift check passes.
Requirements for a local run#
| Tool | Needed for |
|---|---|
| Node 20+ | everything |
| Docker | fern generate --local, which is how all six generators run |
| Python 3.9+, uv | the Python client |
| Ruby 3.3+ | the Ruby client |
| Rust stable | the Rust client |
| JDK 17+, Maven | the Java client |
| .NET SDK 8 | the .NET client |
Toolchains that are not installed are skipped with a message rather than failing, so you can work on just the languages you have.