Offline First Isn't Online Second

27/07/2026

Petteri Sulonen

Co-Founder, CEO & CTO

Tarinoi is conceived first and foremost as a tool for collaboration – between writers, producers, artists, designers, programmers, and everyone else involved in turning an idea into a shipped game. We've built in pretty sophisticated mechanisms for reconciling state between users, both in real time and after time delays. Our Tier 2 will be all about features supporting, organising, and facilitating this kind of collaboration. Why, then, did we choose to build it with an offline-first architecture, and what does that even mean?

Offline-first: what it says on the label

Offline-first is a very literal term. It means that when Tarinoi starts up, it loads its state from its offline cache first. Only then does it attempt to connect to the server and sync up with it. If the server is unreachable, you'll be able to continue working as normal on the data that's been previously downloaded to your device. Same thing if connectivity is lost while you're working: everything you do will be written into your offline cache, and when you're back online, your state will be reconciled with the server state.

The obvious benefit of this is that you can go offline for extended periods of time – on a plane or a train, or even for a few days away from the Internet altogether. However, in today's almost-always-online world, by itself, this would probably not be sufficient justification to build the plumbing an offline-first architecture needs. It turns out that the side benefits of offline-first are actually more impactful for everyday use.

Sometimes you just have to go look at some trees. If you take Tarinoi along, it will continue to keep you company.

Offline-first, online-better

Most webapps have a clean client/server split. The data lives on the server, the client fetches, displays, and modifies it when needed. This architecture has a lot of advantages – it's simple to reason about, the only client/server compatibility problem you'll need to worry about is protocol incompatibility, any desynchronisation between the client and the server will self-correct as fresh data is accessed, the server can take care of reconciling state between clients, and you have a clean, simple API between the user interface and the data layer.

However, there are trade-offs that become more and more significant as the scale and complexity of the data you're working on grows. Downloading all the data you need every time is fine if it's a few hundred kB or even a few MB, but a big project can grow into tens or even hundreds of megabytes. If this data involves semantic relations – cards connected to each other, entities on cards, templates defining card behaviours, collections grouping them – it becomes increasingly complex, and slow, to ensure that everything you need actually is available. The more of this kind of thing you do, the more constrained the user experience becomes by the speed and quality of your connection. A patchy connection makes the entire app flake out, slow down, and feel unreliable. You even risk losing work.

The client-side cache and eventual consistency

The solution to these problems is to cache the data fetched from the server locally. All your edits go into the cache, and then the cache is synced with the server – your edits get pushed to it, others' edits get pulled back. It sounds simple in principle, but in practice there are lots of complications. Suddenly you're dealing with an eventually consistent distributed system – each client has its own state, and there's a mechanism for reconciling their states with each other and the server, even as users make changes in each of them independently. How fine-grained is your state reconciliation? Individual documents? Individual fields? What happens if two users edit the same data at the same time? Who does the reconciling? Do you reconcile timelines, or state? How do you verify that clients are in sync with the server? How do you deal with de-syncs? What are the escape hatches if something does go horribly wrong? How do you detect client/server corruption in the first place?

These aren't trivial problems to solve, but fortunately Tarinoi isn't the first system to encounter them – it's not even the first system like this that I've built – and there is a lot of prior art and known best practices to approach all of them. That was where we started building Tarinoi, right from the data structures and protocols up.

Once the local cache with its reconciliation mechanism is working and wired up, the benefits are obvious and immediate. Your projects load instantly, since they're already downloaded. Loading a board – even a very big one with thousands of cards – takes a fraction of a second, since it comes from a local database rather than over your connection. Your app won't start to feel slow or unreliable if your connection drops or slows. If you go offline, you won't lose any data; it'll all be saved into your local cache, and it'll get synced with the server next time you come online. Unless you happen to be working on something that actually needs a live connection – managing your organisation, having a live collaboration session with a co-writer, etc. – you most likely won't even notice.

From client-side cache to offline-first

Implementing a robust client-side cache with a reconciliation mechanism is actually the hardest part of making an offline-first app. Once we're able to reliably reconcile client state in a way that minimises the risk of lost work and guarantees eventual consistency, the rest of it is pretty straightforward – cache user preferences, session state, and any registries as may be needed, and then load these from the cache first, before refreshing them from the server.

An offline-first architecture with robust state reconciliation isn't a silver bullet that will magically solve every conceivable problem you might run into when working concurrently on something, and the more people are involved, the more important it is to continuously sync, but it will dramatically improve reliability, scale, and the way the app feels when connectivity is patchy or missing – and with a bit of coordination, working offline for even several days won't blow anything up. That part is almost a side benefit, however. As paradoxical as it may seem, an offline-first architecture's main benefit is that it makes the default online state better.

#features
#updates