Building Vesper on Ballast: One State Model to Rule Them
An in-depth look at how the Vesper app uses the Ballast KMP MVI framework to manage frontend and backend states seamlessly.

Stock photo for illustration only, not from the actual event
- Ballast is a custom KMP MVI framework built specifically for the Vesper app.
- Every screen implements a Contract containing Inputs, State, and Events.
- Avoids reflection and KSP by requiring explicit code for better debuggability.
- AppStateViewModel persists application-wide state using multiplatform-settings.
As part of the Vesper Design Diaries series detailing the creation of a production-grade KMP app on a bootstrap budget, creator C.J. Brooks explores Ballast, the foundational architecture powering the frontend and bridging server-side workloads. The author transparently notes that they authored Ballast and have maintained it for several years.
Ballast originated as a KMP MVI framework inspired by React's Redux, adapting Kotlin features like sealed interfaces and Coroutines for safe unidirectional data flow. Under this architecture, every screen defines a strict Contract consisting of Inputs, current State, and one-shot Events.

Stock photo for illustration only, not from the actual event
Enforcing a highly opinionated code structure proves particularly advantageous when leveraging AI coding assistants like Claude Code. Because the framework demands explicit patterns and rules, AI agents can reliably generate boilerplate code without deviating from the architectural design.
By avoiding reflection and code generation tools like KSP—which face performance and debugging hurdles across Kotlin Multiplatform targets—Ballast ensures a transparent codebase that developers can easily maintain, inspect, and extend.
To manage persistent data without relying on a full SQLite database, Vesper utilizes an AppStateViewModel scoped globally to retain authentication tokens, theme preferences, and feature flags. Changes are serialized and flushed to disk using multiplatform-settings, allowing cold launches to restore prior states seamlessly.
"Claude loves structure"
A coworker introduced to Claude
Beyond UI state management, the same Contract and InputHandler paradigm powers server-side background queues via ballast-queue and cron jobs via ballast-scheduler, with inputs serialized directly into a Postgres table for reliable execution.
Source: Dev.to
Found something wrong in this article? Report an issue with this article
Comments
Leave a Comment