Skip to content
Bluetape4k docs1.11

API Maturity Annotations

Latest stable Based on Bluetape4k release 1.11.0

Kotlin visibility cannot express every compatibility boundary. A declaration may need to stay public for inlining or framework integration while still being unsafe to call, unstable to implement, or scheduled for removal. bluetape4k-annotations makes that boundary visible to the compiler with @RequiresOptIn markers.

Bluetape API marker selection map

QuestionChapter
Which marker fits this contract?Marker selection
Should @OptIn live on a function, class, or file?Opt-in scope
How do I permit calls but restrict third-party implementations?Implementation-sensitive SPI
What happens when an API stabilizes or becomes obsolete?Compatibility lifecycle
Where are complete declaration and caller examples?Practical recipes

Use a marker when a public declaration needs a contract stronger than prose. Choose BluetapeExperimentalApi for unstable use sites, BluetapeInternalApi for technically public internals, BluetapeDelicateApi for lifecycle or security-sensitive calls, BluetapeObsoleteApi for migration-only APIs, and BluetapeBetaApi for APIs expected to stabilize. Use BluetapeImplementationApi on SPI types that are safe to call but not yet safe to subclass.

Do not annotate a stable API merely to avoid compatibility work. If the restriction can be represented with Kotlin visibility, prefer visibility.

dependencies {
implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:<version>"))
implementation("io.github.bluetape4k:bluetape4k-annotations")
}

The annotations have binary retention, so downstream compilation sees them without adding runtime behavior.

Error-level markers (Experimental, Internal, Obsolete) require explicit @OptIn. Warning-level markers (Beta, Delicate) surface risk while allowing compilation. BluetapeImplementationApi uses @SubclassOptInRequired, which separates “calling an interface” from “implementing the interface.”

import io.bluetape4k.annotations.BluetapeExperimentalApi
@BluetapeExperimentalApi
fun unstablePlan(): String = "draft"
@OptIn(BluetapeExperimentalApi::class)
fun evaluateDraft(): String = unstablePlan()

Keep the opt-in scope as narrow as practical; a file-level opt-in can hide unrelated unstable calls.

TaskMarker
Publish an API that may change without compatibility guaranteesBluetapeExperimentalApi
Expose a declaration only for technical reasonsBluetapeInternalApi
Warn about lifecycle, concurrency, resource, or security obligationsBluetapeDelicateApi
Keep an API only for migrationBluetapeObsoleteApi
Signal a likely-to-stabilize APIBluetapeBetaApi
Allow calls but restrict third-party implementationsBluetapeImplementationApi

Put the marker on the declaration that owns the unstable contract. Propagation is intentional: a public wrapper around an experimental API should either opt in internally and expose a stable contract, or carry the marker itself. For SPI, annotate the base class or interface so implementations—not callers—receive the warning.

The module depends only on Kotlin annotation semantics and can be used by every other bluetape4k module. Java callers see the annotation metadata but do not receive Kotlin’s opt-in diagnostics, so Java-facing documentation must still state the restriction.

There is no runtime configuration. Build configuration consists of dependency selection and, when deliberately accepting a contract across a bounded source set, Kotlin compiler opt-in flags. Prefer source-level @OptIn because it records the exact acceptance site.

Error-level markers fail Kotlin compilation when a use site has not opted in. Warning-level markers emit diagnostics. Removing a marker annotation class can break downstream source that names it in @OptIn, even after all bluetape4k declarations stop using that marker.

The artifact adds no threads, I/O, allocation lifecycle, or runtime service. Its operational risk is compatibility governance: teams should review opt-ins during upgrades instead of suppressing them globally and forgetting the accepted risk.

BluetapeApiMarkersTest verifies retention, targets, diagnostic level, and the special subclass opt-in contract. Run:

Terminal window
./gradlew :bluetape4k-annotations:test --no-configuration-cache

No dedicated workshop is registered. The smallest useful experiment is a two-module build where one module publishes a marked declaration and the other verifies the expected compiler diagnostic and narrow @OptIn fix.

Markers communicate policy; they do not provide binary compatibility analysis or enforce contracts for Java callers. They also do not replace deprecation annotations when a replacement API and migration message exist.