String routes such as "workout/$id" spread formatting and parsing across call sites. A typo becomes a runtime navigation failure.
Model destinations:
@Serializable
sealed interface Screen {
@Serializable data object Home : Screen
@Serializable data class Workout(val id: String) : Screen
@Serializable data class Report(val period: String) : Screen
}
Navigation code now accepts typed arguments and the destination obtains the same type from its back stack entry.
The route is only one part of navigation state. A deep link may require authentication, tab selection, and loading the entity before presenting its detail. Centralize that coordination rather than letting every screen parse links.
Do not pass large domain objects through navigation. Pass stable identity and load current data from the repository. This supports process recreation and avoids stale serialized copies.
Treat invalid or missing IDs as a normal state with a safe destination. Test cold start, restored back stack, unauthenticated links, and deleted resources.
Typed routes remove string mistakes; explicit state restoration makes navigation reliable.