Last Week
The entire UI is overhauled. Improved user experience on latency-sensitive operations by using optimistic updates.
What does it mean in English?
Optimistic updates is a user interface design pattern that makes applications feel faster and more responsive by immediately reflecting user actions in the interface before receiving confirmation from a server or backend system.
In traditional (“pessimistic”) UI approaches, when a user performs an action like liking a post or deleting an email, the interface waits for confirmation from the server before showing any change. This creates several issues:
- Noticeable lag between user action and visual feedback
- Reduced responsiveness, especially on slow networks
- Poor user experience as the app feels sluggish
- Uncertainty for users about whether their action was registered
How optimistic updates work
- User performs an action (clicks a button, submits a form, etc.)
- The UI updates immediately to show the expected result
- In the background, the request is sent to the server
- If successful: The UI remains as already shown (no visible change needed)
- If failed: The UI reverts to the previous state, usually with an error message
Benefits of optimistic updates
- Improved perceived performance: Applications feel faster and more responsive
- Better user experience: Immediate feedback creates a smoother interaction flow
- Reduced frustration: Users don’t have to wait to see the results of their actions
- Works well with intermittent connectivity: Users can continue interacting even during brief network issues
- Reduces visual “jank”: Eliminates loading spinners and state changes for common actions
When NOT to use optimistic updates
- For critical operations where accuracy is paramount (financial transactions)
- When failures are common or expected
- When the outcome is unpredictable or depends on complex server-side logic
- When conflicts between users are likely
Nerdy Details
The HostDashboardViewModel
in the Shokken app implements optimistic updates for queue management actions like notifying customers.
First, we identify and extract the entry to update.
val currentEntries = _state.value.queueEntries
val entryIndex = currentEntries.indexOfFirst { it.id == entryId }
val entryToUpdate = currentEntries[entryIndex]
We create the updated version. This uses Kotlin’s data class copy() function to create a new instance with modified properties.
val updatedEntry = entryToUpdate.copy(
status = QueueStatus.NOTIFIED,
notifiedAt = Clock.System.now()
)
Update UI immediately (aka. optimistically).
setActionInProgress(entryId, true, ActionType.NOTIFY)
updateEntryInLocalState(entryToUpdate, updatedEntry)
We then make the network request.
val result = queueRepository.notifyCustomer(entryId)
Of course the whole operation is wrapped in the try/catch block since we need to handle the outcome (success/failure). Note that at the end we reset the loading state in either case.
try {
// Make the actual API call
val result = queueRepository.notifyCustomer(entryId)
if (result.isSuccess) {
// Success - UI already updated, just show confirmation
snackbarManager.showSuccess(HostStrings.CUSTOMER_NOTIFIED)
} else {
// Error - revert to the original state
updateEntryInLocalState(updatedEntry, entryToUpdate)
snackbarManager.showError("Failed to notify customer: ${result.exceptionOrNull()?.message}")
}
} catch (e: Exception) {
// Exception - revert to the original state
updateEntryInLocalState(updatedEntry, entryToUpdate)
snackbarManager.showError("Error notifying customer: ${e.message}")
} finally {
setActionInProgress(entryId, false, ActionType.NONE)
}
Next Week
Connect the guest mode to Supabase backend and enable queue insertion.