Monkey Patching

Warning Icon

CAUTION

Advanced Technique: Please note that monkey patching is an advanced feature of Speakeasy that you should use with caution. If you use this feature, you will need to put in additional effort to maintain your SDK.

Monkey patching is a technique to override the code of a module or library (or, in this case, a code generator) with a bespoke patch. You might use a monkey patch to address a bug or provide a workaround until a fix can be made upstream or otherwise implement features, fixes, and customizations the upstream source is unable to handle. In the case of the Speakeasy generation code, you can choose to override any code, documentation, and examples created by the SDK generator.

The tradeoff of using a monkey patch with Speakeasy is that you will need to maintain the added customization. This can add overhead to your team and may lead to failures or inconsistencies in your SDK code, documentation, and examples.

We recommend you carefully consider the value of using the monkey patching feature before implementing it.

Recommended Use Cases

Here are some use cases where monkey patching your code will have the lowest impact. You might want to customize your:

  • README file
  • Usage snippets and example code
  • Other generated documentation

Or you might want to use monkey patching to encode business logic not defined in your API.

Be cautious when using the monkey patching feature to patch any generated code or lists of package dependencies. These changes put your monkey patches into the critical path of SDK generation and can lead to periodic SDK generation failure.

Marking Files With .genignore

To activate monkey patching, start with a generated SDK and add a file named .genignore to your project.

If you are familiar with .gitgignore, the .genignore file works the same but marks any matched files as being maintained by the API owner rather than the generator.

Each line of the .genignore file describes one matching rule. Rules are interpreted as follows:

  1. A blank line matches nothing and may be used for readability.
  2. Lines starting with a # are treated as comments. To match a file starting with a #, use a backslash to escape it, like this: \#.
  3. Trailing spaces are ignored unless followed by a \, in which case, the final \ is ignored but the spaces are preserved as part of the match.
  4. Prefixing a line with ! will create a negative match, which means that files matching that line will NOT be ignored.
  5. A line ending in a / will only match a directory (the / itself is not part of the match).
  6. Any text on a line is treated as a glob, for example, *.go matches all files ending with .go.
  7. Wildcards like * and ? in the glob will not match directory separations. So pkg/*.go will match any file ending .go directly inside a directory named pkg, but not in subdirectories of pkg.
  8. The ** will match multiple levels of directories. For example, pkg/**/*.go will match any file ending in .go in foo or in any subdirectory of pkg. Also, a trailing ** matches everything under that point.
  9. A / at the start of a line will match starting at the current directory. For example, /test/*.go will match files ending in .go inside the test directory within the directory containing .genignore, but will not match a file named pkg/test/*.go from the same folder.

Save the file in place, and you have marked that file as being fully maintained by you. The generator will not delete or modify that file and any changes you make to matched files will be preserved.

Files generated by Speakeasy include a comment like this at the top:


// Code generated by Speakeasy (https://www.speakeasyapi.dev). DO NOT EDIT.

Once you have set up your .genignore file, edit each file it references to include a new header similar to:


// Code originally generated by Speakeasy (https://www.speakeasyapi.dev).

Caveats

Once the SDK generator picks up the .genignore file, it will no longer delete, overwrite, or modify the file. As long as a file is marked by a .genignore rule, you are solely responsible for maintaining that file.

This could result in the following problems:

  • Duplicated Code. If changes to generation result in some names being changed, it is possible that you will see duplicate symbol names between your patches and the generator.
  • Missing Code. Some of the internals of the generator are not explicitly part of the exported interface. If we change the internals, symbols might go missing because they have been replaced with different tools or renamed.
  • Dead Code. It is possible that things change over time such that your monkey patches are no longer referenced by the generated SDK. In such cases, although your patch ships with your code, it might not be an effective part of anything.

Each generation event has the potential to create these or other maintenance issues related to monkey patches that you need to watch out for. For this reason, we recommend you implement monkey patches with caution.

Adding Dependencies to your SDK

Speakeasy supports adding additional dependencies to your SDK through the additionalDependencies config in your gen.yaml file. This is a safer way to add dependencies to your SDK than monkey patching. See the gen.yaml reference for more details.

Catching Up

If you run into a problem due to monkey patching, the following procedure will help you manage it. Assuming your patches are stored in your Git repository, you can:

  1. Comment out lines that cover the impacted files with a # in your .genignore file.
  2. Generate SDKs by manually triggering your GitHub action or by installing the Speakeasy CLI and running speakeasy generate sdk with the appropriate options on your code.
  3. Use git diff to compare the latest unpatched generated SDK to your monkey patch to see what has changed.
  4. Make changes to your patch code and then uncomment the previously commented out .genignore lines OR remove the commented lines from .genignore if you want to stop maintaining your patch for any reason.
  5. Commit your changes.