-
-
Notifications
You must be signed in to change notification settings - Fork 2
API Versioning
This document provides a comprehensive overview of our API versioning strategy, designed to ensure backward compatibility and simplify the versioning process. Our API, built on an RSocket-based backend with routed, type-safe requests and models, is complemented by an SDK that implements communication between client-server with simplified migration steps if needed by abstracting our. This strategy enables smooth evolution and enhances developer experience.
-
Ensure Backward Compatibility: To preserve existing functionality for clients using previous API versions, preventing disruptions during updates.
-
Implement Simple and Straightforward Versioning: To make versioning intuitive, allowing developers to grasp changes effortlessly.
Our versioning strategy operates at the granular level of individual requests and entities. This approach allows for precise control over changes and ensures flexibility.
- Removing or Deprecating Fields in Requests/Entities: Any modification that renders existing fields obsolete or deprecated.
- Adding New Optional Fields to Requests: Clients can choose whether to utilize new fields, ensuring backward compatibility.
- Adding New Fields to Entities (Response Data): New fields in response entities do not break existing contracts as clients can choose to ignore them if not needed.
We commit to supporting existing methods until they are no longer in use by the majority of active clients. This ensures a smooth transition for clients during updates.
To maintain a consistent user experience and ensure that clients are using compatible API versions, we employ the following mechanism:
-
Upon App Entry: Clients are required to send their API version (in semantic versioning format) via a designated request. The server responds with one of the following statuses:
- Client Uses Latest Version: No action needed.
- Client Has Updates Available: Clients are encouraged to update for the latest features.
- Client Is Required to Update (Outdated): Essential updates are necessary for continued service.
@Serializable
sealed class CreateTimerRequest<R : CreateTimerRequest.Result> : RSocketRequest<R> {
@SerialName("v1")
data class V1(
val name: String,
val description: String = "",
val settings: SerializableTimerSettings.V1? = null
) : CreateTimerRequest<Result.V1>()
@Serializable
sealed class Result {
@SerialName("v1")
data class V1(val timerId: Long) : Result()
}
}
@Serializable
sealed class SerializableTimerSettings {
@SerialName("v1")
data class V1(
val workTime: Duration,
val restTime: Duration,
val bigRestTime: Duration,
val bigRestEnabled: Boolean,
val bigRestPer: Int,
val isEveryoneCanPause: Boolean,
val isConfirmationRequired: Boolean,
) : SerializableTimerSettings()
}
Note
If entity evolves to new version, new version of affected requests should be created.
-
AdapterService: This component acts as an intermediary, adapting incoming requests into a format understandable by the domain layer.
-
Domain: The domain layer serves as the core of the system, ensuring consistency to support all gateways. Evolution of the domain layer is carefully managed to maintain compatibility.
-
SDK -> Router -> AdapterService -> Domain: Requests flow through the SDK, router, adapter service, and then to the domain layer. At each step, versioning is ensured, and compatibility is maintained.
By adopting this versioning strategy, we strike a balance between preserving existing functionality and accommodating new features. This approach ensures a seamless experience for both developers and end-users, making the evolution of our API smooth and predictable. The detailed consideration of breaking and non-breaking changes, coupled with the enforcement mechanism for client updates, solidifies our commitment to a robust and developer-friendly API ecosystem.