-
Notifications
You must be signed in to change notification settings - Fork 1
Project Conventions
arthur-mrgt edited this page Nov 13, 2024
·
7 revisions
This document outlines the conventions to follow in order to maintain clean, readable, and consistent code for this project using Jetpack Compose, in line with Google's recommended conventions and industry standards.
- 1. Naming Convention
- 2. Code Convention
- 3. Style Convention
- 4. Commenting & Documentation
- 5. Kotlin Best Practices
- 6. Jetpack Compose Best Practices
-
Classes: Use PascalCase notation. Names should be descriptive, e.g.,
MainActivity
,UserRepository
. -
Interfaces: Don't prefix interfaces with "I" to differentiate from classes, interfaces are named like classes without a prefix, e.g.,
ToDosRepository
.
-
Local Variables: Use camelCase, e.g.,
isLoading
,userName
. -
Object Properties: Use camelCase, and make the names descriptive, e.g.,
currentUser
,isVisible
. -
Private Properties: Prefix private properties with an underscore if necessary for clarity, e.g.,
_userCache
.
- Constants should be in uppercase letters with underscores, e.g.,
MAX_CONNECTIONS
,TIMEOUT_DURATION
. - Group related constants into companion objects or top-level
object
declarations for better organization.
- Use camelCase for function names, starting with a verb, e.g.,
fetchData()
,onCreateView()
. - Functions should be named descriptively to reflect their behavior or purpose.
- Use PascalCase for naming composable functions and objects, e.g.,
MainScreen
,UserCard
,SettingsButton
. - Names should be descriptive to clearly indicate the purpose of the composable, e.g.,
ProfilePicture
,SubmitButton
. - Avoid abbreviations unless they are well-known and do not reduce clarity.
- Use lowercase letters and follow the reverse domain name notation, e.g.,
com.example.myapp
. - Organize packages by feature or layer, e.g.,
com.example.myapp.ui
,com.example.myapp.data
.
- Use 4 spaces for indentation. Do not use tabs.
- Limit the length of a line to 100 characters.
- Break up long lines logically, ensuring readability.
- Avoid wildcard imports (
import *
). Import only what is necessary. - Organize imports into standard Kotlin libraries, third-party libraries, and project-specific imports.
- Use the IDE's auto-import feature to keep imports clean and organized.
- Always use braces, even for single-line blocks:
if (condition) { doSomething() }
- Use
when
expressions instead of multipleif-else
conditions when appropriate. - Prefer
elvis
operator (?:
) for handling null values concisely.
- Use a single space around operators (
=
,+
,-
, etc.). - Add a space after commas in function calls and declarations.
- Avoid unnecessary spaces inside parentheses and brackets.
- Follow this order of declaration in a Kotlin class or file:
- Companion objects
- Constant properties
- Instance properties
- Lifecycle methods (in activities or fragments)
- Public methods
- Private methods
- Always declare return types for public functions.
- For local variables, let the compiler infer types if the type is obvious from the context.
- If a lambda function is the last parameter of a function, place it outside of the parentheses.
button.setOnClickListener { doSomething() }
- Use parameter names like
it
only when the context is clear; otherwise, use descriptive names.
- Use the built-in formatter in Android Studio to maintain consistent formatting.
- Reformat code before committing changes to ensure consistent style : optimize code + reformat code + ktfmtFormat
- Use KDoc to document classes, methods, and public properties. Comments should be clear and concise:
/** * Fetches a list of ToDos from the API. * * @return A list of ToDo items. */ fun fetchToDos(): List<ToDo> { // ... }
- Add inline comments only when necessary to explain complex or non-trivial logic.
- Keep inline comments brief and relevant.
- Use
// TODO:
to indicate incomplete features or areas for improvement. - Include the author's name and date for tracking, e.g.,
// TODO: Refactor this method - John, 2024-10-04
.
- Keep documentation up to date with code changes.
- Use clear and consistent language in comments and documentation.
- Avoid redundant comments that simply restate the code.
- Use Kotlin's null safety system wisely. Avoid nullable types when possible.
- Use
?.let {}
to safely operate on nullable objects. - Consider using
checkNotNull()
orrequireNotNull()
for null checks where appropriate. - Use
lateinit
sparingly and only when you are certain the variable will be initialized before use.
- Use
data class
to represent objects that contain only data, such as models or DTOs. - Override
toString()
,equals()
, andhashCode()
if additional behavior is needed beyond the default.
- Prefer functional methods like
map
,filter
,fold
over explicit loops, unless performance considerations require otherwise. - Use
forEach
for iteration where side effects are expected. - Use
groupBy
andassociateBy
for organizing collections efficiently.
- Use
suspend
and coroutines for asynchronous operations instead of callbacks. - Use
withContext(Dispatchers.IO)
for I/O operations to avoid blocking the main thread. - Prefer
Flow
for handling streams of data asynchronously.
- Use extension functions to add functionality to existing classes without modifying them.
- Keep extension functions relevant and maintainable; avoid excessive use.
- Use
sealed class
for representing restricted class hierarchies, especially for representing different states in a UI or different types in a result.
- Break complex UIs into small, reusable composable components.
@Composable fun Greeting(name: String) { Text("Hello, $name!") }
- Each composable should be responsible for a single part of the UI.
- Use immutable data models to avoid issues related to recomposition.
- Avoid passing mutable state directly to composables; instead, use derived states.
- Use
remember
andmutableStateOf
to manage state within components, while adhering to the MVVM architecture for larger-scale state management.val isChecked = remember { mutableStateOf(false) }
- Prefer
rememberSaveable
for state that needs to survive configuration changes.
- Minimize recomposition by using
remember
whenever possible to store computed values. - Use keys with
LazyColumn
items to maintain consistency and avoid unnecessary recompositions.LazyColumn { items(items = itemList, key = { it.id }) { item -> ItemView(item) } }
- Use
produceState
andderivedStateOf
to efficiently handle derived or computed states.
- Use
@Preview
annotations to quickly test UI components without running the entire app.@Preview(showBackground = true) @Composable fun PreviewGreeting() { Greeting(name = "Compose") }
- Add parameters to
@Preview
to test different states, e.g., light and dark themes.
- Use MaterialTheme for consistent theming across the app.
- Define custom colors, typography, and shapes in a central theme file to maintain consistency.
@Composable fun MyAppTheme(content: @Composable () -> Unit) { MaterialTheme( colors = myColors, typography = myTypography, shapes = myShapes, content = content ) }
- Use
Modifier
to style composables, and chain modifiers for readability.
- Use
ComposeTestRule
for testing Jetpack Compose components. - Write UI tests that validate the expected behavior of composables, using semantics to assert state or values.
composeTestRule.onNodeWithText("Hello, Compose!").assertExists()