SDKs
Custom code best practices
This guide covers best practices for using custom code effectively, avoiding common pitfalls, and working smoothly in team environments.
When to use custom code
Section titled “When to use custom code”Good use cases
Section titled “Good use cases”Custom code works best for:
- Adding utility methods to models or SDK classes
- Extending initialization with custom authentication or middleware
- Modifying configuration files like package.json or pyproject.toml
- Adding business logic specific to the domain
- Performance optimizations that require deep changes
- Integration code for internal systems
When to consider alternatives
Section titled “When to consider alternatives”Consider other approaches when:
- OpenAPI can solve it: Many customizations can be handled via OpenAPI extensions
- Hooks suffice: SDK hooks might provide enough flexibility if you want to simply alter or act on the request or response. They are also useful for custom authentication setups
Avoiding conflicts
Section titled “Avoiding conflicts”Structure changes to minimize conflicts
Section titled “Structure changes to minimize conflicts”Delegate logic to separate files
Keep changes in generated files minimal to reduce merge conflicts.
// ❌ Avoid: Writing complex logic directly in generated filesexport class PaymentSDK { async createPayment(data: PaymentRequest): Promise<Payment> { // Generated code...
// 50 lines of custom validation logic mixed in... if (!data.amount || data.amount < 0) { // ...complex validation... }
// More generated code... }}
// ✅ Better: Import and call external logicimport { validatePayment } from "./custom/validator"; // Only 1 line added
export class PaymentSDK { async createPayment(data: PaymentRequest): Promise<Payment> { validatePayment(data); // Only 1 line added // Generated code continues unchanged... }}Add methods, do not modify existing ones
// ❌ Avoid: Modifying generated methodsclass User { // This method is generated getName(): string { // Changed the implementation return this.firstName + " " + this.lastName; }}
// ✅ Better: Add new methodsclass User { // Generated method untouched getName(): string { return this.name; }
// Custom method addition getFullName(): string { return this.firstName + " " + this.lastName; }}Team workflows
Section titled “Team workflows”Communicating changes
Section titled “Communicating changes”Document customizations
Create a CUSTOMIZATIONS.md file in the SDK:
# SDK Customizations
This SDK has custom code enabled. The following customizations have been added:
## Utility Methods- `Payment.toInvoiceItem()` - Converts payments to invoice format- `User.getFullName()` - Returns formatted full name
## Custom Dependencies- `aws-sdk` - For S3 upload functionality- `redis` - For caching API responses
## Modified Files- `src/models/payment.ts` - Added utility methods- `package.json` - Added custom dependencies and scriptsUse clear commit messages
# When adding customizationsgit commit -m "feat(sdk): add payment utility methods for invoice conversion"
# When resolving conflictsgit commit -m "fix(sdk): resolve generation conflicts in payment model"Troubleshooting tips
Section titled “Troubleshooting tips”Common patterns to avoid
Section titled “Common patterns to avoid”Do not remove generated headers
// ❌ Do not remove these// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.// @generated-id: a1b2c3d4e5f6
// ✅ Keep them for move detection to workDo not copy files with IDs
# ❌ Copying creates duplicate IDscp src/models/user.ts src/models/user-v2.ts
# ✅ Either move or create new filemv src/models/user.ts src/models/user-v2.ts# or create fresh without the @generated-id headerRecovery procedures
Section titled “Recovery procedures”Reset a single file
# Remove custom changes from one filegit checkout HEAD -- src/models/payment.ts
# Re-run generation using the same pristine snapshot (no new snapshot is created)speakeasy run --skip-versioningReset everything
# Disable custom code# Edit .speakeasy/gen.yaml: enabled: false
# Remove all generated filesfind . -name "*.gen.*" -delete # Adjust pattern for the SDK
# Regenerate freshspeakeasy runFix “duplicate ID” warnings
- Find files with duplicate IDs
- Remove
@generated-idline from copied files - Let next generation assign new IDs
Summary checklist
Section titled “Summary checklist”Quick reference
✓ Enable custom code before reorganizing files. Move detection is file-specific, not folder-specific ✓ Document customizations for team members ✓ Do not remove @generated-id headers ✓ Commit custom edits independently of generator changes for clarity