Docusign SDKs: Our Story, Part III
See how the Developer Engineering team manages versioning and releases for our SDKs.
Welcome to the third post of this series where we talk about how we build, maintain and ship SDKs at Docusign. If it is the first time you stumbled across this series, make sure you read the first and second posts. It should only take you a few minutes! ⚡
In the previous post, we saw how we generate and release new SDK versions from Swagger file updates. This process typically happens at the beginning of each month. 📅
First, let’s discuss how to bump SDK versions, how to promote a pre-release to GA, and how pre-release naming conventions can be different from one programming language to another. 🚢
It is possible to browse the source code of any version of any Docusign SDK by going to the SDK’s releases page on GitHub. Each release is tagged with a unique git branch name. Docusign SDK release versions follow the Semantic Versioning (SemVer) convention when it comes to the version core part. We never use the build number part, but we do use the pre-release part to differentiate between alpha, beta, release candidate, and generally available versions.
Note: Version parts don’t have to be in single digits. So “1.23.206” is a valid SemVer version.
Note: SemVer is strict about using the dash sign as a delimiter for the pre-release part; a rule that we had to break for both Python and Ruby SDKs because we believe that widely-accepted language-specific conventions should have precedence over universal rules.
When it comes to the pre-release part, we use this table as a reference:
SDK | Lowercase? | RC starts from 1? | Delimiter | Examples |
---|---|---|---|---|
C# | yes | no | dash | 4.1.0-rc, 3.4.1-rc2, 5.0.0-beta, 4.2.0 |
Java | no | yes | dash | 2.9.0-RC1, 3.7.0-BETA, 3.5.0, 2.0.0 |
Node | yes | yes | dash | 5.4.1-rc1, 4.0.1-beta, 4.3.1, 6.0.0 |
PHP | yes | no | dash | 4.5.0-rc, 4.5.0-rc2, 6.0.0-beta, 2.3.0 |
Python | yes | yes | none | 1.0.1rc, 2.4.0rc2, 3.2.0a1, 2.1.3b0 |
Ruby | yes | yes | dot | 5.3.0.rc1, 3.2.0.beta, 1.12.1, 4.3.0 |
Note: The Python package manager requires using “a” and “b” instead of “alpha” and “beta”.
Let’s look at some use cases:
Say there is a release candidate for Java SDK that includes bug fixes only and that the latest publicly available version is 2.8.3. We need to bump the patch version according to SemVer and then add an “-RC1” suffix according to the pre-release reference table above. The target version is then 2.8.4-RC1. If any issue is discovered, we increment the RC number and push 2.8.4-RC2 before we finally release the GA version 2.8.4.
Now let’s say we have received a request to add support for a new .NET Framework version and that the latest publicly available version for our C# SDK is 4.5.0. Our engineering team adds support for the new .NET version while preserving backward compatibility, then creates an alpha version for internal teams to try out before pushing a beta version out for customers. In this case we need to bump the minor version according to SemVer and then add a “-beta” suffix according to the reference table. The target version becomes 4.6.0-beta and, if no issues are discovered, the 4.6.0 GA version is released without need for an RC.
In this last example, the API team decides to refactor a bunch of API endpoints as part of consolidating features from different APIs into a set of endpoints that provides a common interface to perform CRUD operations on a “core” Docusign Agreement Cloud object, such as Document. This will have a side effect on all the SDKs and will break SDKs developers’ integrations that already use this object. In this case, all SDKs will have to bump their major version according to SemVer.
As a general rule of thumb, SDKs are kept in an RC state for at least a week and in beta state for at least a month. This gives customers time to report any bugs or issues, in which case a fix is released in an incremental RC version. After that, the pre-release suffix is dropped as the GA version is released.
Next let’s talk about the branching strategy. 🌲 At the time of this writing, only the eSignature API requires us to support two API versions: v2 and v2.1. This means that we have to keep two master branches per SDK. The default branch, master, maps to the latest version of the API—v2.1—as it’s considered as the recommended eSignature version. This also means that we need a secondary master branch to map to v2, so we came up with v2-master.
Whenever a monthly API update becomes available, we need to update APIs and models of each SDK and merge the v2 changes into v2-master and the v2.1 changes into master.
In this case, SDK minor versions need to be bumped. Imagine, for instance, that the latest C# SDK that maps to API v2.1 is 4.4.1, and for API v2 it is 3.4.2. Then the new target versions are 4.5.0-rc and 3.5.0-rc, respectively.
For our tool to work correctly, the Github branch name might have an extra suffix right after the SDK version names: for instance, 4.5.0-rc-v2.1-20.2.00 and 3.5.0-rc-v2-20.2.00. In this case, it means that those versions of the SDK are up to date with release 20.2.00 of API v2.1 and 20.2.00 of API v2 respectively. You should not rely on the existence nor the meaning of this suffix, as it’s purely for our internal reference and it may be removed or changed at any time.
Finally, let’s discuss the art of writing release notes. 📝 Each SDK Github repo contains a CHANGELOG.md file where release notes are listed from recent to oldest. The standard that Docusign follows is called “keep a changelog” and a change log is basically the history of all notable changes, aimed to tell users and contributors—thus humans like us and not robots—what features and fixes are shipped in each release.
Release notes best practices:
It should be fun to read for humans because robots can diff commits.
It should be in Markdown format and the file should be called CHANGELOG.md.
CHANGELOG.md should be in the project’s top level.
It should be historically sorted from the newest version to the oldest.
It should allocate one section per release version.
Release candidates may not be listed and may be skipped.
The first version may be named "Unreleased" and should serve as a backlog for the next release(s).
Each section should be titled and its title should contain the full version number as well as the release date in ISO format, i.e.: x.y.z - yyyy-mm-dd.
Each section contains at least one of the following subsections:
Added for new features
Changed for changes to existing functionality
Deprecated for features planned to be removed in upcoming releases
Removed for deprecated features removed in this release
Fixed for bug fixes
Security for security hot patches that require users to upgrade immediately
BREAKING for changes that introduce breaking changes (should be specific to major releases only)
Each subsection contains a list of changes that is aimed to be communicated to users. Referencing Github issues, JIRA tickets, or any other external info provider is generally accepted and might be a nice-to-have (e.g., CVE number).
Release notes bad practices
Dumping in commit history or git diffs.
T.M.I. and unnecessary details.
Failing to emphasize deprecations and breaking changes especially or unpublished releases or other high-impact updates.
Changing previous change logs unless there is a valid reason.
Internal terminology, code names, acronyms that may be confusing.
This completes the overview of the SDKs conventions and best practices. Next time, in the closing post, I will share some future enhancements, touch on edge cases of the release process, and explain why we deliberately decided to keep some tasks manual. 👋
Additional resources
Majid Mallis joined Docusign in 2016, as an early engineer on the Developer Center team. He now leads the team’s mission to make Docusign integration seamless and fun with a new incubation program. Previously Majid worked on many projects ranging from our site, to API Explorer, to SDKs. You can find him on Github, mostly contributing to SDKs or occasionally responding to dev community questions.
Related posts