Skip to content
Bluetape4k docs1.11

Practical recipes

Latest stable Based on Bluetape4k release 1.11.0

Publish an experimental API and use it from one function

Section titled “Publish an experimental API and use it from one function”

The provider marks the declaration that owns the unstable contract.

@BluetapeExperimentalApi
fun planNextGenerationIndex(): IndexPlan = TODO()

The consumer opts in at the smallest accepting scope.

@OptIn(BluetapeExperimentalApi::class)
fun evaluateNewIndex(): Report =
benchmark(planNextGenerationIndex())

Keep APIs with lifecycle or serialization risks inside a small adapter. Bluetape’s Kafka codecs use BluetapeDelicateApi for this boundary.

class TrustedPayloadDecoder {
@OptIn(BluetapeDelicateApi::class)
fun decode(bytes: ByteArray): Payload {
require(bytes.isNotEmpty())
return KafkaCodecs.Jdk.decode(bytes) as Payload
}
}

The adapter is stable only if it owns input validation and allowed types. Otherwise propagate the marker.

Use an internal API from an integration bridge

Section titled “Use an internal API from an integration bridge”

Keep BluetapeInternalApi out of ordinary application code. Permit it only in a small framework bridge when a public-for-technical-reasons declaration is unavoidable.

@OptIn(BluetapeInternalApi::class)
internal fun installFrameworkBridge(registry: Registry) {
registry.register(internalAdapter())
}

Keeping the bridge itself internal prevents the unstable dependency from spreading.

@SubclassOptInRequired(BluetapeImplementationApi::class)
interface QueryEngine {
fun execute(query: Query): Result
}
fun run(engine: QueryEngine, query: Query): Result = engine.execute(query)
@OptIn(BluetapeImplementationApi::class)
class CustomQueryEngine : QueryEngine {
override fun execute(query: Query): Result = TODO()
}

run needs no opt-in. Only CustomQueryEngine accepts the implementation contract.

Use a compiler option only when the whole source set deliberately shares one experimental policy.

kotlin {
compilerOptions {
optIn.add("io.bluetape4k.annotations.BluetapeExperimentalApi")
}
}

Avoid this in ordinary production modules because new experimental calls no longer add an @OptIn line to the source diff.

  • Could visibility close the declaration instead?
  • Does the marker describe the actual unstable contract?
  • Was the diagnostic level chosen for policy rather than convenience?
  • Is @OptIn broader than necessary?
  • Does a public wrapper really absorb the risk, or should it propagate the marker?
  • Are Java callers and migration paths documented separately?
  • Is an owner responsible for reviewing the opt-in during upgrades?
Terminal window
./gradlew :bluetape4k-annotations:test --no-configuration-cache

The current tests verify explicit opt-in for ordinary markers, local implementation of an implementation-sensitive SPI, and the stable type names of all six markers.