Speakeasy Logo
Skip to Content

SDKs

Persistent edits: Add custom code anywhere in your generated SDKs

Thomas Rooney

Thomas Rooney

December 13, 2025 - 4 min read


Speakeasy generates production-ready SDKs and Terraform providers from OpenAPI specifications, with extensive configuration options through hooks, gen.yaml settings, and OpenAPI extensions . Today, we’re introducing persistent edits, a feature that lets you make arbitrary changes anywhere in your generated code which the Speakeasy generator will automatically maintain across regenerations.

With persistent edits, you can:

  1. Add utility methods, custom properties, and business logic to any generated file
  2. Modify configuration files like package.json or pyproject.toml directly
  3. Extend your SDK without waiting for Speakeasy to add new configuration options

The generation ownership problem

Speakeasy’s code generator has always taken an opinionated approach: the files we generate, we own. This ensures deterministic output and predictable behavior. Given the same configuration, you always get the exact same SDK.

This approach has served us well, but it comes with a constraint. When users wanted to add functionality that wasn’t supported through our configuration system, they had two options:

  1. Wait for us to add a new configuration option
  2. Work around it by maintaining changes in separate files

Neither option was ideal. Adding configuration for every use case creates complexity, and maintaining changes separately breaks the convenience of having everything in one place.

What teams really wanted was simple: make a change to the generated code, and have that change persist.

How persistent edits work

Persistent edits removes the restrictions on where you can make changes to Speakeasy-generated code. The concept is straightforward: modify any file in your generated SDK, and Speakeasy will maintain those changes as long as they don’t conflict with updates we’re making.

The conflict detection works like git merge. If you modify a line and we later regenerate code that touches the same line, you’ll see a conflict. But if your changes and ours touch different parts of the file, both changes coexist seamlessly.

Enabling persistent edits

The first time you modify a generated file and run Speakeasy, you’ll see a prompt:

Modified files detected ╭─────────────────────────────────────────────────────╮ We've detected changes to files that Speakeasy │ │ manages. Would you like to enable persistent edits │ │ to maintain these changes? │ │ │ │ Modified files: │ │ - src/sdk/payments.ts │ │ - package.json │ ╰─────────────────────────────────────────────────────╯ Enable persistent edits? (Y/n)

Choose yes, and Speakeasy adds persistentEdits: { enabled: true } to your gen.yaml. From that point forward, all your changes persist automatically.

Real-world use cases

Adding utility methods

One of the most common requests we’ve seen is adding helper methods to generated models. With persistent edits, you can add methods directly to the generated classes:

src/sdk/models/payment.ts
export class Payment { id: string; amount: number; currency: string; status: PaymentStatus; // Generated code above // Your custom method below /** * Convert payment to invoice format */ toInvoiceItem(): InvoiceItem { return { description: `Payment ${this.id}`, amount: this.amount, currency: this.currency, }; } /** * Check if payment requires customer action */ needsAction(): boolean { return ( this.status === PaymentStatus.RequiresAction || this.status === PaymentStatus.RequiresConfirmation ); } }

These methods persist across regenerations. Add a new field to your OpenAPI spec, regenerate, and your custom methods remain intact.

Modifying configuration files

Want to add a dependency that Speakeasy doesn’t know about? Just edit package.json directly:

package.json
{ "name": "@acme/payments-sdk", "version": "1.0.0", "dependencies": { "axios": "^1.6.0", "zod": "^3.22.0", "aws-sdk": "^2.1.0" // Your custom dependency } }

Speakeasy maintains this change. When we update other parts of package.json (like version numbers or our dependencies), your aws-sdk entry stays.

Extending SDK initialization

Some teams need custom authentication providers or specialized configuration. Add them directly to the SDK constructor:

src/sdk/sdk.ts
import { AWSAuth } from "./custom/aws-auth"; export class PaymentsSDK { private client: HTTPClient; private awsAuth?: AWSAuth; constructor(config: SDKConfig) { this.client = new HTTPClient(config); // Your custom initialization if (config.awsAuth) { this.awsAuth = new AWSAuth(config.awsAuth); this.client.interceptors.request.use( this.awsAuth.signRequest.bind(this.awsAuth), ); } } }

Getting started

Persistent edits is available today for all Speakeasy users across SDKs and Terraform providers.

To enable persistent edits on an existing SDK:

  1. Make a change to any generated file
  2. Run speakeasy run
  3. When prompted, choose Yes to enable persistent edits
  4. Commit the updated gen.yaml with persistentEdits: { enabled: true }

For new SDKs, you can enable it immediately by adding the flag to gen.yaml:

.speakeasy/gen.yaml
configVersion: 2.0.0 generation: sdkClassName: PaymentsSDK maintainOpenAPIOrder: true persistentEdits: enabled: true

Once enabled, any modifications you make to generated files will persist across regenerations. The feature works locally and in CI/CD, with no additional setup required.

Persistent edits represents our vision for how code generation should work: deterministic and reliable, but flexible enough to meet you where you are. Generated code shouldn’t be a black box you work around. It should be a foundation you can build on.


Questions about persistent edits or need help with a specific use case? Book time with our team

Last updated on

Build with
confidence.

Ship what's next.