Welcome, and thank you for contributing! 🎉
Readable > Useful > High performance but poorly readable.
Truly readable code is more than just clear—it's understandable even without context (contextless readability).
-
Use English for all comments.
-
Stay polite in code comments. Avoid offensive language.
- You can be grumpy, but express it with decent wording.
-
Avoid confusing abbreviations.
Bad:
dl := &net.Dialer{}
Good:
dialer := &net.Dialer{}
-
Redundant comments are as useless as this sentence.
-
Strive for readability through naming, not excessive comments.
-
Use constants wherever possible.
Bad:
import ( "net" N "github.com/sagernet/sing/common/network" ) func dnsConn() (net.Conn, error) { return net.Dial(N.NetworkUDP, "8.8.8.8:53") // Google DNS }
Good:
import ( "net" N "github.com/sagernet/sing/common/network" ) func dnsConn() (net.Conn, error) { const googleDNS = "8.8.8.8:53" return net.Dial(N.NetworkUDP, googleDNS) }
-
Our style uses names to communicate meaning.
-
Do not build filesystem paths by string concatenation such as
base + "/child"orabsolutePath + "/". -
Prefer
File.resolve(...),File(parent, child), or equivalent path APIs when combining local paths. -
On Windows, we should use
/instead of\Bad:
val geoDir = repository.externalAssetsDir.absolutePath + "/geo" ruleSet.path = "$geoDir/$name.srs"
Good:
import fr.husi.ktx.invariantPathString val geoDir = repository.externalAssetsDir.resolve("geo") ruleSet.path = geoDir.resolve("$name.srs").invariantPathString()
- Run
make fmt_goandmake test_gobefore committing. - Write unit tests wherever possible.
- Make documentation writing a habit.
- Always use imports instead of fully qualified names in code.
- The only exception is when referencing
Rclasses from other packages (e.g.,com.google.android.material.R).
Bad:
val density = androidx.compose.ui.platform.LocalDensity.current
androidx.compose.runtime.DisposableEffect(view) { /* ... */ }Good:
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.runtime.DisposableEffect
val density = LocalDensity.current
DisposableEffect(view) { /* ... */ }- Prefer explicit backing fields for public read-only
StateFloworSharedFlowproperties backed by mutable flows. - This keeps the public type read-only while avoiding extra
_uiState/_uiEventproperties.
Good:
val uiState: StateFlow<ScreenUiState>
field = MutableStateFlow(ScreenUiState())
val uiEvent: SharedFlow<ScreenUiEvent>
field = MutableSharedFlow<ScreenUiEvent>()
fun updateName(name: String) {
uiState.update { it.copy(name = name) }
}-
forEachis fluent, especially at the end of a chain:strings.filter { it.isNotEmpty() }.forEach { println(it) } -
For standalone iterations,
forloops are often more flexible:- Can use
break - Can use
returnfrom enclosing function - Explicit variable names are clearer
fun firstNonEmptyString(strings: List<String>): String? { for (string in strings) { if (string.isNotEmpty()) { return string } } return null }
- Can use
- Prefer
alsooverapplywhenthisis ambiguous. applyis great for object configuration, but nested scopes (e.g. in Activities or Fragments) may introduce confusion.alsomakes the receiver explicit viait, improving readability.
Example of ambiguity with apply:
private lateinit var textView: TextView
private val isVisible = true
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
textView = findViewByID(R.id.textView).apply {
this@apply.isVisible = isVisible // `this` is ambiguous
}
}Preferred version with also:
private lateinit var textView: TextView
private val isVisible = true
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
textView = findViewByID(R.id.textView).also {
it.isVisible = isVisible // `it` clearly refers to the TextView
}
}