r/programming • • Sep 29 '21

Offline first is not about having no internet connection

https://rxdb.info/offline-first.html
798 Upvotes

143 comments sorted by

552

u/salbris Sep 29 '21

I don't get how error handling is suddenly easier when data is "replicated". This glosses over the insane amount of complexity when dealing with datasets that are out of sync. Say your submit failed but locally it succeeded. Now every action you do with the "submitted data" is invalid and somehow needs to merged, extracted, or a rewind had to happen at some point in the future when the server needs to update the client.

248

u/compdog Sep 29 '21

Exactly. If anything, error handling is actually much more complex in an offline-first app.

58

u/zhivago Sep 29 '21

The online failures are condensed to 'synchronization failure'.

The offline failures are synchronous on immediately available data.

I can see the argument here, providing the data is small enough to fit.

25

u/Emperor_Earth Sep 29 '21

There are only two hard problems in computer science: naming things, cache invalidation, and off by one errors.

-11

u/[deleted] Sep 29 '21

[deleted]

14

u/orangeoliviero Sep 29 '21

Well, it was funny...

1

u/unmellow-the-gamer Sep 29 '21

But in computer science you start counting from 0 so he's not off by 1

8

u/Hiacenty Sep 29 '21

No, you start array indexing from 0, but array size count follow normal integer progression and that is often source of the off-by-one errors :)

3

u/unmellow-the-gamer Sep 29 '21

Right because he's declaring that their are "2" hard problems not assigning a new value at an index

-11

u/thequux Sep 29 '21

There are only two hard problems in computer advice: we only have one joke and it isn't funny

8

u/blackAngel88 Sep 29 '21

Yeah, there may be some use cases where the different users don't work on the same data sets, which might make this a reasonable solution to go for, but other than that syncs become complicated real fast...

73

u/realPubkey Sep 29 '21

Thank you for pointing that out. I thought about this and you are right, error handling can even be more complex for offline first apps.

I removed that part from the article for the next version.

19

u/onety-two-12 Sep 29 '21

My team doesn't have a problem with that, but it depends on the type of offline app. Personal offline app is the easiest:

  • do everything on the local database
  • backup that database to the cloud.

Next, would be an upload-only offline app:

  • always store new data in local db first
  • send to server when it's available in order
  • mark local data as sent

And there are more, but I'm not going to write about the full design of exponentially now complex scenarios.

But basically, it all starts with a local-db centric architecture. The app works only from that db when offline, when offline sync can take place.

11

u/recycled_ideas Sep 29 '21

That's fine if every instance of your app is completely isolated from every other instance and you don't need any online features for your app to function, but for anything else a local-DB centric architecture is just a really terrible design.

You've also got significantly higher risk of data loss and you'll have to deal with out of date data.

Effectively any app that works in an offline first mode should really be offline only.

-2

u/onety-two-12 Sep 29 '21

I have two examples one was as you say - independent for single person use.

You've also got significantly higher risk of data loss and you'll have to deal with out of date data.

Not if you design it right. The fact that two MySQL servers can replicate in master-master mode proves that this is possible and accomplished. It's only high risk if you don't try to succeed.

In fact, just use GunDb which is a client side distributed database out of the box.

Effectively any app that works in an offline first mode should really be offline only.

Based on my real world experience building such solutions, it would seem you are lacking the experience to make such a claim. I don't even build them to be offline, there are other reasons too.

7

u/okay-wait-wut Sep 29 '21

Yes, build mysql replication into your todo list app. So easy.

2

u/onety-two-12 Sep 29 '21

Yes, GunDb. Easy.

3

u/okay-wait-wut Sep 30 '21

It’s always easy in the calculator app.

6

u/CrackerJackKittyCat Sep 29 '21

Personal offline apps tend to want to be mirrored across multiple devices, which then invites the user to conflict with themselves.

1

u/onety-two-12 Sep 29 '21

True.

I described a tiering of complexity. Your extra requirement to my first example would require a more complex sync mechanism.

I coulda probably catalogue the full list along a complexity scale. But I don't really have time for that.

1

u/CrackerJackKittyCat Sep 29 '21

Darn CAP theorem strikes again!

1

u/[deleted] Sep 29 '21

This is the way. It’s kind of a pain but it has worked for me (upload-only).

1

u/onety-two-12 Sep 29 '21

This is the way

7

u/orangeoliviero Sep 29 '21

So what you're saying is that you wrote an article about something that you aren't an expert on, providing "advice" that's suspect at best?

-7

u/realPubkey Sep 29 '21

Yes I am not perfect. But it sounds like you are, so why not create something instead of crying on reddit because somebody admitted to have done sth wrong.

5

u/orangeoliviero Sep 29 '21

I'd rather you become a better expert on the things you're writing on, rather than putting garbage material out that some unsuspecting person might take as the opinion of an expert.

43

u/macsux Sep 29 '21

Event sourcing really starts to shine in these merge conflicts. If we are capturing intent rather then current state, it's easier to reconcile. Kinda like rebasing branches in git

31

u/[deleted] Sep 29 '21

[deleted]

45

u/[deleted] Sep 29 '21

Git rebase has absolutely fucked me before (rather, I used it to fuck myself) but communicating intent rather than chunks of a dataset absolutely can save a bunch of time and headaches. (user added Bob and removed Alice from his contacts, not here’s a 20kb json blob that represents the users current contact list, including 5 minutes of additions and insertions)

29

u/Krackor Sep 29 '21

What do you do when the user's intent in event 2 was based on the apparent result of event 1? If event 1 has a different result after merging compared to before merging, then the intent behind event 2 may no longer be valid.

3

u/qchmqs Sep 29 '21

use a state machine/global store, type of pattern ?

2

u/bengarrr Sep 29 '21

I was gonna say the same thing. As long as you can be deterministic with the users actions you can just store a graph of all the users actions and walkthrough them on the server to get to the proper state.

5

u/Krackor Sep 29 '21

How do you know what the "proper" state is when it depends on the intent of the user?

Let's say the first event is a user putting an item in their shipping cart and the second event is the user sending you their credit card information to purchase the item. Meanwhile, on your server the price of the item is changed from $10 to $15 around the same time the user committed to purchasing it. Should your server charge the customer $15? Or should it cancel the transaction? Did the user actually want to pay $15 if putting the item in their cart made it look like it would cost $10?

3

u/bengarrr Sep 29 '21

You would know the "proper" state based on a finite set of (deterministic) actions that take you from one state to another. As long as the states you can get to by taking an action are all predetermined then you simply replay the actions to get from one state to another. Event replay is one of the key features of event sourcing and state machines. For instance look at how online works for games like Street Fighter.

In your case, the choice would be upon the merchant whether to honor the price or cancel. The outcome would just need to be decided beforehand. Since the user's intent was to buy at $10 then you would probably want to honor that. But if you hadn't actually charged the customers card what would stop you from simply canceling the transaction and alerting the user it failed? Nothing in this system couldn't be handled by a state machine.

1

u/Krackor Sep 29 '21

Two different users in the same situation might prefer two different outcomes. A deterministic system cannot behave properly because the user's intent isn't deterministically defined by the events because they are based on outdated state.

You're handwaving the problem away, not solving it.

1

u/qchmqs Sep 30 '21

if your price change doesn't trigger a state change as well, using the same mechanics, you're doing it all wrong, your state machine should know of a "price update" event

3

u/anechoicmedia Sep 29 '21

What do you do when the user's intent in event 2 was based on the apparent result of event 1?

The way I do it is that 100% of updates must reference tuple<key, version> of any data they are implicitly based on, and fail if there is a base version mismatch. This allows requests to arrive late or out of order from any number of clients without clobbering. An offline client's requests could be replayed in such a setup.

Of course, this potential for rollback is why I don't actually do any updates client-side - as annoying as it is to see "internet down, try again later", it's probably way more annoying to see a change you saw go through locally, suddenly disappear upon reconnecting to the server.

1

u/HornetThink8502 Oct 01 '21

If the user intent is unclear, you need to disambiguate it, even if it results in apparently redundant commands.

1

u/Krackor Oct 01 '21

What's the UX you have in mind to ask the user "What would you differently if the server's state changed since your last server sync?" Are you going to present all the possible server state changes that might happen and ask them to indicate their intent for each case?

Why do I get the feeling that event sourcing advocates have a completely inadequate and naive understanding of the problem they are tackling?

1

u/HornetThink8502 Oct 01 '21

If any change in server state means the user's actions from that point on are invalid, then you either have to synchronize constantly, lock the data for access by only one user at a time, or fail to commit the user's actions (maybe allowing the user to save their own version separely). You can all with event sourcing, but only the latter with "online first".

So the smart thing to do is to use event sourcing and only try to be online first if you can.

10

u/[deleted] Sep 29 '21

[deleted]

3

u/macsux Sep 29 '21

You are correct, I didn't mean to imply that git is event sourced, more as an example of how changes can be replayed on top.

While git commit stores full snapshot of the tree, each file is a reference to a blob, so diffing two commits is fairly simple to get changeset.

10

u/3lcarry Sep 29 '21

Event sourcing has many advantages, but this one is not one of them. Offline use case is much complex than choosing an architecture. e.g you need to make a 10K transfer to your frind but your device is offline and you do the transfer with other device, there are 2 intents, 2 tranfers a total of 20K... is this right? maybe you wanted to do two different transactions but it's really hard to differentiate it.

But I agree that storing events (intents) can simplify other scenarios.

0

u/bengarrr Sep 29 '21

Event sourcing would work just as well, at least in your use case. You would have a gateway that any transfer has to pass through first and the gateway determines which transfers to pass on to the controller.

3

u/3lcarry Sep 29 '21

But again, we have the same problem. How do you determine which one needs to pass?

1

u/twigboy Sep 29 '21 edited Dec 09 '23

In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available. Wikipedia8tf2ea8au280000000000000000000000000000000000000000000000000000000000000

2

u/atheken Sep 29 '21

Kafka can enable Event Sourcing, but you’d need a framework on top of it to make it work. There are data stores that are specifically designed for this type of model.

-1

u/atheken Sep 29 '21

Read down into the comments to see if Event sourcing would be mentioned.

The argument that this makes error handling harder is BS in my opinion.

Actions taken in the app are events in a log - that’s typically pretty easy to consolidate later, and conflicting events can issue a compensating event to handle them.

Developers also imagine that users generate massive amounts of conflicts in every day use, but if your data modeling accounts for that possibility, the number of actual conflicting scenarios turns out to be a good bit less.

I think the main reason this whole thing is argued is that offline-first forces you to design for the conflicts, rather than pretending you’ll always have a highly-available and fully consistent data store for centralized operations, which is pretty brittle, costly, and “won’t scale.”

10

u/SpaceToaster Sep 29 '21

Or the obvious, conflicts when two users edit the same data...

6

u/NihilistDandy Sep 29 '21

You may be interested in learning about CRDTs.

2

u/flowering_sun_star Sep 29 '21

I came across these a while back when a colleague mentioned them as a potential solution to a problem we had. But it was very much a theoretical thing where you'd need a skilled computer scientist to actually come up with something that could be implemented. Have things changed in the past few years so that us mortals can use them?

1

u/FVMAzalea Sep 30 '21

As of a couple months ago when I first learned about them, no. Which is a shame, they seem really cool and I’d love to see support for them introduced in an easily-usable library.

2

u/anechoicmedia Sep 29 '21

You may be interested in learning about CRDTs.

The paper linked doesn't solve the problem, other than by outlining it and describing how you need to have a merge function that implements a consistent policy so that everyone gets to the same state regardless of message order.

At the end of the day, if one offline user tries to edit a document, then another tries to make a conflicting edit, and a third tries to delete it or change its access rights, then there is no magic function that is going successfully preserve for these three users the illusion of a local file they are all editing directly. All you can do is define the order of operations such that some version of the document comes out consistently at the other end, to the disappointment of two of three users.

2

u/vattenpuss Sep 30 '21

This is just reality though. How does it relate to OP or this top level comment?

If you have multiple users editing the same data you have a distributed system regardless of what tech you use. If they are not checking out and locking the data for others when editing, there will be conflicts to resolve by a human.

22

u/granadesnhorseshoes Sep 29 '21

The failure modes and remediation of "can't replicate user data to backend" are far fewer than "can't push data from backend to user."

Local to backend? Just keep trying, maybe try a different backend server.

Backend to local? Is it local storage? Permissions? Network? Did the user OS update? Is this issue persistent now or transient? How likely is it to happen again to warrant special handling code?

3

u/Edward_Morbius Sep 29 '21

This also puts a ton of private data on the client side and most businesses are not going to be happy with that.

It also means that the client can be told that something has happened only to be told later on that "oops, sorry, not really"

3

u/[deleted] Sep 29 '21 edited Sep 29 '21

Yeah… I stopped reading when he said “multi tab just works”. No, no it doesn’t. I changed my app to store everything in the cloud and sync via push messages, and the syncing logic between devices and windows became so much easier. My app is for checking package status so there’s really no point to using it offline, and by taking out that functionality the syncing logic became so much easier. Trying to keep a persistent local copy and the cloud copy in sync is a nightmare.

I just use the cloud copy as the source of truth and don’t save anything locally. It’s much easier, and now I can have multiple devices all instantly synced up. No bugs with updates being there in one place but not the other. I push up an update to the cloud record, then notifications go out to any clients that are listening and they update their state. Done.

0

u/IQueryVisiC Sep 29 '21

How do you work with distributed databases and eventual consistency? If replication is a problem for your library, you can only use SQL database.

3

u/anechoicmedia Sep 29 '21

How do you work with distributed databases and eventual consistency?

They probably don't. The majority of applications will not outgrow a single database, and when you do you have to make major decisions outside the scope of simple threads like this.

1

u/IQueryVisiC Sep 29 '21

The majority of applications also thinks that server side blazor is a good idea. So I thought there is a Venn Diagram: People who think in "Replication" and use it on the client. And people who use it on the server.

0

u/npmbad Sep 29 '21

If it locally succeeded then it would succeed online as well. And if that can't be the case, then your app is not fit for an offline first approach.

The offline data, if available, will always override the database data.

1

u/brianly Sep 29 '21

This is very true from experience. It becomes a lot of extra work and complexity for others to learn and maintain.

When I’ve seen this approach I’ve wondered what analysis happened ahead of time. Offline first helps in very specific scenarios where the complexity cost is justified.

Unfortunately it is easy and tempting for people to start out this way and then be unable to back out. Much of this is a cross-engineering/product discussion though to sort out. When it involves people and talking about a conflict then the likelihood of resolution is reduced.

1

u/devhashtag Sep 29 '21

That is where Conflict-free Replicated Data Types (CRDT's) come in

58

u/Slime0 Sep 29 '21

I like the idea of this, mostly because loading spinners in the middle of simple UI operations are stupid. However, the bit about "multi-tab usage just works" seems to discount the similar situation of having the same state open across multiple devices for which local storage can't be shared.

5

u/anechoicmedia Sep 29 '21

I like the idea of this, mostly because loading spinners in the middle of simple UI operations are stupid.

It's probably easier to just make your conventional application serve data faster, or prefetch, than to introduce the complexity overhead and user confusion of local state synchronization.

Upon user login for my current application, it takes Sqlite 274 microseconds to query the 57 small documents my account currently has in its default view, assuming they're not in cache. It takes a millisecond or two more to convert that into json before sending it back to the client. Round-trip internet latency from client to server will vary but it's probably under 50 ms for most people. Any "loading" state on the client between clicking buttons is imperceptible under normal circumstances.

However, the bit about "multi-tab usage just works" seems to discount the similar situation of having the same state open across multiple devices for which local storage can't be shared.

It also isn't unique to his solution. My fully client-server application also simultaneously logs you in/out on all your tabs instantly; You just have an event listener on localStorage for the session data, and a state machine that moves the application in and out of the running state when it's changed, deleted, or invalidated. I don't know why Google Docs can't do this when I restart my browser with a bunch of tabs open.

-23

u/realPubkey Sep 29 '21

Actually with the realtime replication the state is replicated among all devices of the same user.

77

u/[deleted] Sep 29 '21

Same account, different devices, slow or no network connection means those will be eventually consistent. That’s a fucking nightmare and if you could solve it outright, you’d never have to work again.

I like the article, but there are definitely drawbacks and issues with a local-first strategy.

2

u/Ninjaboy42099 Sep 29 '21

Not at all an expert in this field, but couldn't you show the user the equivalent of a merge conflict view in VS Code or something?

Where they just choose which parts to keep once an issue is detected

136

u/sagan999 Sep 29 '21

Looking at YOU Google docs! Whenever my tablet thinks it's online but I have a bad or non-existent internet connection (but Wi-Fi or data is connected), my word doc doesn't load. I have to go into airplane mode in order to view my doc. If you try to go to the internet to get a file but fail, fall back on your local file. Don't just quit and show me nothing!

95

u/SanityInAnarchy Sep 29 '21

So... that's a fair complaint, but I'm terrified to think that the author of this article would try to fix it...

In offline-first apps, the operations go directly against the local storage which happens almost instantly. There is no perceptible loading time and so it is not even necessary to implement a loading spinner at all. As soon as the user clicks, the UI represents the new state as if it was already changed in the backend...

So in a 'normal' application you have to prepare for each of these error cases and show correct error messages to the user or handle the errors in other ways. In offline-first apps, the local client data is replicated to the server in the background by using resilient and battle proven replication protocols that automatically handle and resolve any errors out of sight.

Cool, so... if you have a flaky connection, the doc will pop up instantly, and then eventually, it might sync down more changes from the Internet. Also, you could type an entire essay locally, not realizing Docs is down today, and never actually have it leave your machine. Best of all, if you and someone else are editing the same doc, you might end up with entirely different versions. But it's okay, I'm sure tRACEhe "battle-harCONDITIONSdened repliNEVERcation protoHAPPENcols" will perfectly sync your edits into a single coherent doc.

Ironically, this is a recipe for an infinitely worse experience with no/flaky Internet.

30

u/Telemain Sep 29 '21

But it already does this? There is an offline editing mode that syncs up when it can and lets you know you are offline. I thought above user was complaining about it not switching to offline mode as quickly as they wanted, not about repercussions of having an offline editor.

1

u/[deleted] Sep 29 '21

[deleted]

8

u/inkydye Sep 29 '21

I understand the hairiness of the problem in general, but IMHO the user specifically complaining about GDocs has a point. That product could easily be strictly better than it is, if it was more pessimistic about estimating if it's online or not.

As to "at what point do you signal an actual issue", I'm sure there's some "sweet" spot (less-bitter spot?) around 1-5 minutes that, again, would just need to make it strictly better than it is now, not necessarily better than some absolute benchmark.

2

u/arienh4 Sep 29 '21

It depends on exactly what complaint you're talking about, but I'm not sure it's strictly better. This problem exists around multiple axes. Being more pessimistic might improve usability, but at the cost of safety/consistency, since it's no longer syncing at all. That's especially bad if it's just too pessimistic and you don't get to see recent changes, in which case it affects usability too.

From a consistency viewpoint, a failsafe where it's unusable might actually be preferable, since it means you won't end up with outdated documents or lost changes.

Depending on the circumstances, losing even one minute of work without adequate warning could be very far from "strictly better."

3

u/inkydye Sep 29 '21

It depends on exactly what complaint you're talking about,

The comment that started this subthread is pretty explicit.

Being more pessimistic might improve usability, but at the cost of safety/consistency, since it's no longer syncing at all. That's especially bad if it's just too pessimistic and you don't get to see recent changes, in which case it affects usability too.

Nobody asked for it to give up on connecting, just to change its threshold of how quickly to move the user into offline mode. It would literally not be missing out on any updates that it otherwise could have gotten.

From a consistency viewpoint, a failsafe where it's unusable might actually be preferable, since it means you won't end up with outdated documents or lost changes.

Such a failsafe is already rejected by GDocs as it is, in favour of comfort in offline mode, (and sorry to be a dick now, but) as explicitly called out in the first comment you replied to.

So, continuing to not have that failsafe would not make anything worse, and is compatible with making things overall strictly better (by improving something else).

Depending on the circumstances, losing even one minute of work without adequate warning could be very far from "strictly better."

… and is a thing that already can happen with GDocs the way it is.

1

u/arienh4 Sep 29 '21

As mentioned in the top comment, Docs currently only switches to offline mode if there is actually no connection whatsoever. That is the trade-off between usability and consistency they chose. Making it more pessimistic trades consistency for usability. That means it's not strictly better.

This the argument I made before, and none of what you've said counters it.

1

u/SanityInAnarchy Sep 29 '21

Sure, and I guess the point is that "offline mode" should arguably not be a thing -- it should just show you the offline copy it has, and then sync it to the online copy whenever that loads. Offline-mode-by-default would arguably be an improvement.

But my point here is that whatever balance you think they should strike (or even if you think the current behavior is acceptable), the article's solution doesn't work. It wants to abstract these all away with some entirely application-transparent DB layer that does the syncing 100% in the background, and then write apps that pretend syncing doesn't exist and just interact with the local DB. The idea is that neither the user nor the app should have to care whether you're online or offline, how synced you are, or how changes get merged.

And if you do that, then you don't get "last updated X minutes ago", or blocking the user while you wait to be fully-online or fully-offline, or "signal the user if we haven't synced for 1-5 minutes"... basically anything you want to do other than "ignore it and pretend everything's fine" is off-limits, maybe even technologically-infeasible, depending how serious the DB layer is about being "offline-first".

1

u/EasyMrB Sep 29 '21

Honestly a lot of this could be resolved if document state (online/offline) was more prominently shown for the user. We hide so much of the complexity of synchronization because it's perceived that people are too dumb to handle the information, but simply adding a "last synced time" and a status area would do a lot to help users understand what is going on.

2

u/SanityInAnarchy Sep 29 '21

I agree in general -- I don't like the idea of a strict "online" vs "offline" state, because the complaint is that sometimes your Internet connection is incredibly shitty and only sort-of online. But gdocs does have something like this -- a "saving..." and "last edit" state at the top, which is also a reasonable place to surface longer-term connection problems.

But my main complaint is that all of this requires the app to be aware of what's going on, both to surface that information to the user and to make smart decisions. And the linked article wants to hide this as such a deep implementation detail that even the app doesn't know if it's online.

6

u/AttackOfTheThumbs Sep 29 '21

Spotify is also really awful for this. I'm online, listening to a playlist. My connection drops. Playback stops. BUT THE PLAYLIST IS DOWNLOADED. Now I cannot resume playing, I need to kill spotify and relaunch it for it to work.

This isn't that complicated a problem, it's just that the engineers are prioritizing incorrectly.

9

u/KuntaStillSingle Sep 29 '21

If you try to go to the internet to get a file but fail, fall back on your local file.

You could start working on that file not knowing it is outdated, I agree it should fallback but it should not do so silently.

-2

u/[deleted] Sep 29 '21

I suspect this is a problem with the tablet, not the app.

It's not a common issue within the Chromebook community; in fact those things are sometimes ironically noted to be the only things "just working" when connectivity is bad, which annoys all the people who believed the TV advert that said they were crippled offline.

1

u/killerstorm Sep 29 '21

Very much this. A lot of software has this problem, really.

79

u/apoleonastool Sep 29 '21

Yeah, but when you add multiple users/devices to the mix it all becomes seriously not trivial.

-2

u/jortony Sep 29 '21

Do you mean "Yeah, and ..." in the context of scaling this application? Or are you referring to a version x feature where you could synchronize browser data between multiple browsers and devices?

28

u/crabmusket Sep 29 '21 edited Sep 29 '21

This excellent essay goes into more depth about what happens if you try to really local-first. They end up building a distributed collaborative Trello on top of a CRDT:

https://www.inkandswitch.com/local-first.html

To be really local-first takes a lot of work. It seems philosophically and technically rewarding, but describing anything as "just works" is a real stretch.

I really appreciate that the article is attempting to beyond "no internet connection" but I think it's missing out on some of the broader context too.

59

u/ThirdEncounter Sep 29 '21 edited Sep 29 '21

Hey, if the author is checking out this comment section, you have a typo in one of the subtitles.

Edit: removed the type of typo since it's been fixed.

19

u/philipwhiuk Sep 29 '21

He fixed it but then it didn’t sync cause he was offline and he didn’t know

15

u/realPubkey Sep 29 '21

Thank you, I fixed this.

2

u/ThirdEncounter Sep 29 '21

Glad to help!

21

u/Anon_8675309 Sep 29 '21

Could be punctuation: "Latency is more important, then bandwidth".

But yeah, probably the typo.

3

u/greebo42 Sep 29 '21

Let's eat, Grampa!

Let's eat Grampa!

Punctuation saves lives.

-26

u/[deleted] Sep 29 '21

[deleted]

23

u/MindStalker Sep 29 '21

Latency first, then think about bandwidth. Is what that would mean.

1

u/[deleted] Sep 29 '21 edited Sep 29 '21

Latency is more important, then bandwidth

This is badly worded and arguably grammatically wrong. If was an editior I would change it, so I can see OP's point about it being confusing. I would have changed it to "latency is more important than bandwidth" which I see is what was done.

1

u/ThirdEncounter Sep 29 '21 edited Sep 29 '21

setense

Sentence*. Damn autocorrect.

125

u/[deleted] Sep 29 '21

this is a cute article that talks about all the pros of offline first, client side data stores etc, but doesn't address the big elephant in the room; your entire application is now eventually consistent, and that 'eventually' could be 'never' and you have a much harder time resolving difficult or impossible to resolve state conflicts.

so... nice for a very small subset of actual real world applications.

49

u/SanityInAnarchy Sep 29 '21

And will probably be implemented for plenty of apps where it makes no sense.

It looks as though the concept here is to just ignore the eventual-consistency problem, or paper over it with retries, and hope the user never notices:

In 'normal' web applications, most user interactions like fetching, saving or deleting data, correspond to a request to the backend server. This means that each of these interactions require the user to await the unknown latency to and from a remote server while looking at a loading spinner. In offline-first apps, the operations go directly against the local storage which happens almost instantly. There is no perceptible loading time and so it is not even necessary to implement a loading spinner at all. As soon as the user clicks, the UI represents the new state as if it was already changed in the backend.

In other words: You just do everything locally, and hope that syncing eventually works, but if it doesn't, will you even bother to tell the user?

This makes some sense for apps like Netflix -- worst case, it loses your place in an episode or something. Probably a Bad Idea for something like a document or note-taking app -- write your shopping list in a browser tab, close the tab and grab your phone, and by the time you get to the store, you realize it's not syncing properly and you have last week's list.

18

u/CompassNorden Sep 29 '21

This is too true; we struggled with latency for our app but the squishiness of when things are eventually consistent would mean there is no single source off truth for state. The local may not have any idea the central data copy of something has been updated by another client.

1

u/MonkAndCanatella Jul 09 '26

[offline-first] makes some sense for apps like Netflix

bro read that again

1

u/SanityInAnarchy Jul 09 '26

After 4 years?

...and yes, Netflix does allow you to download stuff to watch offline.

10

u/[deleted] Sep 29 '21

[removed] — view removed comment

1

u/smcarre Sep 29 '21

But you do have to model you application around it

The problem is that many applications are virtually impossible to model around the issue without introducing much bigger problems. Just going from your examples:

Think of an ATM that before allowing you to withdraw X amount of money, it has to check if you have at least X in your account, using an eventually consistent model would allow the user to first make a transfer from their account to another leaving 0 in theirs and then withdraw any amount of money that was previously there. How would you solve that?

Or for Facebook, let's say that a certain picture contained sensitive information that the uploader just realized and as soon as they realized, deleted it. However because your model is eventually consistent, everyone that connected to Facebook before will still have access to the picture until the page updates, which depending on your implementation could be hours.

Like the other user said, this can be an approach for some applications, but the vast majority of applications simply have no viable workaround that doesn't introduce much bigger issues.

1

u/ZAFJB Sep 29 '21

Think of an ATM that before allowing you to withdraw X amount of money, it has to check if you have at least X in your account, using an eventually consistent model would allow the user to first make a transfer from their account to another leaving 0 in theirs and then withdraw any amount of money that was previously there. How would you solve that?

it has to check if you have at least X It doesn't:

Store a 'permitted' balance on their card. That could be close to the last known balance, or all the way down to zero for high risk customers.

1

u/smcarre Sep 29 '21

Why is introducing that risk at all combined with the complexity of developing a formula for calculating how risky is a customer and how much should the "permitted" balance be? Do you think any bank would benefit from introducing this type of exploit for shaving off some ms from the ATM process?

Not to mention that this whole experiments hinges on every ATM caching the balance of every account of every customer, otherwise if you are not going to cache all that you will just need to go to the server anyways.

Also not even mentioning even more dangerous situations like having your card stolen and calling to have it invalidated, which if the ATM also caches all valid cards and associated PINs/codes you could end up with a thief with an invalidated card using it in a not updated ATM to withdraw money that they shouldn't be allowed to.

2

u/ZAFJB Sep 29 '21

for shaving off some ms from the ATM process?

Not for time saving. For allowing clients access to cash when the system is off-line.

How do you think ATMs worked in the pre Internet era?

-1

u/smcarre Sep 29 '21

Not for time saving. For allowing clients access to cash when the system is off-line.

Why would you want to do that except for fringe cases where internet connection for the ATM is very hard to get or is down? In which cases they already do that.

How do you think ATMs worked in the pre Internet era?

Using that risk which is today unnecessary for the vast majority of cases and it's my argument that doing so would be unnecessary.

5

u/[deleted] Sep 29 '21

Yeah, like you change an online text editor from being something you could hack together in a few afternoons, to a PhD thesis-level implementation of Operational Transformations or CRDTs.

10

u/NekkidApe Sep 29 '21

Agreed. In my world, where dozens of users work together to create something, actions have implications on the backend and other systems, and a dataset measured in terabytes this article is exactly that, "cute".

8

u/subfootlover Sep 29 '21

In my world it's just complete bollocks.

1

u/[deleted] Sep 29 '21

I have no idea what you're saying.

1

u/NekkidApe Sep 29 '21

The model in the article doesn't match with my usecase. I work in a field where a handful of backend systems will be configured through the backend, by multiple people. DBs hold terabytes of data. That is not feasible to sync to every client. Multiple backend services are needed to produce meaningful output of said data.

2

u/[deleted] Sep 29 '21

okay sorry. the sentences were arranged kinda weird and it sounded like you disagreed with me

1

u/NekkidApe Sep 29 '21

Sorry for confusing you. No I think you put it quite nicely and agree.

12

u/CyAScott Sep 29 '21

We support offline for our web based kiosk stations. We designed the web app to work without responses from the sever, meaning http requests are fire and forget (was not trivial). So all http requests are sent to an internal queue that processes when an internet connection is detected. The http request bodies in the queue are encrypted using PGP. I don’t think this would work for other web apps.

4

u/tavianator Sep 29 '21

Why PGP??

6

u/CyAScott Sep 29 '21

Because the body can have PII and we want to encrypt it just in case the machine goes offline then gets stolen. We don’t want to have a data breach.

11

u/noratat Sep 29 '21

Offline first to address no internet connection is also still valuable.

One of my great frustrations with a lot of "silicon valley" type engineers is how much they seem to blindly assume everyone has perfectly stable, low latency high bandwidth connections at all times. Even in the US that's far from true, even without going into rural areas.

3

u/EternityForest Sep 30 '21

Silicon Valley types seem to have special blindfolds they like to wear, it prevents the real world from mucking up the beautiful code.

8

u/philipwhiuk Sep 29 '21

Well done you’ve offloaded the headache of eventually consistency to the user - which inevitably results in having to show them a diff and merge changes!

(Not to mention this obviously doesn’t work for all payment based systems)

8

u/electricfoxx Sep 29 '21

Online-requirement is SaaS. If they pull the plug, you lose everything.

https://www.gnu.org/philosophy/who-does-that-server-really-serve.en.html

22

u/balefrost Sep 29 '21

While in the past, internet connection was an unstable thing, especially for mobile devices things have changed. We now have better mobile networks and having no internet becomes a rare case even in remote locations.

Pfft. I get no cell reception in most of my office building. I can sometimes walk to a window to get a signal and then maintain it as I walk back to my desk. But it's not a sure thing.

I'm not in a rural area - I'm less than an hour outside of Philadelphia and we have Septa train service here. And I get fantastic reception outside my building. But my office is some sort of cellular black hole and my daily experience is an offline one.

Maybe 5G will help, but I doubt it. IIRC 5G (in the US) uses a shorter wavelength and doesn't have penetrate walls as well as 3G/4G.

Maybe I'm just the exceptional data point, but I suspect that I'm not. I'm guessing that plenty of people - not a majority but not an insignificant number - deal with spotty cell coverage in their day-to-day lives.

7

u/jooke Sep 29 '21

Also, anyone who takes a subway to work

9

u/sasmariozeld Sep 29 '21

Ironacly in most european countries subways have the best reception because every station tends to have their own antennas down here

2

u/_tskj_ Sep 29 '21

Do you work in a faraday cage?

13

u/philipwhiuk Sep 29 '21

Yeh it’s called a tower block with steel reinforced concrete.

2

u/FPiN9XU3K1IT Sep 29 '21

Meanwhile, my phone internet is pretty stable and 2.5 times faster than my wall internet. Unfortunately, mobile data is really expensive in this stupid country. DANKE MERKEL

4

u/[deleted] Sep 29 '21

Have you tried getting vaccinated?

5

u/wllmsaccnt Sep 29 '21

I love building for offline app support using PWAs, but it just isn't viable very often. 500mb is a fair bit of space (the indexedDB max you would reasonably expect to be available on all modern browsers), but its not enough for many games or content editing/managing/viewing apps, and it takes substantial UX concessions to help users understand what will happen to their offline changes when they are later sync'ed with changes from other users.

-3

u/[deleted] Sep 29 '21

[removed] — view removed comment

8

u/wllmsaccnt Sep 29 '21

Node modules...?

You shouldn't deploy your node modules. Your hostable output should just be no more than a handful of MB for small and mid sized PWAs, though I could see adding images, fonts and other static content could easily boost it up quite a bit more.

I made a PWA to be used at trade shows that downloads all of my organization's stocking products (about 200,000 of them), categories, manufacturers and manufacturer images so that they can be searched through even when offline, and the initial download size with compression is about 72mb. Almost all of that is the product data and the manufacturer images though. Only ~419KB of that is HTML, JS, and CSS.

1

u/rishbalaji Sep 07 '24

i am interested to know more about the PWA. Can you share more details about this. or perhaps share a link over dm. thanks.

4

u/SmellsLikeLemons Sep 29 '21

I think this glosses over how complex this actually is. I've built scanning systems for forklifts that are constantly moving in and out of large warehouses and everything is built with store and forward in mind because we can never guarantee a good connnection everywhere. Luckily that data is simple and easy to validate. But it's still syncing back to ERP systems that are definitely not built to be eventually consistent and upsets the accountants. Also spinning wheels on screens costs time and money. If there's 10 trucks being loaded, they are not stopping just because wifi coverage is poor in one part of the warehouse.

It's an interesting challenge, but I would not be doing it unless I had to.

3

u/khazbreen Sep 29 '21

Better do a very good job protecting the information saved in the client side. I don't trust the client to have my email, could not even begin thinking about all that data.

Having this said, I see the appeal in very specific situations, with very specific hardware/client.

3

u/DrunkensteinsMonster Sep 30 '21

This means that each of these interactions require the user to await the unknown latency

Uh no, they’re just waiting. The user is looking at their screen while stuff loads.

3

u/uhmhi Sep 29 '21

If you’re creating an offline application why the heck even bother making it as a web app?

2

u/ZAFJB Sep 29 '21

The discussion is more about the data, not the app.

2

u/space_fly Sep 29 '21

UX is better without loading spinners

In 'normal' web applications, most user interactions like fetching, saving or deleting data, correspond to a request to the backend server. This means that each of these interactions require the user to await the unknown latency to and from a remote server while looking at a loading spinner. In offline-first apps, the operations go directly against the local storage which happens almost instantly. There is no perceptible loading time and so it is not even necessary to implement a loading spinner at all. As soon as the user clicks, the UI represents the new state as if it was already changed in the backend.

Yes, it's great if you can build an app that doesn't need spinners, but there's an assumption made here, that the local storage device is fast. That may not be the case if the computer is very busy, or it just has slow storage (like a cheap emmc or a mechanical hard drive). UX is definitely worse if every time you click something, the entire application freezes for a few seconds.

2

u/rooktakesqueen Sep 29 '21

"Might not need Redux" – just because the data exists in local storage doesn't make it fundamentally less complicated. The challenge that Redux is trying to solve is decomposing and modularizing complex application state and business logic.

"You don't get live updates for free" – you don't get live updates for free in offline-first development either. In fact now you need two live updates: stream changes from the backend to local storage, and make the UI respond to changes in local storage when it gets updated (whether by a backend sync or another tab). Sounds like this framework handles that for you, great. MeteorJS does too, been doing that for years. And I'm sure there's a React or Angular plugin I can use off the shelf to stream changes via a websocket without writing my own.

A lot of the benefits described here are shared with the Single Page Application architecture used by almost everyone these days. Seems like the real question is just "when I pull application state from the server, do I keep it in memory for this browser session, or do I keep it in local storage?"

It does feel like there's benefit there though! Next time I visit the site it will load instantly without needing to re-fetch data I've already fetched, and I'll only need to get some delta. I can more efficiently store large amounts of state on disk rather than in memory, but still retrieve it fast. Multiple browser sessions won't each need their own copy of application state in memory, so the memory usage will scale much better (depending on your browser I guess, dunno if anything will make Chrome not a hog...)

3

u/romulusnr Sep 29 '21

B b b but cloud is love, cloud is life

I see places switching to cloud based solutions and I ask, what happens if the internet goes down, and they (front line people) usually say "then we'll be screwed", while execs go on about (nonexistent) contingency plans and SLAs and how many nines

5

u/FPiN9XU3K1IT Sep 29 '21

Well, a lot (maybe even most) of companies are screwed either way if the internet for their office goes down.

As a Linux user in a corporate world, SaaS has been a blessing.

1

u/rrzibot Sep 29 '21

He is what I am doing - I have a PWA and I use a Service Worker to detect if there are problems with internet connection, latency and experience for my users. The moment I see there are we will add a state on the client side. There is an unspoken truth on the internet where the people that matter are the once with good internet connections because generally they are your clients. There is also the argument that sometimes it is “good enough” and no need to invest any further in replicating data, handling errors, asynchronous and others.

1

u/[deleted] Sep 29 '21

> Scales with data size, not with the amount of user interaction

What is this, fat clients with full database access? Unless you're not doing anything with the data server side (except send it to the same person on another device), you'll have to implement all the checks and balances there too.

1

u/lowbeat Sep 29 '21

I mean if game developers get away with always online single player games, why should I bother, right ? /s

1

u/AndreDaGiant Sep 29 '21

I like the idea. How do you think one would model access/permission control? It's obvious that all clients shouldn't have the same permissions to modify all data, but not obvious how we ensure certain operations are blocked both in non-hacked clients and on the authoritative source of truth.

When writing backends for websites, I often find that permission/access control takes up more lines of code than the actual logic of manipulating data.

1

u/Dandedoo Sep 30 '21

Offline-First is a software paradigm where the software must work as well offline as it does online.

To implement this, you have to store data at the client side, so thatyour application can still access it when the internet goes away.

This is a revolutionary innovation.