Deprecations
This page is a checklist of what has to change in the code when a version bump reaches a certain version. When you deprecate something, add an entry under the version that will remove it, or under "No target version yet" if that is not decided. When you bump a version, work through its section and delete the entries you completed. It is organised by version kind, then by target version. See API_VERSIONING.md for how the versions work and API_CONSTANTS.md for the current values.
API version
The API version is ApiVersion.CURRENT (ApiVersion.kt). Features are gated by ClientFeature (ClientInfo.kt) and implemented as CompatRules (CompatRule.kt) that ClientCompat.kt applies to responses. A compat rule can only go once every client that needs it is unsupported.
When ApiVersion.LEGACY reaches 11
- The
lyricsfield ofSongandUserSongwas removed in version 11 (ClientFeature.LYRICS_SERVICE). Older clients still get it as an extra wire field. To remove it:- Remove
@LegacyWireField("lyrics", untilApiVersion = 10)from both models in Song.kt, thelyricsentry ofLegacyWireFieldsin WireFormats.kt,LegacyLyricsCompatfromCompatRules.alland LegacyLyricsCache.kt with its Koin binding. - When no rule uses
CompatRule.prefetchany more, the hook and the song id collection inResponseShapercan go as well. - Rewrite the
fallbackofLYRICS_SERVICEto say that there is no fallback.
- Remove
No target version yet
- The remaining compat rules exist only for old clients.
TitleTagsCompat(version 6),ReleaseVersionsCompat(version 7),AlbumTitleTagsCompat(version 9) andAlbumVersionsCompat(version 9) can go once clients below those versions are unsupported. The same holds for the inlineClientInfo.supportschecks ofDOLBY_ATMOS(3) andLOSSLESS_WAV_AIFF(2), which have noCompatRule. ClientFeatureentries are never removed. They are the history of what each API version introduced and they produce the feature table in API_CONSTANTS.md. When the compat code of a feature is removed, rewrite itsfallbacktext to say that there is no fallback, then regenerate the table.AUDIO_INFO(4) andFIELD_RENAMES(8) are in that state today.AUDIO_INFOhas no code reference left and exists for documentation only.FIELD_RENAMESstill gates the renames of the legacy wire formats inwireFor(WireFormats.kt). No model carries@LegacyWireNametoday and theServerWireregistry is empty, so the renames change nothing for clients below version 8. The mechanism inserver/src/main/kotlin/dev/dertyp/core/wire/stays for the next rename.- Raising
ApiVersion.LEGACY(currently 1) is what makes a rule dead. UpdateClientInfo.fromHeadersexpectations and the tests when you do.
UI schema version
The UI schema version is UiSchemaVersion.CURRENT (UiSchemaVersion.kt). Each component is registered with its introducing version in UiSchema.introducedIn and UiSchemaCompat downgrades newer ones to Fallback, for FileField to a base64 TextField and for SearchField to a query TextField, a Search button and a Select of the results (UiSchemaCompat.kt). See SERVER_DRIVEN_UI.md.
No target version yet
UiComponent.FileField(introduced in schema version 2): theasTextFielddowngrade inUiSchemaCompatandBASE64_HINTcan go once clients below schema version 2 are unsupported. When schema version 1 is dropped,UiSchemaCompatitself and theintroducedInmap can be reduced the same way.UiComponent.SearchField(introduced in schema version 3): theasSearchColumndowngrade and theui.search.<key>invoke interception inUiSchemaCompat, theCompatScopeit needs and theinterceptUiInvokehook inCompatRulecan go once clients below schema version 3 are unsupported.
Plugin API version
The plugin API version is ISynaraPlugin.apiVersion (ISynaraPlugin.kt, default 1). The server accepts plugins up to PluginManager.CURRENT_API_VERSION (currently 4) and the history is in PLUGINS.md. Nothing in plugin-api is annotated @Deprecated today.
No target version yet
- Nothing is deprecated. When you drop support for old plugins, raise the lowest accepted version in
PluginManagerand remove thePluginContextmembers only older versions needed.
Other versioned things
No target version yet
- Audio timeline format:
AudioTimelineCodec.VERSION(currently 3) is stored inSongAudioTimelineTable.versionand rows with a lower value are re-analysed byAudioAnalysisService. A format change bumps the constant. Nothing is deprecated. - Credential protocol:
CredentialProtocol.PROTOCOL_VERSION(currently 1, incommon-credentials) is reported by the credential server health endpoint asprotocolVersion. See CREDENTIAL_SERVER.md. Nothing is deprecated. ReverseProxyServicecarries two@Deprecated(level = ERROR)overrides (hostandport) of Ktor'sRequestConnectionPoint. They belong to the Ktor interface and are not Synara deprecations, so there is nothing to remove.
Conventions
- Mark deprecated API with a constant per target version:
@Deprecated(REMOVED_IN_API_N + " Use x.", ReplaceWith("x()")). Leave outReplaceWithwhen the replacement is not a drop-in call. No such constant exists at the moment. The next deprecation creates itsREMOVED_IN_API_Nconstant in Compatibility.kt, next toLegacyWireName. - A renamed method keeps its old name as a
@Deprecatedmethod with a default body in the common-rpc interface that delegates to the new name. Servers and client wrappers implement only the new name. kRPC keeps serving the old name becauseRpcDeclarationScannerregisters every declared function. - Every deprecated RPC method gets a default body in the common-rpc interface, so clients never override it. A deprecation without a rename delegates to its replacement as closely as it can, for example a one-shot flow over the matching getter. The server keeps its own override where older clients need the full behaviour until removal and marks it
@Deprecatedwith the same message, so the override itself raises no warning. - The generated REST handlers (
server/build/generated/ksp/.../*Rest.kt) call every deprecated method. Their generator writes a file-levelSuppress("DEPRECATION")into those files, so the generated code raises no deprecation warnings. Hand-written code never suppresses deprecation warnings and keeps them visible. The one exception is the two Ktorhostandportoverrides inReverseProxyService(see above), which carry@Suppress("DEPRECATION")because the interface members they override are deprecated. Suppressions of other warning kinds, such asUNCHECKED_CAST, are not covered by this rule. The generatedDefaultsobjects of the REST layer copy the@Deprecatedannotation onto their probe overrides. - Wire renames switch to the new name immediately in common-rpc. Only the server keeps the old name, for older clients, through
@LegacyWireNameon the renamed property or class. Clients never carry compatibility code and use the plain generated serializers. The server picks the legacy formats inwireFor(WireFormats.kt), today for renames belowClientFeature.FIELD_RENAMES. A rename that targets a later version adds its ownClientFeatureand extends that check. - A removed field that older clients still read is declared with
@LegacyWireField(name, untilApiVersion)on the model class in common-rpc. The server registers aLegacyFieldSourcewith the same name and version inLegacyWireFields(WireFormats.kt), and a mismatch between the two versions fails the encode. A value that needs the database is loaded in a batch throughCompatRule.prefetch, asLegacyLyricsCompatdoes. - A renamed field that is reachable through a sealed class needs that sealed class with all its subclasses in the
ServerWireregistry (WireFormats.kt).LegacyWireRegistryTestfails when one is missing. The registry is empty at the moment. The tests of the mechanism use annotated fixture classes inserver/src/test/kotlin/dev/dertyp/core/wire/fixtures/. - Parameters are never renamed, because REST derives its keys from them.
- A parameter added to an RPC method that released clients call is nullable, and null means the default. kRPC does not apply Kotlin default values for arguments an older client did not send. A missing non-null argument fails the call before the service is reached, and a missing nullable one arrives as null. The server maps null to the default in its RPC adapter.
KrpcMissingDefaultArgumentsTestpins this. - Old methods whose replacement derives the same REST route get
@RestExclude. - Every new deprecation gets an entry in this file.