xiaohongshu
xiaohongshu
lang-kotlin-library-dev
Kotlin-specific library development patterns. Use when creating Kotlin libraries, designing idiomatic Kotlin APIs with extension functions and DSLs, configuring Gradle Kotlin DSL (build.gradle.kts), managing multiplatform projects, testing with Kotest/JUnit, writing KDoc documentation, or publish...
Full skill instructions
Kotlin-specific patterns for library development. This skill extends meta-library-dev with Kotlin tooling, multiplatform capabilities, and ecosystem practices.
meta-library-dev - Foundational library patterns (API design, versioning, testing strategies)For general concepts like semantic versioning, module organization principles, and testing pyramids, see the meta-skill first.
meta-library-devlang-kotlin-dev| Task | Command/Pattern |
|---|---|
| New library | gradle init --type kotlin-library |
| Build | ./gradlew build |
| Test | ./gradlew test |
| Generate docs | ./gradlew dokkaHtml |
| Publish (local) | ./gradlew publishToMavenLocal |
| Publish (Maven Central) | ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository |
| Check API compatibility | ./gradlew apiCheck (requires binary-compatibility-validator) |
plugins {
kotlin("jvm") version "1.9.22"
id("org.jetbrains.dokka") version "1.9.20"
`maven-publish`
signing
}
group = "com.example"
version = "1.0.0"
repositories {
mavenCentral()
}
kotlin {
jvmToolchain(17)
compilerOptions {
freeCompilerArgs.add("-Xjsr305=strict")
allWarningsAsErrors.set(true)
}
}
dependencies {
implementation(kotlin("stdlib"))
testImplementation(kotlin("test"))
testImplementation("io.kotest:kotest-runner-junit5:5.8.0")
testImplementation("io.kotest:kotest-assertions-core:5.8.0")
}
tasks.test {
useJUnitPlatform()
}
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
pom {
name.set("My Kotlin Library")
description.set("A brief description of what this library does")
url.set("https://github.com/username/repo")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("username")
name.set("Your Name")
email.set("[email protected]")
}
}
scm {
connection.set("scm:git:git://github.com/username/repo.git")
developerConnection.set("scm:git:ssh://github.com/username/repo.git")
url.set("https://github.com/username/repo")
}
}
}
}
repositories {
maven {
name = "sonatype"
url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.findProperty("sonatypeUsername") as String?
password = project.findProperty("sonatypePassword") as String?
}
}
}
}
signing {
sign(publishing.publications["maven"])
}
# Publishing configuration
signing.keyId=ABCD1234
signing.password=your-gpg-password
signing.secretKeyRingFile=/path/to/secring.gpg
sonatypeUsername=your-username
sonatypePassword=your-password
# Kotlin configuration
kotlin.code.style=official
kotlin.parallel.tasks.in.project=true
# Build optimizations
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
org.gradle.parallel=true
org.gradle.caching=true
plugins {
kotlin("multiplatform") version "1.9.22"
id("org.jetbrains.dokka") version "1.9.20"
`maven-publish`
}
group = "com.example"
version = "1.0.0"
kotlin {
// JVM target
jvm {
compilations.all {
kotlinOptions.jvmTarget = "17"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
// JavaScript target
js(IR) {
browser()
nodejs()
}
// Native targets
linuxX64()
macosX64()
macosArm64()
mingwX64()
// iOS targets
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
// Common source set
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
// JVM-specific
val jvmMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.7.3")
}
}
val jvmTest by getting
// JS-specific
val jsMain by getting
val jsTest by getting
// Native shared
val nativeMain by creating {
dependsOn(commonMain)
}
val nativeTest by creating {
dependsOn(commonTest)
}
// Configure all native targets
val linuxX64Main by getting { dependsOn(nativeMain) }
val macosX64Main by getting { dependsOn(nativeMain) }
val macosArm64Main by getting { dependsOn(nativeMain) }
val mingwX64Main by getting { dependsOn(nativeMain) }
val iosX64Main by getting { dependsOn(nativeMain) }
val iosArm64Main by getting { dependsOn(nativeMain) }
val iosSimulatorArm64Main by getting { dependsOn(nativeMain) }
}
}
src/
├── commonMain/kotlin/
│ └── com/example/
│ └── Library.kt
├── commonTest/kotlin/
│ └── com/example/
│ └── LibraryTest.kt
├── jvmMain/kotlin/
│ └── com/example/
│ └── JvmSpecific.kt
├── jvmTest/kotlin/
├── jsMain/kotlin/
├── jsTest/kotlin/
├── nativeMain/kotlin/
│ └── com/example/
│ └── NativeSpecific.kt
└── nativeTest/kotlin/
Use extension functions for fluent APIs:
// Good: Extension function pattern
fun String.toSnakeCase(): String =
this.replace(Regex("([a-z])([A-Z])"), "$1_$2").lowercase()
// Usage
val result = "HelloWorld".toSnakeCase() // "hello_world"
// Good: Extension on generic types
fun <T> List<T>.second(): T? = this.getOrNull(1)
// Good: Extension with receiver
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
Type-safe builders for configuration:
// DSL API
class HttpClient internal constructor() {
var timeout: Long = 30_000
var followRedirects: Boolean = true
val headers: MutableMap<String, String> = mutableMapOf()
fun header(name: String, value: String) {
headers[name] = value
}
}
// Builder function
fun httpClient(configure: HttpClient.() -> Unit): HttpClient {
return HttpClient().apply(configure)
}
// Usage
val client = httpClient {
timeout = 60_000
followRedirects = false
header("User-Agent", "MyApp/1.0")
}
Nested DSL example:
@DslMarker
annotation class HtmlDsl
@HtmlDsl
abstract class Tag(val name: String) {
private val children = mutableListOf<Tag>()
protected fun <T : Tag> initTag(tag: T, init: T.() -> Unit): T {
tag.init()
children.add(tag)
return tag
}
}
class HTML : Tag("html") {
fun head(init: Head.() -> Unit) = initTag(Head(), init)
fun body(init: Body.() -> Unit) = initTag(Body(), init)
}
class Head : Tag("head") {
fun title(init: Title.() -> Unit) = initTag(Title(), init)
}
class Title : Tag("title")
class Body : Tag("body")
// Usage
fun html(init: HTML.() -> Unit): HTML = HTML().apply(init)
val page = html {
head {
title { }
}
body { }
}
Use sealed classes for restricted hierarchies:
// Good: Sealed class for results
sealed interface Result<out T> {
data class Success<T>(val value: T) : Result<T>
data class Failure(val error: Throwable) : Result<Nothing>
}
// Exhaustive when expressions
fun <T> Result<T>.getOrThrow(): T = when (this) {
is Result.Success -> value
is Result.Failure -> throw error
}
// Good: Sealed class for events
sealed class NetworkEvent {
data class Connected(val connectionId: String) : NetworkEvent()
data class Disconnected(val reason: String) : NetworkEvent()
data class MessageReceived(val message: String) : NetworkEvent()
data object Reconnecting : NetworkEvent()
}
Use inline for higher-order functions:
// Good: Inline function with reified type
inline fun <reified T> Any.asOrNull(): T? = this as? T
// Usage without explicit type
val string: String? = someObject.asOrNull()
// Good: Inline with lambda
inline fun <T> measureTime(block: () -> T): Pair<T, Long> {
val start = System.currentTimeMillis()
val result = block()
val time = System.currentTimeMillis() - start
return result to time
}
// Good: Inline value classes for type safety
@JvmInline
value class UserId(val value: String)
@JvmInline
value class Email(val value: String)
// No runtime overhead, but type-safe
fun sendEmail(userId: UserId, email: Email) { }
Use operator functions judiciously:
data class Vector(val x: Double, val y: Double) {
operator fun plus(other: Vector) = Vector(x + other.x, y + other.y)
operator fun minus(other: Vector) = Vector(x - other.x, y - other.y)
operator fun times(scalar: Double) = Vector(x * scalar, y * scalar)
operator fun unaryMinus() = Vector(-x, -y)
}
// Usage
val v1 = Vector(1.0, 2.0)
val v2 = Vector(3.0, 4.0)
val sum = v1 + v2
val scaled = v1 * 2.0
my-kotlin-library/
├── build.gradle.kts
├── gradle.properties
├── settings.gradle.kts
├── src/
│ ├── main/kotlin/
│ │ └── com/example/library/
│ │ ├── Library.kt # Public API
│ │ ├── Models.kt # Data classes
│ │ ├── Extensions.kt # Extension functions
│ │ ├── Dsl.kt # DSL builders
│ │ └── internal/ # Internal implementation
│ │ └── Utils.kt
│ └── test/kotlin/
│ └── com/example/library/
│ ├── LibraryTest.kt
│ └── DslTest.kt
├── docs/
│ └── index.md
└── README.md
Main library file (Library.kt):
package com.example.library
// Public API exports
public fun createClient(): Client = ClientImpl()
public interface Client {
fun connect(): Result<Unit>
fun disconnect()
}
// Internal implementation
internal class ClientImpl : Client {
override fun connect(): Result<Unit> = TODO()
override fun disconnect() = TODO()
}
// Public - part of API (default, but explicit is clearer)
public fun publicFunction() {}
// Internal - visible within module only
internal fun internalFunction() {}
// Private - visible within file only
private fun privateFunction() {}
// Protected - visible in class and subclasses
protected open class Base {
protected fun protectedFunction() {}
}
import io.kotest.core.spec.style.FunSpec
import io.kotest.core.spec.style.StringSpec
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.collections.shouldContain
import io.kotest.matchers.string.shouldStartWith
// String spec (simple)
class SimpleTest : StringSpec({
"string length should be 5" {
"hello".length shouldBe 5
}
"string should start with h" {
"hello" shouldStartWith "h"
}
})
// Fun spec (descriptive)
class CalculatorTest : FunSpec({
test("addition should work correctly") {
val result = 2 + 2
result shouldBe 4
}
context("division") {
test("dividing by non-zero should work") {
10 / 2 shouldBe 5
}
test("dividing by zero should throw") {
shouldThrow<ArithmeticException> {
10 / 0
}
}
}
})
// Behavior spec (BDD)
class ClientTest : BehaviorSpec({
given("a connected client") {
val client = createClient()
client.connect()
`when`("sending a message") {
val result = client.send("test")
then("it should succeed") {
result shouldBe Success
}
}
`when`("disconnecting") {
client.disconnect()
then("it should be disconnected") {
client.isConnected shouldBe false
}
}
}
})
import io.kotest.core.spec.style.FunSpec
import io.kotest.property.Arb
import io.kotest.property.arbitrary.*
import io.kotest.property.checkAll
class PropertyTest : FunSpec({
test("string reverse is involutive") {
checkAll<String> { str ->
str.reversed().reversed() shouldBe str
}
}
test("addition is commutative") {
checkAll(Arb.int(), Arb.int()) { a, b ->
a + b shouldBe b + a
}
}
test("custom generator") {
val emailArb = arbitrary { rs ->
val name = Arb.string(5..10).bind()
val domain = Arb.string(5..10).bind()
"$name@$domain.com"
}
checkAll(emailArb) { email ->
email shouldContain "@"
}
}
})
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class JUnitTest {
private lateinit var client: Client
@BeforeEach
fun setup() {
client = createClient()
}
@Test
@DisplayName("Client should connect successfully")
fun `client should connect`() {
val result = client.connect()
assertTrue(result.isSuccess)
}
@ParameterizedTest
@ValueSource(strings = ["hello", "world", "test"])
fun `should handle various inputs`(input: String) {
val result = process(input)
assertTrue(result.isNotEmpty())
}
}
/**
* Creates an HTTP client with the specified configuration.
*
* This function uses a DSL to configure the client. All settings
* are optional and have sensible defaults.
*
* @param configure A lambda with receiver for configuring the client
* @return A configured HTTP client instance
*
* @sample com.example.samples.basicClientExample
* @see HttpClient for available configuration options
* @since 1.0.0
*
* Example:
* ```kotlin
* val client = httpClient {
* timeout = 60_000
* followRedirects = false
* header("User-Agent", "MyApp/1.0")
* }
* ```
*/
fun httpClient(configure: HttpClient.() -> Unit): HttpClient {
return HttpClient().apply(configure)
}
/**
* Represents the result of an operation.
*
* @param T The type of the successful result value
* @property value The result value if successful
* @property error The error if failed
*
* @constructor Creates a result instance
*/
sealed class Result<out T> {
/**
* Represents a successful result.
*
* @property value The successful value
*/
data class Success<T>(val value: T) : Result<T>()
/**
* Represents a failed result.
*
* @property error The error that caused the failure
*/
data class Failure(val error: Throwable) : Result<Nothing>()
}
build.gradle.kts:
plugins {
id("org.jetbrains.dokka") version "1.9.20"
}
tasks.dokkaHtml {
outputDirectory.set(buildDir.resolve("dokka"))
dokkaSourceSets {
named("main") {
moduleName.set("My Library")
includes.from("docs/index.md")
sourceLink {
localDirectory.set(file("src/main/kotlin"))
remoteUrl.set(
URL("https://github.com/user/repo/tree/main/src/main/kotlin")
)
remoteLineSuffix.set("#L")
}
externalDocumentationLink {
url.set(URL("https://kotlinlang.org/api/latest/jvm/stdlib/"))
}
}
}
}
# Generate key
gpg --gen-key
# List keys
gpg --list-keys
# Export public key
gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID
# Export private key
gpg --export-secret-keys YOUR_KEY_ID > secring.gpg
build.gradle.kts:
plugins {
`maven-publish`
signing
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
}
nexusPublishing {
repositories {
sonatype {
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(
uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
)
username.set(project.findProperty("sonatypeUsername") as String?)
password.set(project.findProperty("sonatypePassword") as String?)
}
}
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
artifact(tasks["kotlinSourcesJar"])
artifact(tasks["dokkaJavadocJar"])
pom {
name.set(project.name)
description.set("Library description")
url.set("https://github.com/user/repo")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("userid")
name.set("User Name")
email.set("[email protected]")
}
}
scm {
connection.set("scm:git:git://github.com/user/repo.git")
developerConnection.set("scm:git:ssh://github.com/user/repo.git")
url.set("https://github.com/user/repo")
}
}
}
}
}
signing {
sign(publishing.publications["maven"])
}
val dokkaJavadocJar by tasks.registering(Jar::class) {
dependsOn(tasks.dokkaJavadoc)
from(tasks.dokkaJavadoc.flatMap { it.outputDirectory })
archiveClassifier.set("javadoc")
}
val kotlinSourcesJar by tasks.registering(Jar::class) {
from(sourceSets["main"].allSource)
archiveClassifier.set("sources")
}
# 1. Build the project
./gradlew build
# 2. Publish to local Maven for testing
./gradlew publishToMavenLocal
# 3. Publish to Sonatype staging
./gradlew publishToSonatype
# 4. Close and release staging repository
./gradlew closeAndReleaseSonatypeStagingRepository
# Or all in one
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
build.gradle.kts:
plugins {
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.14.0"
}
apiValidation {
ignoredProjects.add("test-utils")
nonPublicMarkers.add("com.example.InternalApi")
validationDisabled = false
}
# Generate API dump
./gradlew apiDump
# Check API compatibility
./gradlew apiCheck
Commit the API dump:
my-library/
├── api/
│ └── my-library.api
// Bad: Extension on Any
fun Any.doSomething() { }
// Good: Specific type
fun String.doSomething() { }
// v1.0.0
fun connect(host: String, port: Int = 8080)
// v1.1.0 - WRONG! This is breaking
fun connect(host: String, port: Int = 8080, timeout: Long)
// v1.1.0 - Correct: Add overload or new default at end
fun connect(host: String, port: Int = 8080, timeout: Long = 30000)
// Bad: Everything public by default
class InternalHelper {
fun helpfulMethod() { }
}
// Good: Explicit visibility
internal class InternalHelper {
internal fun helpfulMethod() { }
}
// Bad: Mutable public property
val items: MutableList<String> = mutableListOf()
// Good: Immutable public interface
private val _items: MutableList<String> = mutableListOf()
val items: List<String> get() = _items
// Or use read-only collection
val items: List<String> = listOf()
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
}
plugins {
kotlin("plugin.serialization") version "1.9.22"
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}
dependencies {
testImplementation(kotlin("test"))
testImplementation("io.kotest:kotest-runner-junit5:5.8.0")
testImplementation("io.kotest:kotest-assertions-core:5.8.0")
testImplementation("io.kotest:kotest-property:5.8.0")
testImplementation("io.mockk:mockk:1.13.9")
}
meta-library-dev - Foundational library patternsxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience