This document outlines the coding standards and best practices for the KMP Multi-Module Project. Following these guidelines ensures consistency, readability, and maintainability across the codebase.
- General Principles
- Kotlin Coding Conventions
- Multiplatform-Specific Guidelines
- Documentation Requirements
- Architecture Guidelines
- Testing Standards
- Enforcement and Tools
Write code that is easy to understand:
- Optimize for readability rather than cleverness
- Prefer explicit over implicit
- Keep functions and classes focused on a single responsibility
- Avoid deep nesting of code blocks
Maintain consistent patterns across the codebase:
- Follow established conventions for module structure
- Use similar solutions for similar problems
- Be consistent with formatting, naming, and organization
Balance readability with performance:
- Profile before optimizing
- Document performance-critical sections
- Consider all platforms when making performance decisions
- Match file names to the top-level class:
UserRepository.ktfor classUserRepository - Use camel case with an uppercase first letter:
NetworkClient.kt - Extension functions: Use the format
TypeExtensions.kt:StringExtensions.kt
- Use PascalCase:
UserRepository,NetworkClient - Suffix interfaces with their role when appropriate:
UserRepository, notIUserRepository - Use descriptive and concise names that reflect the responsibility
- Use camelCase:
getUserProfile(),calculateTotal() - Prefer verb phrases for action functions:
fetchData()notdata() - Boolean functions should be phrased as questions:
isValid(),hasAccess()
- Use camelCase:
userName,isEnabled - Avoid single-letter names except for loop indices or mathematical formulas
- Constants and immutable top-level properties: Use SCREAMING_SNAKE_CASE:
MAX_COUNT,API_KEY
- All lowercase, with meaningful names:
org.example.feature.profile - Avoid plural forms:
org.example.modelnotorg.example.models
The project enforces formatting with Spotless and the official Kotlin style guide:
- 4 spaces for indentation (no tabs)
- Maximum line length of 120 characters
- Use trailing commas in parameter/argument lists for better diffs
- Organize top-level declarations in the following order:
- Package statement
- Import statements (alphabetical order, no wildcards)
- Top-level declarations
Organize class members in the following order:
- Properties
- Constants
- Immutable properties
- Mutable properties
- Secondary constructors
- Factory methods/companion object members
- Public methods
- Internal methods
- Private methods
- Inner classes and interfaces
- Companion object
- Avoid nullable types when possible
- Use the Elvis operator (
?:) for default values - Prefer safe calls (
?.) over null checks where appropriate - Always handle potential null values explicitly
- Use extension functions to enhance existing classes
- Keep extensions focused and single-purpose
- Place related extensions in a dedicated
TypeExtensions.ktfile
Use appropriate scope functions:
let: For executing code block with non-null valuesrun: For executing code block and returning resultwith: For calling multiple methods on the same objectapply: For configuring objects (returns the object)also: For additional actions that don't change the object (returns the object)
- Prefer structured concurrency patterns
- Always define and respect the CoroutineScope
- Use the appropriate dispatchers for the work being done
- Handle exceptions properly with try-catch or supervisorScope
- Place platform-agnostic code in the
commonsource set - Use intermediate source sets (e.g.,
jvmCommon,jsCommon) for platform group-specific code - Move code to the most general source set that can support it
- Use
expect/actualdeclarations for platform-specific implementations - Keep
expectdeclarations minimal and focused - Provide complete documentation on
expectdeclarations - Ensure all
actualimplementations maintain the contract specified by theexpectdeclaration
- Isolate platform-specific code in appropriate source sets
- Use clear naming to indicate platform specificity:
AndroidNetworkClient,IOSNetworkClient - Minimize platform branching in common code
- Use KDoc format for documentation comments
- Document all public APIs
- Include usage examples for complex or non-obvious functionality
- Explain the "why" rather than the "what" when the code is not self-explanatory
- All public classes, interfaces, and functions must have KDoc comments
- Include
@param,@return, and@throwstags where applicable - Document expected threading/coroutine usage where relevant
Example KDoc:
/**
* Fetches user data from the remote API.
*
* This method handles authentication and caching internally.
* If the network is unavailable, it will return cached data if available.
*
* @param userId The unique identifier of the user
* @param forceRefresh Whether to bypass cache and force a network request
* @return A [Flow] emitting the [User] data
* @throws [NetworkException] if the network request fails and no cache is available
*/
fun fetchUser(userId: String, forceRefresh: Boolean = false): Flow<User>- Each module should have a README.md file explaining:
- The purpose of the module
- Key components and their responsibilities
- How to use the module
- Dependencies and relationships to other modules
- Respect layer boundaries:
- Domain layer should not depend on data or presentation layers
- Data layer should not depend on presentation layer
- Use models appropriate to each layer; don't leak implementation details
- Each feature module should be self-contained
- Minimize dependencies between feature modules
- Use interfaces for cross-feature communication
- Use constructor injection where possible
- Configure Koin modules in a consistent way
- Scope dependencies appropriately
- Test all business logic in isolation
- Use descriptive test method names:
when_invalid_credentials_given_then_return_error() - Organize tests using the Given-When-Then pattern
- Use appropriate mocking strategies and fakes
- Test key user flows
- Focus on user-visible behavior
- Use screenshot testing where appropriate
- Aim for high coverage of business logic (domain layer)
- Test all edge cases and error conditions
- Include tests for both success and failure scenarios
The project uses several tools to enforce code quality:
- Detekt: Static code analysis
- Spotless: Code formatting
- Git Hooks: Pre-commit checks
- GitHub Actions: CI/CD enforcement
Detekt is configured with custom rules to enforce project-specific guidelines. Key areas include:
- Complexity metrics (cyclomatic complexity, LOC)
- Potential bugs
- Performance issues
- Style violations
Spotless enforces formatting using the ktlint ruleset with project-specific adjustments.
During code reviews, pay special attention to:
- Adherence to the architecture and layer separation
- Consistency with existing code
- Test coverage and quality
- Documentation completeness
- Performance considerations
This style guide is a living document. If you identify patterns or practices that should be standardized, propose changes through:
- Opening an issue with the "style-guide" label
- Discussing with the team
- Submitting a PR to update this document