Building an SDK Generator: Maintaining Custom Files
We've been dealing with a persistent problem in SDK generation: when you add custom files to a generated codebase, they get deleted during the next update. Similarly, any custom code added to generated files gets overwritten in subsequent updates.
After building API client SDK generators for dozens of companies, we kept hearing the same frustration: "Your generated SDK is great, but I need to add my own wrapper class here, or a custom error handler there, and they keep getting deleted when you regenerate."
So we built something different: a code generator that treats your custom files and code as first-class citizens. Our generator also maintains edits to the actual generated files, an even greater challenge, but that solution will be detailed in a future post. This article explains our solution that leans heavily on a macro! in the Rust programming language.
You generate a Python SDK:
You add custom utilities:
The next API update comes in. You regenerate. What happens to retry_wrapper.py
and test_helpers.py
?
In most generators: Gone.
The Roach System: API-First Generation
We call it the "Roach" system because like cockroaches, your custom files survive everything.
This can't just be a CLI tool. The file resolution problem requires tracking what was generated previously to intelligently clean up obsolete files. But we can't force users to keep old API specs around. The workflow needs to be simple to fit nicely into a modern API development flow:
Upload new spec
Regenerate
All customizations maintained and new logic added
So we built our code generator as a synchronous HTTP API:
First SDK generation: POST /sdk
returns a complete tar of the SDK
Updates: POST /sdk/{id}/update
→ returns git patch with only the necessary changes to the API reflected
Smart File Resolution
Here's the tricky part. Your API removes the /legacy-reports
endpoint. When you regenerate:
legacy_report.py
should be deleted (no longer needed)retry_wrapper.py
should be kept (you added it)
But both look identical to the generator: files that exist but aren't in the current generation spec.
The solution is tracking what was generated before. We built a simple macro in Rust that defines exactly which files belong to the generated SDK structure. Not familiar with Rust macros? Learn more here.
The Roach Directory Macro
Our roach_dir!
macro creates structured definitions of what files should exist in each part of the SDK:
This macro generates structs that track every file that should be part of the generated SDK, along with their boilerplate content.
Defining SDK Structure
We use the macro to define the complete structure of a Python SDK:
The Resolution Algorithm
With this structure defined, we can resolve which files should be kept, updated, or deleted:
The core logic differentiates between generated and custom files:
How It Works in Practice
Each RoachPath
knows both its filesystem location and its expected boilerplate content. This means we can:
Track ownership: Every generated file is explicitly declared in our macro definitions
Detect changes: Compare current boilerplate against what should be generated
Preserve custom files: If a file exists but isn't in any
roach_dir!
definition, it's custom
The macro system eliminates guesswork. When your API removes the /legacy-reports
endpoint, the corresponding legacy_report.py
file simply won't appear in the new sdk_paths
list, but it will be in prev_sdk_paths
, so we know to delete it.
Meanwhile, retry_wrapper.py
appears in neither list—it exists in your Git repository but was never part of our generated structure, so it gets preserved.
Real Example
You call the update endpoint with a new API spec:
Get back a git patch:
Apply with git apply patch.diff
. Your custom files (retry_wrapper.py
, test_helpers.py
) are untouched. Only generated files that actually changed are in the patch.
Traditional generators force an ugly choice: accept generated code as-is, or maintain complex separation between generated and custom code.
Our API-first approach fixes both the technical problem and the workflow problem:
Instant feedback: Synchronous code generation, no waiting
Precise updates: Git patches show exactly what changed
Easy integration: HTTP APIs work with CI/CD and automation
Smart cleanup: Removes obsolete generated files, keeps your custom ones
Your teammates can confidently add files knowing they won't disappear. Your CI can call a simple endpoint to get updates. You get code generation without the usual constraints.
Maintaining Edits to Generated Files: Post Coming Soon
We will publish a post explaining how our unique approach to codegen unlocks maintaining edits to generated files.
Sideko Releases C# SDK Generator
Sideko's new C# SDK generator, leveraging C#'s growth (+1.43% in TIOBE), creates invisible, fully tested, strongly typed, and auto-authenticated SDKs with customizable code and complete documentation.
The Stripe API Challenge: How Sideko Generated over 1 Million Lines of SDK Code in less than 60 seconds.
Sideko's Rust-based SDK generator created massive Python (~607k lines) and TypeScript (~421k lines) Stripe SDKs in under 60 seconds on a 1GB container, saving an estimated 9 engineer-years of development time.