Minimize the surface callers can depend on
public declarations are broadly visible. private hides implementation details. internal exposes declarations within a compilation module, which is not necessarily the same as a package. protected makes a class member available to subclasses as well as its declaring class.
class Counter {
var value: Int = 0
private set
fun increment() { value++ }
}
internal fun counterLabel(counter: Counter): String = "Count: ${counter.value}"
A caller can read the count but cannot assign an arbitrary value. Visibility is an API organization mechanism, not encryption or a security boundary. Compiled code and reflection may still reveal implementation details.
Top-level private declarations are file-private. Kotlin does not provide Java's package-private default. Making everything public expands the compatibility promises you must preserve later.
Exercise
Create a small repository with a public read operation and a private mutable collection. Expose a snapshot rather than the mutable backing object.
Check: callers should be unable to mutate the repository's state through a returned collection or a public setter.