Offline-First Apps: Why Should Apps Be Made to Work in an Offline State?
dewsolutions.in
dewsolutions.in
I'd settle for offline-second apps that, when ostensibly having the ability work offline, can work with any sort of designed or considered experience (dismiss banners about no connection, don't hang randomly, etc.)
you must not spend much time in California
Back when I used Spotify, I would navigate to where I needed to be while I had wifi to prepare for when I had no signal, like before a run or plane flight. Else it would often just fail to load at all.
Youtube and Youtube Music at least have a deliberate "Downloads" tab which is what I want when I know that I'm offline and want to browse my downloaded media.
But it does still suck. I'd imagine the main reason Spotify choose to limit it like that is some requirement put on them by the music labels.
And be locked out of your music if either the internet or their servers are down.
The whole app is basically unusable with a spotty connection though. It is constantly attempting to do network things that block the ui.
I download music on Spotify so I can use it on the tube with no internet connectivity.
So why does the page for any downloaded album seemingly require connectivity to load FFS? How am I supposed to click on the song?
(Randomly, after half an hour often, it just works. I have no idea how or why.)
The "no connection" banner is usually still present but shouldn't block UI.
Offline mode adds a cost to every new feature, even if it's just deciding if you are going to support said feature offline.
If you're dealing with sensitive data, it also means you need a way to secure that data on the client device.
I'm not saying apps shouldn't have an offline mode, but software engineering resource isn't free and most businesses would need to justify the cost of offline (even if it's just an opportunity cost).
Not necessarily true if you can run the same code on the client as you do on the server.
Plus, you frequently don’t want to architect your client code the same as your server code (you’re not going to run a bunch of micro services on the client side for example)
Maintaining multiple sources of truth is hard, especially if multiple people are working on the same data.
What was the user load like? Performance / latency?
It does not force you to replicate your functionality.
What forces you to replicate your stack is poorly designed architecture.
You might be at a remote site, or on a plane with no Wi-Fi.
But, yeah, if those are common use cases for your app, you should absolutely be designing for offline.
Your job may literally involve entering data at a remote site with sketchy-to-no internet (mines, farms, geological surveys, etc).
You can absolutely implement offline in a web application (Google drive does this). Obviously, you need to be online to get the app in the first place, but once it’s cached, you should be good to go.
Again, it’s a cost-benefit analysis for the business.
A lot of the tech in the web world is focused around the traditional client <-> server / API model. Building a local first app feels like you're going against the grain in some ways and you're often not able to take advantage of the latest and greatest tech.
In my case I am using PouchDB as a local database, which sync to an instance of CouchDB on the server. I think there is room for improvement with this combo, but for the most part they are a mature, battle tested combo that works really well as an offline first sync solution.
One of the decision trees is you often have some API needs and can't just live within the pouch db database. Particularly account creation and such. It may need to plug into other services and initiate them, you could use a database as a message queue, but it's awkward compared to an API.
The couch/pouch models gets somewhat more complex when you leave the one db per user model. If there are shared data buckets, the database access patterns require a good deal of thought and experience with the couch db auth model. Particularly it's limitations.
Offline access also requires thought around sensitive data. If a user has had their access revoked, an offline first app may preserve it which can rub against security requirements.
Initial sync can be very slow if you're dealing with a lot of data. This can be a poor experience if you make a new user wait minutes to get started. Or you can also build a backend so they can start immediately and then switch to the local copy after replication. I've build this kind of system before.
There are advantages to the route. Minimal data transfer from the server can represent lower load for your services. You can also do an offline only model for data, potentially lowering cloud costs for the number of users you serve - a definite boon for free tiers.
The replication is pretty slick and quick, web socket like snappy data without web sockets. Using tools that do most of the heavy lifting for you and you mostly have to just plumb correctly.
From my experience most of our customers are not mobile developers (they’re JavaScript developers asked by their bosses to build mobile apps) and don’t even want to spend time learning coredata or room for iOS or Android. So most of the time they just stick to what they know, HTTP requests.
Luckily we made our SDK easy to use so that these JavaScript developers can get both the network communication and the offline first caching in one product. They absolutely love it.
Most apps aren’t offline first because it’s so hard to build the infrastructure to pull it off without a lot of bugs. Most apps focus entirely on the UI code layer. If infrastructure and frameworks made this a lot easier to use, I bet offline first would be a lot more popular and users would be a lot happier.
Right now we have our enterprise pricing nicely figured out. This might sound a bit strange but unlike many startups, we actually went to market backwards by focusing on the enterprise first. When we started the company, we had this crazy idea that we could build a distributed, query replicated, database that could sync over a mesh network. No one had ever built anything like this and we had some skepticism that such a product could actually generate revenue to create a sustainable business. Eventually, most infrastructure or database startups survive in large part to enterprise deployments. So we sought enterprise use cases and dollars first. And I have to say that was totally worth it and we've been rapidly gaining customers here.
However, in our hearts, we are all frontend web, mobile, IoT app developers that all wanted to build collaborative apps like iMessage, Figma, Miro, Trello that had offline, sync, and conflict resolution capabilities out of the box. Once we make a couple of product and ops advancements over the next could of quarters, we will have very clear pricing like any other service out there. So just hang tight!
Want us to get to pricing faster? Definitely recommend some of your infrastructure, kubernetes, and rust distributed systems developers to join us. We are hiring hardcore!
Ditto is both an embedded + cloud database + a mesh network, it's really 2 startups in 1.
A) If you're looking for just offline-first and data sync, there are some company's that have done this
1. Realm (MongoDB) - this is the company that my CoFounder and I came from. 2. Firebase (Google) - one of the biggest inspirations for me personally in data sync. 3. Supabase - a very popular growing open source Firebase alternative 4. All the GraphQL Backend-as-a-Service like Prisma, Hasura etc... These have offline caching with a lot of the GraphQL client libraries. I don't think actually have a database underneath the hood that you can query.
B) If you're looking for just mesh networks:
1. Build it yourself using Bluetooth Low Energy, Local Area Network, P2P Wi-Fi Direct, Apple Wireless Direct, Wi-Fi Aware APIs that come with most of your device frameworks. Build an advertising system, a common communication protocol, and add your identity security system. If you want multi-hop, you'll need to create a dynamic routing and presence system on top of it. After that design an API to send data around, respond to errors. If you want offline-first you should research CRDTs and try to build a database replication system using the mesh network. 2. You could use Apple's Multipeer Connectivity framework: this is iOS, MacOS devices only. No multi-hop here but you can build a system on top if it. One thing I've noticed is Apple's framework is a ruthless battery drainer. My phone gets very hot after a minute. It doesn't look like it uses Bluetooth Low Energy and it's advertising system seems to be extremely aggressive 3. Google has an abstraction called Nearby Messages that uses Bluetooth Low Energy. It isn't very stable but you could try to trick it to re-establish connections. After that you'll want to investigate how to pull off multi-hop. It's the same as step 1 and 2 https://developers.google.com/nearby/messages/overview 4. There was a company called Hypelabs that offered mesh network solution, but not the offline-first part. I'm not sure what's up with them 5. There's another company called Bridgefy https://bridgefy.me/ that built a chat app used in some of the Hong Kong protests 6. Open Garden also had Firechat in 2014
Ditto is a combination of both families of problems, it's basically creating 2 startups at the same time (mesh + distributed database):
* Offline first embedded mobile, web, IoT database called the small peer * A large distributed database in the cloud called the Big Peer (this is new and what we need to operationalize for general avaiability pricing) * A replication engine that uses our mesh network powered by Bluetooth Low Energy, Local Area Network, P2P Wi-Fi Direct, Apple Wireless Direct, Wi-Fi Aware
The problems that we have to tackle are so crazy; network optimizations, compression, multi-plexing, conflict resolution, scaling on the edge and cloud etc.... It's like the product that we're trying to create is teaching us as we build. For example one of the challenges that we have now with multi-hop is scaling performance. A large mesh of 1,000 devices may chatter so much just on the distributed routing table that it can cripple the replication of the actual data! So we are trying novel ways to dynamic route data by also incorporating special characteristics of CRDTs so that chatter is reduced and performance increases. Other major things we will improve are ways to prevent denial-of-service attacks even with trusted actors, decentralized access control of data, graph centrality theory etc...
Regarding use cases?
1. Well anything that's latency sensitive is perfect for us. Think controlling robots, syncing whiteboard pen strokes across devices, games, VR+AR. 2. Industry wise, any place where _any_ issue to internet connectivity means a loss of money, life, user experience: aviation, hospitals, point of sale, education, manufacturing, defense. A lot of our customers have internet 99.9% of the time but even that 0.1% is a nightmare that causes great issues.
> 4. All the GraphQL Backend-as-a-Service like Prisma, Hasura etc...
Just wanted to quickly drop in to clarify that Prisma is not a GraphQL-as-a-Service tool any more but an ORM that gives you a type-safe JavsScript/TypeScript client for your DB and a migration tool. The main differences between Prisma 1 and the Prisma ORM (i.e. Prisma 2+) are explained here: https://www.prisma.io/docs/guides/upgrade-guides/upgrade-fro...
You see how hard it is to change perceptions? This is why people spend so much time on branding for developer tools, databases, and infrastructure companies.
There are expressive CRDT libraries like Yjs and Automerge but these don’t give you persistence.
There are mature databases out there that include CRDTs, like Redis and Postgres EDB, but these have very limited semantics.
Ditto is a proper database with a rich set of CRDTs. It’s well engineered, the team are great and it has a compelling DX / realtime APIs.
Source: working with CRDTs at Vaxine.io
Can you go into more detail here?
I'm not sure if you know what CRDTs are but they're a family of data types that allow different actors in a distributed system to edit data concurrently even during network partitions. If enough data is shared, they will deterministically agree on the same value. They kind of give that "google docs" behavior if you're looking for an analogy. They're perfect for peer to peer and offline-first systems.
However there is actually more to it, and a much more detailed write up is coming soon. Ditto is a distributed database, each peer has it's own database. The database is organized into collections and each collection is a Ditto Document (this does not work like most NoSQL document databases). Each property of the document is it's own CRDT, you as the user can pick which CRDT you'd like to use, our current catalog includes:
* Registers (Causal Last Write Wins) * Counters (sums of each writer's numeric values) * Binary Attachments (same as a Register but you can put large arbitrary data like say video files, images, PDFs whatever) * AddWinsMaps (coming soon) - This type allows for concurrent upserting and removing of values based on a key. * ReplicatedGrowableArray - this is an array type that allows for concurrent insertions while preserving some semblance of order. It behaves rather closely to a collaborative text editor merge behavior.
Our AddWinsMap and ReplicatedGrowableArray are more special than you might think. They can actually host nested CRDTs. Think of it like a folder within a folder in Google Drive that can hold synced documents nested within.
I'd love to show you over perhaps a call! We tend to be perfectionist when it comes to documentation and have been so busy that we haven't fleshed it all out. Perhaps we might just open source our CRDT system.
Email is in my profile, I love chatting and sharing about this stuff!
This is what is required to build an app where any instance (node) can be offline for an arbitrary amount of time, but still be able to share state with the rest of the nodes when it's reconnected.
To implement this, every application node keeps a vector clock per register (an atomic piece of shared state). The vector clock allows any node the compare its own version of the register with the state received from any other node. Two values of a vector clock can either be causally related (in which case the most recent write wins) or concurrent. However the concurrency is from the system's perspective, but not necessarily from the user's perspective. An extra physical timestamp can be kept at the register level to order concurrent updates in a way consistent with the user's time perception.
Now, having the hybrid clocks in place to version each register on each node, the system must implement a protocol to ship every register update to all nodes (reliable broadcast).
Once all updates are shipped to all nodes, it's guaranteed that all nodes have the same (most recent) state.
(I built an offline-first product and had to roll my own protocol)
I've read the CRDT paper but never implemented it. Question - if you're not using LWW (instead you have concurrent values of a vector clock), this is where you have your CRDT and merge the states coming from every node?
1. its hard
2. its a significant dev cost
3. there aren't a lot of resources to help you out if you aren't already familiar with CRDTs and the like (and that assumes your backend also already supports them)
I can't help with #1 and #2, but for #3 I found Jake Archibald to be the best source of information online. Here is a great high level presentation (he really is a gem of a presenter. I always drop what I'm doing to watch anything he puts out, offline-first or otherwise): https://www.youtube.com/watch?v=cmGr0RszHc8
Also, Trello put out a multipart blog series which gets into some nuts and bolts which are really valuable when designing your own app: https://tech.trello.com/sync-architecture/
EDIT: I just noticed Jake has a full course available. I'm not sure how good it is, or how indepth, but I'm sure its decent based on the author: https://jakearchibald.com/2014/offline-cookbook/
From my experience, building an offline-first app is much easier than a traditional client-server app.
I maintain an authentication backend for PouchDB/CouchDB-based apps and have received this feedback multiple times: It only requires a little bit of frontend web dev knowledge to store and retrieve JSONs locally + sync them up, without having to worry about authentication, API design, network connection or caching.
> 2. its a significant dev cost
It allows to move much faster than a traditional setup where frontend devs might need to sit in meetings with backend devs in order to argue about some API design. But early mistakes in the data model are much harder to fix because with CouchDB, your data model is your API.
Having built apps with Pouch/Couch for > 4 years now, I think the perfect middle ground is to only accept changes made by a user through an API (proper data validation, no conflicts,...) and to use Pouch/Couch in a read-only manner, replicating all necessary user data to the client.
It's as simple as that.
Having used Apollo's GraphQL recently, it definitely has a fairly robust toolkit for handling the cached data; it's a good mini-database-like thing. It feels more than a little like having a Spring repository[1], but in the front end.
My hope is, as much as address user needs, we also as a positive side-effect get a nice well defined front-end architecture. The question of how programmers represent & handle data is fascinating, and offline-first is a more general capability we should have some story for when architecting these client-server resourceful systems.
[1] https://www.digitalocean.com/community/tutorials/spring-repo...
There are good resources online that outline the different fetch strategies (e.g. cache-first, cache-then-network, network-only) but it doesn't prevent the developer from needing to go through and make the decisions around each of those strategies and to implement them.
The only thing approaching an off the shelf solution to this is the CouchDB replication protocol. While technically good, it suffers from a number of issues if you want to use it in practice:
- dearth of cloud providers for CouchDB
- There's pouchDB for the browser but there's nothing for native mobile clients
- While PouchDB is great, it has to run on-top of browsers IndexedDB implementation, all of which suffer from reliability and capacity issues
Yeah there's IBM Cloudant and that's about it :/ but a single node CouchDB isn't hard to operate.
> There's pouchDB for the browser but there's nothing for native mobile clients
There are native clients but they aren't maintained anymore by IBM.
> While PouchDB is great, it has to run on-top of browsers IndexedDB implementation
When building an iOS/Android hybrid app with web technology, SQLite can be used as storage engine for PouchDB.
BTW, I reported the fact that their "Add the Second Wind repo to your F-Droid app" link is broken several months ago, but feel free to report it to encourage them to get that AWS problem taken care of. <3
So yes, you have a small SQL Server Express cache of data and you build around syncing data and working with it offline.
This feature has a cost and might limit the functionality - that’s why it should be implemented only when necessary.
one shining example is GPS guidance system.
> 1. Users are offline; experiencing latency issues or are in unreliable network conditions.
> 2. Fetching the data over the network will be slower than fetching it from a local source.
> 3. The app users should be informed about the low network conditions but it shouldn’t be a hindrance to their objective.
> 4. Users’ network and battery status are taken into account, and thus only the data that has changed since the last synchronization should be synced.
which itself is from RTFM (manual), which was the catch-cry of early 90s irc and usenet tech newsgroups.
I think the acronym used to represent the phrase "The F*ing Article", and then, for a softer touch, people changed F to represent 'Fine'.
A big aspect of this is data collection. Microsoft office has tons of features that could easily be local buy that it wants to be "connected services" that are subject to their own TOS so they can steal your data. Based on the lag, it seems like all spell and grammar checks go through some server somewhere, almost certainly with the primary goal of scraping as much info as they can from what you're doing. Online first is all about data harvesting, much less so ease of operation and certainty not customer experience.
And even then, I have this phone thingie that has lots of storage - why can't I sync to that directly without 3rd-party involvement? This would address you "what if it crashed" by giving a second copy (or more if there's multiple people sitting near me).
Because the app would be too easy to steal and too hard to monetize.
At this point, from an economic perspective, apps either need to be completely open source or running in the cloud behind secured servers.
Even if I dismiss piracy as a problem (and I don't), the bigger issue is that if the app does something clever/technical and is somehow successful, someone will tear it apart, clone it, and put their own copy on the app stores.
Running in the cloud is how companies are dodging that race to the bottom (see: Chitubox as a prime example--good or bad example is up to you to decide).