{"id":1177,"date":"2026-04-09T10:00:00","date_gmt":"2026-04-09T09:00:00","guid":{"rendered":"https:\/\/wade.one\/blog\/?p=1177"},"modified":"2026-04-09T10:00:00","modified_gmt":"2026-04-09T09:00:00","slug":"offline-support-sounds-smaller-than-it-is","status":"publish","type":"post","link":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/","title":{"rendered":"Offline Support Sounds Smaller Than It Is"},"content":{"rendered":"<p>Offline support sounds smaller than it is.<\/p>\n<p>People often describe it like a checkbox: cache some data locally, let the app keep working, sync it later, done. I understand why that sounds reasonable. On the surface, it is just another product feature. In practice, it changes how the app, the backend, and the user all have to behave.<\/p>\n<p>The first problem is that &#8220;offline&#8221; is not one feature. It is a bunch of features pretending to be one feature.<\/p>\n<p>You have to decide what can be read offline, what can be edited offline, what happens when the user comes back online, how long stale data is acceptable, what gets retried automatically, and what should force a hard failure. Each of those decisions creates a new edge case. If you do not define them properly, the app will define them for you in production.<\/p>\n<h2>Local data is the easy part<\/h2>\n<p>Storing data locally is not the hard bit.<\/p>\n<p>The hard bit is deciding what that data means once it is no longer current.<\/p>\n<p>If a user edits something offline, do you treat the local version as the source of truth until sync completes? If the server has changed in the meantime, do you merge, overwrite, reject, or prompt? If the app shows old data, is that acceptable, or does it need a visible &#8220;last updated&#8221; signal? These are not edge cases. They are the actual product.<\/p>\n<p>I have seen plenty of teams underestimate this because local storage feels like an implementation detail. It is not. Once the app can operate without the server, every stale value becomes a potential disagreement between reality and the UI.<\/p>\n<h2>Sync is where the pain starts<\/h2>\n<p>Sync sounds straightforward until you try to make it reliable.<\/p>\n<p>You need retry rules that do not create duplicate writes. You need idempotency in places that were never designed for it. You need to handle partial failure, because one request may succeed while the next one dies. You need conflict resolution that is good enough for real users, not just neat on paper.<\/p>\n<p>That is before you deal with retry storms, out-of-order updates, and queued actions that sit around long enough to become wrong.<\/p>\n<p>The backend has to change too. A normal API often assumes the client is online, current, and able to ask again immediately if something fails. Offline support breaks that assumption. Suddenly the server has to tolerate delayed writes, repeated writes, stale reads, and data arriving in a different order than the user created it.<\/p>\n<p>That is a real design change, not a frontend tweak.<\/p>\n<h2>Users expect it to feel simple<\/h2>\n<p>Users do not care that offline support is complicated.<\/p>\n<p>They just want the app not to feel broken. That is the dangerous part. If the feature is done badly, the app can still look fine most of the time while quietly creating bad data or confusing people about what actually happened.<\/p>\n<p>If the user taps a button offline, you need to make it obvious whether that action is pending, saved locally, rejected, or already synced. If you hide that state, people will assume the app has lied to them. They are usually right.<\/p>\n<p>That is why offline support needs good feedback, not just good storage. Pending states, conflict messages, retry indicators, and sync status are part of the feature. Without them, the app becomes hard to trust.<\/p>\n<h2>The real question is whether you need it<\/h2>\n<p>I am not saying offline support is a bad idea.<\/p>\n<p>Sometimes it is exactly the right call. If people genuinely need to keep working in poor network conditions, the feature is worth the extra complexity. But I think teams often add it because it sounds user-friendly, without first asking how much correctness they are willing to trade for convenience.<\/p>\n<p>My default questions are simple:<\/p>\n<ul>\n<li>What should still work without a connection?<\/li>\n<li>What can safely wait?<\/li>\n<li>What happens when offline changes collide with server changes?<\/li>\n<li>What do we tell the user when the app cannot finish the job yet?<\/li>\n<li>How much extra backend complexity are we willing to carry for this?<\/li>\n<\/ul>\n<p>If you cannot answer those clearly, the implementation will answer them for you later.<\/p>\n<p>Offline support is useful, but it is not a small feature. It is a systems problem with a UI attached to it. The sooner you treat it that way, the less painful it becomes.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Offline support looks like a simple feature request until you have to deal with stale data, sync conflicts, retry rules, and user expectations. The hard part is not storing data locally. It is keeping the whole system honest.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[13,25],"tags":[87,84,85,86,72],"class_list":["post-1177","post","type-post","status-publish","format-standard","hentry","category-programming","category-software-engineer","tag-mobile","tag-offline","tag-retries","tag-state-management","tag-sync"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Offline Support Sounds Smaller Than It Is - wade.one<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Offline Support Sounds Smaller Than It Is - wade.one\" \/>\n<meta property=\"og:description\" content=\"Offline support looks like a simple feature request until you have to deal with stale data, sync conflicts, retry rules, and user expectations. The hard part is not storing data locally. It is keeping the whole system honest.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/\" \/>\n<meta property=\"og:site_name\" content=\"wade.one\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-09T09:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/Wade-Logo-cropped.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1518\" \/>\n\t<meta property=\"og:image:height\" content=\"1506\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@wadewomersley\" \/>\n<meta name=\"twitter:site\" content=\"@wadewomersley\" \/>\n<meta name=\"twitter:label1\" content=\"Estimated reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/04\\\/09\\\/offline-support-sounds-smaller-than-it-is\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/04\\\/09\\\/offline-support-sounds-smaller-than-it-is\\\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Offline Support Sounds Smaller Than It Is\",\"datePublished\":\"2026-04-09T09:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/04\\\/09\\\/offline-support-sounds-smaller-than-it-is\\\/\"},\"wordCount\":734,\"publisher\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#\\\/schema\\\/person\\\/8b4739f8f8bb2cff5d792d4b8779fcc3\"},\"keywords\":[\"mobile\",\"offline\",\"retries\",\"state-management\",\"sync\"],\"articleSection\":[\"Programming\",\"Software Engineer\"],\"inLanguage\":\"en-GB\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/04\\\/09\\\/offline-support-sounds-smaller-than-it-is\\\/\",\"url\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/04\\\/09\\\/offline-support-sounds-smaller-than-it-is\\\/\",\"name\":\"Offline Support Sounds Smaller Than It Is - wade.one\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#website\"},\"datePublished\":\"2026-04-09T09:00:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/04\\\/09\\\/offline-support-sounds-smaller-than-it-is\\\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/04\\\/09\\\/offline-support-sounds-smaller-than-it-is\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/2026\\\/04\\\/09\\\/offline-support-sounds-smaller-than-it-is\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/wade.one\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Offline Support Sounds Smaller Than It Is\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/wade.one\\\/blog\\\/\",\"name\":\"wade.one\",\"description\":\"wade womersley - york based software engineer\",\"publisher\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#\\\/schema\\\/person\\\/8b4739f8f8bb2cff5d792d4b8779fcc3\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wade.one\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-GB\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/#\\\/schema\\\/person\\\/8b4739f8f8bb2cff5d792d4b8779fcc3\",\"name\":\"Wade Womersley\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/wp-content\\\/uploads\\\/2015\\\/02\\\/200px.png\",\"url\":\"https:\\\/\\\/wade.one\\\/blog\\\/wp-content\\\/uploads\\\/2015\\\/02\\\/200px.png\",\"contentUrl\":\"https:\\\/\\\/wade.one\\\/blog\\\/wp-content\\\/uploads\\\/2015\\\/02\\\/200px.png\",\"width\":202,\"height\":200,\"caption\":\"Wade Womersley\"},\"logo\":{\"@id\":\"https:\\\/\\\/wade.one\\\/blog\\\/wp-content\\\/uploads\\\/2015\\\/02\\\/200px.png\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Offline Support Sounds Smaller Than It Is - wade.one","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/","og_locale":"en_GB","og_type":"article","og_title":"Offline Support Sounds Smaller Than It Is - wade.one","og_description":"Offline support looks like a simple feature request until you have to deal with stale data, sync conflicts, retry rules, and user expectations. The hard part is not storing data locally. It is keeping the whole system honest.","og_url":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/","og_site_name":"wade.one","article_published_time":"2026-04-09T09:00:00+00:00","og_image":[{"width":1518,"height":1506,"url":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/Wade-Logo-cropped.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_creator":"@wadewomersley","twitter_site":"@wadewomersley","twitter_misc":{"Estimated reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/#article","isPartOf":{"@id":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/"},"author":{"name":"","@id":""},"headline":"Offline Support Sounds Smaller Than It Is","datePublished":"2026-04-09T09:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/"},"wordCount":734,"publisher":{"@id":"https:\/\/wade.one\/blog\/#\/schema\/person\/8b4739f8f8bb2cff5d792d4b8779fcc3"},"keywords":["mobile","offline","retries","state-management","sync"],"articleSection":["Programming","Software Engineer"],"inLanguage":"en-GB"},{"@type":"WebPage","@id":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/","url":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/","name":"Offline Support Sounds Smaller Than It Is - wade.one","isPartOf":{"@id":"https:\/\/wade.one\/blog\/#website"},"datePublished":"2026-04-09T09:00:00+00:00","breadcrumb":{"@id":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/wade.one\/blog\/2026\/04\/09\/offline-support-sounds-smaller-than-it-is\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wade.one\/blog\/"},{"@type":"ListItem","position":2,"name":"Offline Support Sounds Smaller Than It Is"}]},{"@type":"WebSite","@id":"https:\/\/wade.one\/blog\/#website","url":"https:\/\/wade.one\/blog\/","name":"wade.one","description":"wade womersley - york based software engineer","publisher":{"@id":"https:\/\/wade.one\/blog\/#\/schema\/person\/8b4739f8f8bb2cff5d792d4b8779fcc3"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wade.one\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-GB"},{"@type":["Person","Organization"],"@id":"https:\/\/wade.one\/blog\/#\/schema\/person\/8b4739f8f8bb2cff5d792d4b8779fcc3","name":"Wade Womersley","image":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/200px.png","url":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/200px.png","contentUrl":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/200px.png","width":202,"height":200,"caption":"Wade Womersley"},"logo":{"@id":"https:\/\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/200px.png"}}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1173,"url":"https:\/\/wade.one\/blog\/2026\/04\/06\/most-mobile-app-complexity-is-not-in-the-ui\/","url_meta":{"origin":1177,"position":0},"title":"Most Mobile App Complexity Is Not in the UI","author":"","date":"April 6, 2026","format":false,"excerpt":"The hard part of a mobile app is usually not the screen layout. It is sync, auth, state, permissions, offline behavior, and the release problems that show up after the UI already looks finished.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1167,"url":"https:\/\/wade.one\/blog\/2026\/03\/31\/serverless-is-great-until-you-need-to-debug-it-at-2-a-m\/","url_meta":{"origin":1177,"position":1},"title":"Serverless Is Great Until You Need to Debug It at 2 a.m.","author":"","date":"March 31, 2026","format":false,"excerpt":"I still like serverless, but the tradeoff is obvious when something breaks at 2 a.m. The architecture is easy to ship and harder to reason about when you need logs, context, and a fast path to the real failure.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1169,"url":"https:\/\/wade.one\/blog\/2026\/04\/02\/the-problem-with-simple-aws-architectures\/","url_meta":{"origin":1177,"position":2},"title":"The Problem With &#8220;Simple&#8221; AWS Architectures","author":"","date":"April 2, 2026","format":false,"excerpt":"AWS diagrams love to look simple. The problem is that the operational reality behind them is usually doing a lot more work than the picture admits.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":102,"url":"https:\/\/wade.one\/blog\/2009\/09\/12\/good-news-windows-7\/","url_meta":{"origin":1177,"position":3},"title":"Good News &#8211; Windows 7","author":"Wade","date":"September 12, 2009","format":false,"excerpt":"That's the name of the video \"Good News - Windows 7.\" The little kid is called Kelly and she believes \"seven\" is a happy word - it does stand for \"togetherness\" in China though so maybe that's the underlying message here - \"with Windows 7, we're together again and I'm\u2026","rel":"","context":"In &quot;News&quot;","block_context":{"text":"News","link":"https:\/\/wade.one\/blog\/category\/news\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1168,"url":"https:\/\/wade.one\/blog\/2026\/04\/01\/why-i-still-like-serverless-for-small-teams\/","url_meta":{"origin":1177,"position":4},"title":"Why I Still Like Serverless for Small Teams","author":"Wade","date":"April 1, 2026","format":false,"excerpt":"Serverless has real tradeoffs, but for small teams I still think it usually wins. The operational overhead stays low, the first version ships faster, and the mistakes are easier to afford early on.","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":764,"url":"https:\/\/wade.one\/blog\/2015\/02\/07\/standardising-logging-and-parallelisation-in-multiple-languages\/","url_meta":{"origin":1177,"position":5},"title":"Standardising logging and parallelisation in multiple languages","author":"Wade","date":"February 7, 2015","format":false,"excerpt":"Over the last few days I've been working on interfaces to use across multiple programming languages to standardise how some things are done. This stemmed from a requirement to allow processing to be dealt with in any language from any other language. People do this all the time, it's nothing\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/wade.one\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/source-code-583537_640.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/source-code-583537_640.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/wade.one\/blog\/wp-content\/uploads\/2015\/02\/source-code-583537_640.jpg?resize=525%2C300&ssl=1 1.5x"},"classes":[]}],"_links":{"self":[{"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/posts\/1177","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/comments?post=1177"}],"version-history":[{"count":1,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/posts\/1177\/revisions"}],"predecessor-version":[{"id":1200,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/posts\/1177\/revisions\/1200"}],"wp:attachment":[{"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/media?parent=1177"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/categories?post=1177"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wade.one\/blog\/wp-json\/wp\/v2\/tags?post=1177"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}