- Don't write or change READMEs unless the user specifically asked for it
- Don't add unnecessary comments or KDoc in code or build scripts
- Stick to the current code style
- Don't use scope functions (
let,run,apply,also,with,takeIf,takeUnless) - Don't create files with loose top-level helper or extension functions that are only used a few times, put the logic directly where it is used or into an object instead
- Avoid
var,lateinit varandby lazy - Use the
initblock only for things likerequireandcheck - Prefer early returns and guard clauses over nested
ifs, e.g.val id = sender.getUniqueId() ?: return - Use trailing commas in multiline parameter lists, arguments and enum entries
- One class, data class, interface or enum per file, only configs may keep their nested data classes in one file
The config is .detekt/detekt.yml on top of the detekt defaults.
- No
!!, use?: return,?: error("...")or a null check - No unsafe casts (
as) without a check, useas?oris - No
println/print, use an SLF4J logger:LoggerFactory.getLogger(MyClass::class.java) - No
printStackTrace(), log the exception:logger.error("Failed to ...", e) - No swallowed exceptions: log them, or name the parameter
_if ignoring is intended (catch (_: Exception)) - Don't throw generic exceptions (
Exception,RuntimeException,Throwable), useerror(...),require(...),check(...)or a specific exception with a message - Use
require/check/requireNotNull/checkNotNullinstead of manually throwingIllegalArgumentException/IllegalStateException - No unused imports, parameters, private properties, private functions or variables
(allowed unused parameter names:
ignored,expected,event,_) - No empty blocks (empty
catch,if, functions without a reason) - Max 3
returnand 3throwstatements per function (guard clauses don't count) - Functions max 80 lines, max 6 function parameters, max 8 constructor parameters (default params don't count)
- Max nesting depth of 4, max 4 conditions in one
if, keep cyclomatic and cognitive complexity low, split into private functions when it gets too big - No
Thread.sleepin coroutines, usedelay; noGlobalScope, norunBlockingin plugin code - Don't mark functions
suspendif they don't suspend - Don't catch
CancellationExceptionsilently in coroutines - No
varwith mutable collections (var list = mutableListOf()), andvalinstead ofvarwhen it's never reassigned - Use
const valfor constant primitives and strings in objects and companion objects - Constants in
SCREAMING_SNAKE_CASE, classesPascalCase, functions and propertiescamelCase - File name must match the single top-level class in it
- Package must match the directory (
app.simplecloud.plugin.<plugin>.<platform>...) - No redundant
Unitreturn types, no unnecessary?.on non-null values, useisNullOrEmpty()/orEmpty() - No
@Suppressunless there is really no other way - Disabled, so these are fine: magic numbers, wildcard imports, max line length,
TooManyFunctions, catching generic exceptions, spread operator
- Spotless with ktlint (
intellij_ideastyle) and.editorconfig - 4 spaces indent (2 for yaml), LF line endings, final newline, UTF-8
- Keep lines around 120 chars
- Wildcard imports are fine for
kotlin.**,kotlinx.**andio.ktor.**, otherwise explicit imports