SDK Design · Chapter 4 · 9 min
Publishing and docs
Semantic versioning, changelogs, generated references and examples that are tested.
Semantic versioning discipline
The ub3r SDK follows strict semantic versioning to ensure that updates are predictable and safe for all users. We never introduce breaking changes in minor or patch releases, even if the change seems trivial. This commitment allows developers to use optimistic version ranges in their package managers without fear of an unexpected build failure. When a major version is released, it is accompanied by a detailed migration guide to help teams upgrade smoothly.
Maintaining multiple versions of the SDK allows us to support legacy environments while still innovating with modern language features. We provide a clear deprecation policy, giving users at least six months of notice before a major version reaches end-of-life. This transparency helps engineering teams plan their maintenance windows and keep their dependencies up to date. A well-managed versioning strategy is the foundation of a stable developer ecosystem.
Human-centric changelogs
A good changelog is more than just a list of commit hashes; it is a communication tool for the community. We group our updates into categories like Added, Fixed, and Changed to make it easy to see what impact an update will have. Each entry links back to the original pull request or issue to provide full context for the change. We also include migration snippets directly in the changelog for any significant updates.
The changelog also serves as a historical record of the SDK's evolution and the decisions made along the way. By documenting the why behind changes, we help future maintainers and curious developers understand the constraints we were working under. This level of detail builds a stronger connection between the ub3r core team and the developers who use our tools every day. Clear communication is just as important as clean code in open-source development.
// Before v2.0
client.sendSms("+123", "hi");
// After v2.0 (The changelog explains why this became a resource) client.messages.create({ to: "+123", text: "hi", channel: "sms" }); ~~~
## Living documentation
Documentation should be treated as code, with the same rigor for testing and verification as the library itself. We extract the code snippets from our documentation and run them against a live mock server in our CI pipeline. This ensures that the examples you see in the README are always accurate and functional. Nothing frustrates a developer more than a copy-paste example that fails to compile because the API has changed.
In addition to reference documentation, we invest heavily in conceptual guides that explain the patterns and principles behind the SDK. These guides help developers understand how to build resilient systems using the tools we provide. While an API reference tells you what a function does, a guide tells you when and why you should use it. We believe that empowering developers with knowledge is the best way to ensure their success with the «ub3r» platform.
:::note Automatic Generation
We use our TypeScript definitions as the single source of truth for our API reference documentation. This means that as soon as we update a type in the code, the documentation is automatically updated to reflect the change.Discussion (4)
We learnt the idempotency lesson the expensive way: duplicate OTPs during an incident. This chapter is now required reading on my team.
Same experience here. Per-route metrics were the only way we found it.
Clear and opinionated. One nit: worth mentioning clock skew between producer and consumer when choosing the tolerance window.
The callout about alerting on queue age instead of depth changed how we page people. Thank you.