From 41eb5f89fdf9c435061b2e7d2ef8e376b8a4b860 Mon Sep 17 00:00:00 2001 From: Laxystem Date: Thu, 6 Feb 2025 23:35:14 +0000 Subject: [PATCH] Update CONTRIBUTING.md --- CONTRIBUTING.md | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d19f680..d02e729 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,15 +11,26 @@ Contributions are welcome and accepted. Please create issues or pull requests! ### APIs -APIs should be thread-safe and immutable. Expensive operations should be `suspend fun`s. - -To create a new API, *always* declare an interface or (for immutable data) a data class. If possible, declare it as a `fun interface` (see [Interfaces, Functional Interfaces, or Typealiases](#interfaces-functional-interfaces-or-typealiases) below). - -Implementations should be creatable via extension functions (e.g. `ByteArray.appender()`) or functions named the same or as if they were a subtype of as the interface (e.g. `ByteAppender()`, `NonThrowingByteAppender()`). - -If the implementation accepts a function as a parameter (e.g. `EverlastingMutex(producer: (Descriptor) -> Product)`), it should be implemented as an object expression using `crossinline` on the lamabdas. Otherwise, declare a private class (usually called `FooImpl`). - -#### Interfaces, functional interfaces, or typealiases. +* APIs should be thread-safe and immutable. +* Expensive operations should be `suspend fun`s. +* To create a new API, *always* declare an interface or (for immutable data) a data class. If possible, declare it as a `fun interface` (see [Interfaces, Functional Interfaces, or Typealiases](#interfaces-functional-interfaces-or-typealiases) below). +* Implementations should be instantiated via extension functions (e.g. `ByteArray.appender()`) or functions named the same or as if they were a subtype of as the interface (e.g. `ByteAppender()`, `NonThrowingByteAppender()`). +* If an implementation accepts a lambda as a parameter (e.g. `EverlastingMutex(producer: (Descriptor) -> Product)`), it should be implemented, with the lambda marked `crossinline`. Otherwise, declare a private class (usually called `FooImpl`). + * Mark the implementation class (or if inline, the function) with the `@author` KDoc tag. +* Always expose everything necessary for users to reimplement a built-in API. If some API is lower-level and should not be used except for this specific purpsoe, mark it with `@RawSpockApi`. +* If an API allows easily breaking the contract of another, mark it with `@DelicateSpockApi`. +* If you're unsure about an API, mark it with `@ExperimentalSpockApi`. +* Document your APIs, and always use the `@since` KDoc tag. + * When removing the `@ExprimentalSpockApi` annotation, don't change the `@since` tag. + * When breaking ABI *or* API compatibility, reset the `@since` tag. +* Always use `@Throws` when applicable, including the `@throws` KDoc tag. + +#### Interfaces, Functional Interfaces, or Typealiases + +* First, decide on your interface's contract. +* If you intend on declaring extensions on the interface, or having a complex contract, do not use a typealias. +* If you're unsure about a function's signature, mark it with `@ExperimentalSpockApi`. +* If you're sure about the current API, but not sure if you want to add more functions later, mark it with `@RequiresSubclassOptIn(ExperimentalSpockApi::class)`. If you need to choose between having more than one function on your interface and `fun`ness, rhe non-functional interface is likely the better choice. @@ -31,6 +42,11 @@ Prefer a `@RequiresSubclassOptIn` annotation over `fun`ness. See the [Kotlin documentation](https://kotlinlang.org/docs/fun-interfaces.html#functional-interfaces-vs-type-aliases) on the topic. +### Implementation + +* Prefer nullability over `::prop.isInitialized`. +* Prefer `Flag` over `lateinit var: Unit`. + ## Useful Commands > [!TIP]