¶ Intro
What do you mean replicate the whole data to the end client? Like, It's too big. Like, that can't work. So I understand the hesitation. But any like half step in that direction, it just a gateway to replicating more and more and more. Cause you just want more and more and more to be instant. So I had a very traditional mindset around it, which is, okay, we have an API, the API serves data and we're going to like cache some of that locally.
And I think a lot of people are already doing this stuff, like react-query put you in that direction where just caching stuff and then refetching. It kind of gives you some of this like real time local-first feel, but then you start to hit the limits with that, if you're looking for stuff that's not literally in the exact view, you're then round tripping with the server again.
The key thing to understand is if you're building an app that people are actually using every day, the initial load time is really not something you need to care about. Welcome to the Local First FM podcast. I'm your host, Johannes Schickling, and I'm a web developer, a startup founder, and love the craft of software engineering. For the past few years, I've been on a journey to build a modern, high quality music app using web technologies.
And in doing so, I've been falling down the rabbit hole of local first software. This podcast is your invitation to join me on that journey. In this episode, I'm speaking to Dax Rad, who is using local first in multiple projects, including the serverless deployment tool, sst. dev, a healthcare app, and an upcoming personal finance app.
In this conversation, we explore how local first simplifies app development, the UX and data patterns he's using, and how self hosting could empower local first apps. Before getting started, also a big thank you to Expo and Crab Nebula for supporting this podcast. And now my interview with Dax. Hey Dax, welcome to the show. How are you doing? Good. How are you doing? I'm doing great. Super excited to have you on the show.
You're certainly also one of the more, well known voices on Twitter sharing the local-first gospel. So, seems like you're not just working on one project with local-first, but almost quite a portfolio of local-first inspired projects. So super curious to hear more about those, but maybe you want to share a bit of your story of like, what has led you to local-first?
¶ Local-first motivation and projects overview
yeah, so my name is Dax. My primary project that I work on is a project called SST. Uh, we're a, we're a dev tool. Like, we help people ship applications. We help people deploy all kinds of infrastructure to all kinds of places. And we have an adjacent project to it. Uh, so SST users get access to this thing called the SST console. And that is a local-first application. I mean, I'm gonna say local-first. There's obviously, like, a spectrum. I would say it's a local-first.
The least local-first thing with that you could possibly have while still, I would say, embodying some of the characteristics, but my history with it, like most people or like a lot of people, I guess, uh, it started with, with Linear. I came across. The product probably like 2018 or 2019 somewhere, somewhere around there, probably 2019, something like that. And I found them fascinating in a few dimensions. One, their attention to detail was just kind of outrageous.
Like there's all these little details. Outside of local-first stuff, just these details that are in there that, you know, they're, they're like real craftsmen. They care about, uh, every little aspect of their product. I found them really interesting from a business perspective because they entered the most crowded and most cliche space you can possibly think of. Everyone has had the idea of, I hate my project management tool.
Like I should build my own, uh, the most boring, cliche, non innovative idea possible. They enter that space and they absolutely crush it. Like no one really could have predicted that. I think you would have, if you just heard that idea, you're just been like, whatever, and they did that purely from this aspect of. Really excellent quality and performance. And these are things that people will love to say, like, oh, the user doesn't care. People, performance doesn't matter.
Like, you know, they'll try to say that it's something that you can do later. And this is a great example of a company that's kind of violating that narrative. So I find them really fascinating. And I got obsessed with just digging into how They built their stuff initially from like a UI perspective. But then I was like kind of looking into how all this, how everything is super responsive and instant.
And I started them so hard to the point where I think I was really convinced at some point that I knew more about their system than anybody that didn't work there. Like I understood like their exact protocol, like how they sort stuff locally. Uh, I kind of just kept going back to it to refer to it. And around the same time, my wife and I were starting a new project in the healthcare space, and this was a productivity tool.
So it's the type of tool that people log into the beginning of the day, use it all day until they finish their work. So these are the places where I feel like the performance aspect is really understated. When you're using an app every single second of the day to do your work, There's all those little paper cuts where something takes a little bit longer to load, or there's like a spinner. These do weigh on you. If you talk to the user, they probably won't articulate it.
But if you ask them the question, does it feel like a joy to do your work? Or does it feel draining to do your work? And they're going to say draining unless you push things to this degree. and this never shows up in like typical user research or like user feedback or any of that stuff. So because we were working on something in this category, I was like, okay, we have to do it. We have to like set the, meet the same bar that Linear met.
And that's kind of when I started digging into what are all the different ways that we can enable this. And we started from like a JSON API using Orbit to like replicate the data locally, to GraphQL and Urql to do something similar. Um, and then we eventually ended up on Replicache after, like, trying a few of those. Uh, we also tried, I think, RxDB at some point. Because we knew that, yeah, we want to have almost every operation happen locally, if possible, so that the feedback is instant.
You know, searching, and this is in the healthcare space, so searching through patients should be instant. Loading up their information should be instant. Even if we had a really large data set on the server, In terms of like, a single business might have, you know, tens of thousands of patients. An individual doctor is only seeing a small subset. So having the ability to like, partially sync the data that we know the user cares about.
So we spent like, through the course of building this product, we tried so many different setups. And we've probably spent two years of learning and trying how to do these patterns before we landed on something where, one, like the tools, like I said, we ended up on Replicache. Replicache is a tool that we liked, and two, like, As people building products, like we understood how to like, really take advantage of it, how to structure things and where to pay attention.
So that product is called Bumi. Uh, we actually ended up deciding to pivot away from it. Uh, like we have, we have some customers, but it just ended up not being a market that was particularly large. And so we wanted to switch to something that was a lot more, like, wide, like anyone can use. And something that we personally would use, I found that any product I build where I personally use it, it just comes out like 10 times better.
It's just way easier to understand the problems and be motivated to fix them. So we're working on a new product called Radiant now. And that is a personal finance application, which again, there's thousands of, very similar to the Linear story. But we really believe building, like, a very power user productivity focused version of it. There's like interesting room in the market for that.
So the one other thing just to kind of cap off the story is, uh, so between those two things, uh, working on SST, we also kicked off a new project there, the SST console, which I mentioned earlier. Kind of a similar story where this is a tool that you use alongside your day to day development. You're using all the time.
Having every single thing be fast really makes a difference in the end feeling that users have of the product, so we decided to go with the same kind of local-first ish model for that. And that one is completely open source. We were really public about building it. I think a lot of people were exposed to the idea of This pattern, and we've seen people like, kind of take that project and like, use a basis, use it as a basis for things that they're building.
So yeah, I think for me it's always been about delivering a much higher user experience. I really feel like at some point the web was really focused on how can we make the web feel native, and that was like the obsession. And for some reason that like, kind of went out of popularity, but I personally still care about that as an end user when I use products and they're slow and laggy. I want everything to be my browser.
I like that and I want people building those things to care about those things because I'm just going to be happier as a user. And it's kind of been my. My reasoning to really like, you know, spread the word about this stuff. Yeah. Everything you've just said deeply, deeply resonates with me. I'm similarly also very inspired by, by the folks at Linear, how much value they put into the craft.
I was actually just, uh, as a little anecdote, I was just having drinks last night, uh, with Julian from the, the, the Linear team. And we were also chatting about this and similar to what you've described as that it's so tricky. It never shows up in your user interviews, et cetera, that people say like, Oh, like I'm really annoyed that this button takes like 300 milliseconds. But if it was instant, then I think people will really, uh, share that.
And it's basically like a, more of like a death by a thousand paper cuts, and it's too hard to enumerate the thousand paper cuts, but it's still kind of clear that the product is not very smooth. But once you really, once you're using Linear, then there is no going back. And this seems to be also still like a challenge in terms of communication for them. How do they articulate to someone who has not tried it out yet? What the difference is?
It's that overall quality and, but I think they're doing a great job of just being sort of like the lighthouse example now of like what that next generation category defining product feels like and should be like. So I think they're, they've like bootstrapped their way out of that, that problem and it's very inspiring for me where I'm like my JIRA is kind of music apps where I am annoyed by the thousand paper cuts that you have in when you're using Spotify or SoundCloud, et cetera.
So many of those supposed apps feel more like websites that really like Winamp was a, or iTunes back in the golden days, that was a much superior user experience to what we have today. We have maybe a richer ecosystem in terms of available music, but the actual user experience that has really suffered and I similarly also care a lot about how native can you make a web app feel like. And not just, end up with your ambitions on like a website level. So, uh, that deeply resonates.
I think there is, it's both, uh, equally exciting to explore that from a user experience, what really drove you. But I think there's also a lot of potential from a developer experience point of view, where I think there's also so much stuff we just put up with as a developer.
So I'm also curious to hear more on that, but yeah, you, you mentioned that you had a one to two years of explorations early on that, where you figured out how not to do it, I'd love to hear a bit more about those failed attempts.
¶ A slower initial page load is worth it
Yeah, so I think, the spectrum really was just us dipping a toe to then going further and further and further with how much is done locally. So, I think initially, it's, that sounds weird to everyone, right? Like, what do you mean replicate the whole data to the end client? Like, it's too big. Like, that can't work. Or like, what about this? What about that? What about that? Uh, so I understand the hesitation.
But any like half step in that direction, it just kind of a gateway to like, just replicating more and more and more. Cause you just want more and more and more to be instant. So I had a very traditional mindset around it, which is, okay, we have an API, the API serves data and like, we're going to like cache some of that locally. And I think a lot of people are already doing this.
Like a lot of stuff, like react-query like gives you this starts to like put you in that direction where just caching stuff and then like refetching, you know, when the window becomes active again, it kind of gives you some of this like real time local-first feel, but then you start to hit the limits with that, where. If you're like, if you're looking for stuff that's not literally in the exact view, you're then kind of round tripping with the server again.
And I think the key thing to understand is, is if you're building an app that people are actually using every day, the initial load time is really not something you need to care about. The first time they load the app, like let's say they like log out or it's a new computer or whatever, they're setting it up. Okay, like maybe they're downloading a couple megabytes of stuff.
That's going to be slower than You visiting some public website that's like optimized around like the initial load time, of course, but once that initial cost is paid, every single interaction that they have going forward from there is insanely fast. And that trade off is so worth it for apps that people actually use as a joke. I like to make out is that the web has become so obsessed with optimizing for websites where people visit and leave as fast as possible.
I'm like, If you're working on something like that, totally get it. Like totally get all the, the SSR stuff. Everyone's obsessed with all the, like the infinite levels of like CDN caching and the little optimizations there. I totally understand that, like minimizing bundle size, all that stuff. But a lot of people work on SaaS products. Most of the products that I use every day are not, don't fit in that category. I'm using them every day, like just preload stuff for me.
And I feel this way about some of the biggest products, right? Like think about GitHub. I'm an open source developer, like, my day to day job is maintaining open source projects. I'm in GitHub all day, clicking through issues, looking through commits, looking at PRs. It is so frustratingly slow how almost every single interaction results in like a page load. GitHub knows, okay, you're not going to send me all of GitHub to me locally.
But you know there's these three repos I work on every single day. Sync that locally, sync that optimistically. Like that would save me so much time and frustration and energy. And actually scratch that. It's not really about the time. It's really about the energy for me. You can spend four hours doing work that saps your energy and four hours where you feel empowered. And to me, like good apps like this. I like feel super empowered and it's not sapping my energy.
I'm not drained after the four hours. I'm like ready to go on to my next thing. Right. And yeah, some of these biggest apps that are productivity apps are so built like traditional websites. Definitely. Yeah. I mean, the, the use case that you've mentioned in regards to GitHub is, is an excellent example. Like for, like you have those.
units that are really self contained, most repos don't have hundred of thousand of issues it's really easy, like takes roughly the same amount of data that like your initial JavaScript bundles took to just download the entire, thing as a SQLite database or like in another format, and then just have it there available.
Since you're navigating so much within the scope of a GitHub repo, whether it's Between GitHub issues or going to like a pull request or something and whatever paper cuts you experience there, it really lowers your effort of like wanting to go the extra mile. Whether you want to respond to someone's like, in a GitHub comment.
I think, uh, Jamie Kyle, actually, I think he ran a couple of experiments there that's literally faster on GitHub to open a new tab for a link and have, get the server side rendered stuff than dealing with like the, The, the rail style in navigation on the existing site. And that speaks exactly to, to the symptoms that you've, that you've described. I will also call out a project.
I don't fully remember the name anymore, but someone on Twitter, I think was working on, on exactly that, like a little GitHub client that is local-first inspired, that pulls down your data that you're dealing with to make exactly these interactions fast. Maybe you know the name. Yeah, so I actually tweeted asking about this. I remember seeing it and I couldn't remember it. And I think it was, uh, by Devin, the Parcel JS. Yeah, so I saw him messing around with that.
I don't know if he ever, like, finished it or released it or anything, but yeah, this is so desperately needed. GitHub isn't going to do it, I don't think, because it's so outside the scope of how their system works. But just someone, please, just build a thing that syncs the issues and PRs optimistically. And you're going to save me so much. I'll pay for this product easily.
I don't know how big the market is for people that care this much, but it doesn't seem like that much work because it's just a single repository view and some of the stuff that's in there. And you could totally do this. Yeah. This is kind of what I was saying before. I'm mostly speaking as a developer, like building these things. But the end thing I care about is as a user. I just want more of this stuff out there in the world. So I'm not as frustrated. I definitely agree.
And like, I applaud you for like going that adventurous path and that extra mile with SST console, since I think you had the right intuition there and it is paying off in terms of just how fast that feels to use and how much, like you say, like it no longer drains your energy. Yeah. It gives you energy using that.
And once you had that experience, you'd really like every loading spinner that you see is so much more painful afterwards when you're like using, for example, the, Cloudflare console, it's like the same thing there. Like you're within a project , you have that many deployed workers and you just want to navigate between that. It's fine that my logs stream in. But all the metadata around that should just be instant. It's a tiny amount of data.
And like the majority of apps, if you really look at what data people are constantly going between, it is so small. So yeah, you figured out like the, the patterns early on or rather that you've realized, okay, you started with a bit of caching, a bit of more caching, a bit of more caching, and you didn't quite find the right line to draw. So you just, okay. We're going to just bring over all the data. Did you have other sort of learnings where you had to rewire your brain?
¶ Local-first makes app development simpler
Yeah. So I think, uh, one other middle step we had was we tried it through GraphQL. We weren't particularly interested in the GraphQL part. It was, it just happened to be that, uh, Urql, the GraphQL client. It implemented a lot of, uh, the local-first stuff like you could replicate the whole database locally and because GraphQL has a nice, idea of like relationships between objects, the data format locally could be pretty intelligent.
like, downloaded 1 thing, it knows how it's related to another thing. So if you query it from a different path, it can, like, resolve it locally. So there's just some clever things that it did. And we thought that was really cool. But what ended up sucking was we didn't really like GraphQL. And everything you did, even local operations, required GraphQL as input. which again is fine. It's just, it just wasn't really clicking for us.
And again, it still had this, uh, There was a limitation of like, it wasn't really meant or initially designed to have all data locally, so it's all like, there's always like some stealing or some performance issue or something you run into. So when we went all in on having, okay, like we, the system is primarily designed around syncing the data locally. What we found actually, and I think the Linear people talk about this too, is. The developer experience around this became a lot more simple.
So my wife, who I work on this stuff with, she is not an engineer. She does everything else outside of it. But in the past couple of months, we're like, why are we drawing that boundary? Just learn how to be more hands on. And so she's been learning like how to actually like write the code and build these things and she's making like fantastic progress. One of the things that's been crazy helpful is. All the data's just there locally.
So for her, even though she's a beginner, it's such a simple model. She's not like figuring out the round trip with the server, like syncing all that. Like, I handle all that, and I make sure, like I figure out how the data looks like on the back end, and I make sure it's all synced correctly to the front end. From there, there's not all this weird like, loading states, or like fetching it, or like just a whole bunch of complexity around getting data back and forth.
It's solved in one part of your app, and then you never have to think about it anywhere else. So just from like a team productivity point of view, like she can build any feature she wants, even if I didn't like explicitly think about it from the backend point of view, because she has all the data locally. She's like, I want to create a view that searches through this set of data. She can just go do that. All the data is there. Very, very straightforward.
And it's actually wild how much of a productivity boost that has on your team, because you're not like constantly building these like back and forth with every new feature you're not rebuilding another way to like sync that data back and forth. and it's been amazing. That's so liberating.
Uh, we talked about this also in the recent episode with Kyle, who drew the comparison of like going from jQuery to React where React freed you up of like that manual view maintenance and now going from like the, the manual state transfer across different environments that frees you up of like that data management and you can now actually. Focus on what matters most, like building a great user experience and putting the effort into the pieces that do really matter.
And that simplification that is just so, so hard to overstate how nice that is. And I think that also unlocks a whole bunch of like new. tooling. So for Overtone that I co developed with the project Rffle, or now called LiveStore, it's always been a first class citizen to have great dev tools. And one of the parts of the dev tools is to have a data browser in there. So it basically feels like a Google spreadsheet and really it also acts as a Google spreadsheet.
You have, you have like visual view. on all of your data, if you change something such as like a playlist title in that data browser, it like automatically updates in your app. And there's no question of like, Oh, do I not need to like send this to this REST request? But do I need to also like, Invalidate this in Redis. No, you just like set the data and there it is. Yeah, that's great.
I mean, a local data route is actually something that we're definitely missing, but that's a, yeah, it's like it enables so many things like that. Just the debug ability and observability of what's going on and just being able to change something in one place and have it update everywhere, like in your view and in the backend, it's just so, it's so much simpler. And it's weird because I feel like apps used to kind of work this way. Like when I got started, I was building desktop applications.
And I guess there wasn't really a cloud component in some of the initial things that I built. And they were this simple because the data was just local and like you'd hit a button and you like mutate the data and it's it's there. So yeah, to me it feels like returning to a simplicity with like much more power and capabilities that we didn't have back then. Exactly. Plus we now also get the power of the cloud for collaboration and like a lot of the, the nice pieces.
But I think there's just been an over investment over the last 10 years into having everything be so cloud centric that like you built like even a local calculator probably as like a remote rendered server app.
And, I think now also bringing more investment into kind of back to the roots, uh, in, in this way of building, building apps, I think this will also help more people who are not quite as adventurous as you are to, to really explore this before others do, that this becomes a more viable standard path that people build apps with.
¶ Local-first data patterns
Yeah, and like there are, like a lot of stuff becomes simpler. There's some new stuff you have to learn, of course, like with any new paradigm, there's, things that are a little bit different. I think some of those involve things like, so in our case, in the Bumi example, we unfortunately had customers that had too much data, where it wasn't as simple as just syncing it all locally.
So we have to think of like some new kind of like UX related patterns where we kind of optimistically sync stuff that we think the user will care about. So like the first time you visit a patient, it might take a little bit to load because we didn't sync all the patients locally. We do sync like a shallow version of the patient locally for everyone, so they can search through all of them really quickly. But to get into the full details, like you might not.
That's too much data to send to everyone. So the first time you go there, it'll be a slow load. But from that point, we kind of flag them as, Oh, this user cares about this patient. And from then on, we'll constantly send them updates. If they haven't visited the patient's profile for like two weeks or whatever, then we'll kind of unflag it as something that they care about. These are like new patterns that, you know, they don't really exist in a traditional app.
But this mixture of local and cloud, you can get pretty far with tricks like that. And we kind of have to learn some of those tricks and some of those ideas. Because we have a whole like We have, I forgot what we called it, but it's like, uh, We have like a system where you can easily flag stuff as a user is interested in this thing. And if you flag it, it starts getting synced and then it'll get unsynced after some window that you specify.
So we kind of have to like, you know, think about that and, and create a framework around it. But that's one. The second thing is like, obviously handling, And this isn't unique to local-first. Anywhere you're doing optimistic things, obviously there can be errors that happen on a delay. Again, it depends on the type of application. Like a music player, probably, the syncing is probably like pretty straightforward, but for something that's Looks more like a traditional SaaS app.
There might be sources of truth that can't be validated entirely locally. So you'll end up with situations where the user does something, then goes away, then the error shows up later. And like, you need like a whole different UX pattern when you can't block the user. It's worth doing because 99 percent of the time they don't hit that error. So it's good to let them move on. But you have to handle a case where, where they do.
So there's different problems that show up, but again, it's all in the name of a better experience to the user. So totally worth it. So, let's say you have some kind of. You can create a new field that needs to be globally unique in your system. So let's say, uh, I mean, we can just use Linear as an example. Like, you can create a new project in Linear, but the name of the project needs to be unique.
there's an impossibly small chance that you create it and you move on, but someone had created it at the same time, because they thought it was their job to do it. And there's like an issue, because your write maybe got rejected. in a typical UI, when you create the project, you would see a spinner. Spinning. Okay, created. Because you have a spinner there, you can, like, make sure stuff gets written correctly and then you don't let the spinner come back till you know for sure. It's good.
Um, so anywhere with this like unique feel type of situation, we run into this problem. Our fix is actually pretty simple. We just have like some kind of like context thing of like pending stuff that hasn't been confirmed. And if it comes back with an error, we just pop up a toast that brings them back to the original. We, like, reload that original content. If they want, they can click back and go back to where they were. And, like, you know, change the issue that they had.
So I think we found that pattern to work pretty well. Obviously, it's like, the example I gave is really stupid. Like, no one is ever going to run into that. There's some examples that are a little less stupid, where it is reasonably possible that it could happen. But we found this idea of, like, preserving the context of the UI and bringing them back to it. If the error happens, they can opt to go back. We found that to work pretty well. Yeah, I think that's a very elegant solution for this.
I mean, another possibly even more aggressive solution to this would be to say to gate this feature behind being online. And to actually like establish some notion of like a global transaction for that and, only let the user pass through this intentional bottleneck if that has happened, but the way how you've designed this in a asynchronous way. I think it's also very elegant.
but I think this is the nice thing that like local-first affords you or forces you to do is like, it frees you up from a whole bunch of stuff you no longer need to do, but it really forces you to think about carefully, what are the domain requirements of your specific app? And if your domain expert from the app says like, Hey, this is what's really important here. There cannot be two project names with the same exact name. Then like you can actually put in the extra efforts to enforcing that.
And this is what makes the app worth it later. Yeah. Yeah. And it's, yeah, it's not all or nothing. You can opt in. So the thing that you said, we are in the SC console. We do that when you're creating a new workspace, we actually do have a spinner there because it's like weird to like, Create a new workspace and get kicked back 'cause the name was taken.
So yeah, we, we hold you there And that's like, you know, a more traditional experience and that's fine 'cause that's like a very small percentage of the app where you're like creating a new workspace. You do that once when you sign up and you never do that again. So yeah, you can kind of granularly pick where you want a more traditional blocking approach and where you want an optimistic approach.
And I think a lot of the people that are maybe skeptical of the local-first thing, I think they tend to imagine it in these extremes of like. You have to sync all of your data and obviously that won't work. Or like you have to have everything be optimistic and obviously everything can't be optimistic, but you can really pick and choose. It's more about what you default to. And then you opt out of It's good to default to optimistic first. Cause that's, what's great for the user. Right.
I think in the, in your context of building SaaS apps, I think there's kind of layers to this entire thing. There's like the global system. That you're still in control of. That global system is probably not local-first by all the ideals, but once you create an account or once you've created a project, and now you enter like a smaller scope of that overall system, and that now is very much local-first because this is where it actually matters. It's okay if your users sign up.
takes half a second to load. But once you're in your productivity environment, this is where you want to really have unlock the benefits that local-first gives you. Yeah, exactly. The data, what is the day to day workflow like? And that should be, that should be, you know, an excellent experience.
I think the other area, and we talked about this a little, is there's also the desire for apps that don't die because a company went out of business or like, you know, They were forced to find a business model that didn't make sense for the idea that they were working on. And I think this does make a lot of sense, I think. If you think about something like a, like a video game. I mean, video games are different now, but like, you know, there was a time where you would buy the video game.
It was a finished product, and it was on, like, a physical thing, and you could just run it and play it. And. You had that forever. So, but I mean, now you have like some games that require you to be online or it's a, it's a hybrid of both. And like, you can imagine how at some point this game won't be playable anymore. And that's happened to a few things. I think people feel that way about applications as well. And I totally get that.
I personally don't work on anything where that's like the critical burning desire where like, you want it to like really run locally. You want, you care about the privacy, you care about whatever. I like totally get all of that. It just hasn't intersected with like the areas that I focus on. So for me, it's been more about, uh, just the experience side of it. And I think the reason is a little bit tricky to go to that extreme in SaaS.
is a lot of like a portion of your value is in the end experience. And like we talked about, I think people are under delivering there, but usually it's also tapping into some kind of bigger data set or bigger capability that can't really run locally. So it's usually a mix of the two things. So I typically will have to mutate the data outside of the user interacting with it. So if you look at something like, uh, like Radiance, so this is going to be a personal finance app.
You can totally build a fully local-first version of this that runs entirely locally. That whoever's building the app, it can be totally encrypted where they don't even have access to seeing any of this stuff. And there's like value in building something like that.
But, you know, with all these advancements in AI and LLMs, those can actually impact Personal finance apps a lot in terms of like making sure that data is cleaned up correctly, auto categorizing, like all the things that sucked historically about these applications, LMS have a lot of potential to fix that, but there's not really a good way to do that in a local-first way, at least today. So that's why for us, you know, for building this thing, it's not going to be this.
thing that runs entirely locally because we still want the ability to like process your data using tools and technologies that, you know, unfortunately can't run locally today. Yeah, I think you've touched on a couple of really interesting points there.
So the, I think right now, the canonical definition of like, what is local-first software is as according to the Ink and Switch essay that lays out the seven ideals of local-first and I think you're, You're, nicely sticking to the first four, which is like about the loading spinners and that it works offline and so on.
And I think what you've just hinted at, what you don't do yet is what local-first calls like the long now that your app like lives on, even if the creators are no longer working on it. And that's very understandable. That takes a lot of time to, and effort to put into. And given that you just have so much time to work on the app, that is not your highest priority.
However, what I think is still nice and a glimmer of hope there for the entire category of local-first inspired software is that if at some point you say like, okay, I'm winding down development on this app, then you could still much more easily to put in a bit of extra effort. to now round up the project in a local-first way. I think we've seen a really nice example of that with another personal finance app called Actual Budget by James Long.
And so as he no longer had time to work on that project, it was so much easier now to transition the app from something that he hosted as a cloud service to something that people can just run by themselves. If you build like those monstrosities of like giant Kubernetes cloud things, it's really, really hard to like, tell someone how to easily just download a DMG and run this on your MacBook, whereas if your app is local-first to begin with.
Then, I think it's much easier to repackage the app that you've built. And you can trim away a lot of like the multi tenant stuff that you need about SaaS and just package the, the productivity aspect of the core of your app. And then I think it's actually quite nice and easy to achieve lasting software.
¶ Self hosting
Yeah, I agree. I think so. And with Radiant, we're actually, so because we can't. Do this thing where it's like end to end encrypted and because of what, what our goals are, instead, we're making it dead simple to fully run the whole system yourself. If you care about hosting yourself and you want all the data and a big part, and to be honest, that's a lot of what SST does. SST helps you ship very complex software multiple times in multiple environments.
and so, because Radiant is an SST app, it is very easy to point it at your own private infrastructure and say, I'm just going to have a self hosted version of this. And it's, and we're making sure it's like, As simple as it can be to do that, and get as close to, like, using the hosted version because there's like some weird benefits of that. Like, so this version that we're building is going to be mostly built on Cloudflare.
If a lot of people end up self hosting it, that's great because Cloudflare is paying for the usage. Like, The free tier is coming out of each individual's deployment. They're like self deploying it into CloudFlare. And that's like an interesting model for, I don't think for radiant, it's like particularly interesting, but if you look at, um, there's another project by, uh, Ben Vinegar, he works at Sentry, called CounterScale. So this is a classic, like Google analytics type product, right?
It does like this web analytics, the basics, and it's very early on. But it's not a hosted project. It's just something that deploys into your own CloudFlare account. So he can have a million users using it for free without ever really, you know, paying for that because, you know, CloudFlare is much more set up to offer that. I'm very interested in that model. Uh, cause I think for a lot of infrastructure pieces like that, it is a nice. To have it alongside the rest of your infrastructure.
I worked a lot in healthcare environment, which has a lot of compliance requirements. And oftentimes I couldn't use the tool I wanted to use because it wasn't hostable in my own environment. So, um, we're pretty excited about like letting people do more of that. I love that. And I think this is like intuitively.
It should be easy to self host your own software, like whether it's like some company running it or whether I just like take the software, put it on my Raspberry Pi, put it on, on Cloudflare or AWS, but in reality, that's very much the exception. I'd be curious whether you can share a bit more of like the reasons that you see why that's so hard and how you're trying to make that easier. Yeah, I think, uh, it's funny because there's a weird alignment if you think about this. We are a small team.
If our system is really complicated to run, that sucks for us. If we can make our system really easy to run and really low overhead and we care about that more than everything, it kind of starts to look like something that's easy to self host by anyone, right? So I think for us, like, a lot of times we'll choose architectures that look weird because they're like, Oh, that looks like the fourth option on the list of the ways they could have built it.
But it's because we really, really care about, low operation overhead long term. So even if it's like, you know, 20 percent harder to build up front, if it results in like less babysitting on our end, we'll like choose that option. And it's because we're very committed to continuing to be a really small team.
And so we'll, we'll end up picking those options and those options happen to align with like, what's, what's simplest for, for the end end user, if they're self hosting, I will say like there is a version of doing this, which we don't do, which is. Building your stuff in the most, like, neutral way possible, so it can run in literally any environment. I definitely get that, but you do end up having to run a lot of infrastructure on your own to, like, get any complicated application working.
If you do it in a totally neutral way. We kind of picked a middle ground where it's like, we do rely a lot on, like, in this example, like, we're going to be using CloudFlare, we're going to be using workers, we're going to be using their queues, we're going to be using their PubSub, all the CloudFlare native things. So, yeah, if you want to go take that and run it inside, like, just a standard server you have, it's going to be a little bit challenging.
And like, there are adapters and stuff, and you can figure that out. But we're starting with just making it runnable in your own cloud environment. So you can sign up for a Cloudflare account and run it. I know that doesn't like hit the exact extreme of like being able to fully run it yourself, but we think it's like a nice middle ground for a lot of companies, right? Companies typically are already using some form of this. Getting this stuff deployed into their own account.
I think it works well. What's weird is you ask, like, why isn't this more common? I think it's actually common in this weird way that people don't expect. A lot of companies build SaaS software, right? They'll build like, I don't know, let's say, let's take some random common thing, like Auth as a service, right? Like, I'm going to build Auth as a service that people can use. And they initially launched that as like a hosted product. And nobody uses it except for people that have no money.
So they have like a lot of users, but you know, these users don't pay a lot. They start to try to move up, up market. They end up with bigger companies, you know, medium sized companies, enterprise companies. They'll all love the product, but refuse to use a hosted service. So then they're forced to figure out how to make their stuff run inside the customer's AWS accounts.
So if you do look at a lot of these companies, they kind of do have this hidden, like thing that they eventually figured out. I think companies should realize they're going to end up in this place anyway. Like it's very hard to build a big business purely off of a hosted thing. Like some people do it when you really have like a large market, but oftentimes you're forced to. Bring yourself inside someone else's environment. So I think one way or another, you're likely to hit that.
So it's good to plan for that upfront and assume that you're going to run into that. And we, because we're like, We're more in the infrastructure space. We kind of expected that from the beginning, which is why we care a little bit more about making all that possible. So let's imagine I want to follow this paradigm for Overtone.
And I do think that Cloudflare could be a very interesting fit for that in terms of the modern primitives that it provides, so what would I need to do as the application developer of Overtone who buys into that vision and says like, Hey, I don't want to operate like the cloud thing for that, but I want to empower every user to deploy their own for the benefits that it provides. What would I as the application developer need to do to make that happen? And then also follow up question.
What does someone who wants to deploy that Which sort of additional stuff do they need to deal with as opposed to just using something like Spotify? Yeah. Yeah. Cool. So I think, I think the simplest way to think about it is how hard is it you for you to spin up a staging environment for your own application, right? You want to create an exact copy of your environment, but you know, just the staging version. So this requires bringing up all the same infrastructure.
It requires like, you know, running your database migrations to get the schemas running. Um, requires a bunch of things. And, you know, the obvious thing is you should be using infrastructure as code to help you do this. You define all your infrastructure as code so you can point it at different environments and run entire copies of it. That's like the bare minimum. If you yourself can quickly create another environment for yourself, you've kind of solved the problem.
Now someone else can follow those same steps. Where they're just cloning your project and running. You might be using Terraform, you might be using SST, whatever it is. Uh, you can do a thing, deploy with your credentials. And the same thing ends up in your account. Where this is a little bit challenging is updates. So obviously, you know, you have new versions. People can clone and redeploy, whatever. But there's sometimes operationally challenging updates.
Like you might have like a schema change. You might have, uh, like data needs to be migrated. So, ultimately, the thing to always go back to is how do you make it easy for your own team to do that? Are there programmatic ways of doing all this? Or, like, if you need to do ad hoc random stuff and that's part of your workflow, it's going to be annoying for your end user to self host because then you need to, like, send them the instructions, they need to do it, they might, like, do it incorrectly.
So, if everything is tracked in code and is automated and is programmatic, It's good for your team. That's good for the end user. and for an end user, in terms of what they should expect, if you are using some version of the cloud, you should hope that the thing you're self hosting is taking advantage of as many managed services as possible, right? So I had this issue with PostHog. So PostHog is an open source analytics tool.
And when I was exploring it for my healthcare thing, I was like, okay, we'd have to self host it. Let me go look at the first self hosting steps. Step one, set up Kubernetes. Right, immediately there, I'm like, I like, found a way to finally free myself of Kubernetes for my main application. I don't want to like go, have to go manage a Kubernetes cluster again, just for this thing. So you should hope that, you should pick tools that use managed, versions of these things.
That's why, like, we typically focus a little bit more on some of the serverless stuff, because it's very easy. If our app just runs as a Lambda function inside of someone's AWS account, or as a Cloudflare worker, there's almost no overhead. You're not like, you don't have to like set up like Datadog to like monitor the memory and like scale this up, up and down. It sucks to do that for like a small tool that you're adding to your project.
So we try to make use of managed servers as much as possible so that the end user doesn't have as much operational overhead. and again, some people are willing to do that. It's just, uh, it's obviously a spectrum with all of these things. I do think it's a double edged sword while it like really takes away that operational burden and operational overhead.
It's not just about, uh, deployment cycles and like the update cycles of the app that you're deploying, you potentially also need to update a, like from database version A to database version B of like just a database server that's running or like your analytics server or whatever. So that certainly takes a lot more effort.
However, on a longer time horizon, if you do rely on managed services, they might just at some point send out a notice and say like, Hey, next quarter We're shutting down like that plan that you're relying on. So you're also in some regards might be building on quicksand. I think the, the larger a services such as Cloudflare, I think the chances that the rug is being pulled underneath you are probably lower. But it's always a double edged sword.
And so my preference typically is in terms of the architecture that I'm using is like to go as simple as possible. This, for example, why I'm also very drawn to SQLite and I love the, all of the, the new tooling being built around SQLite since SQLite that's, no one's going to take that away from me. It's just like a little file. That I can put on a SD card or whatever. And, uh, that's still gonna like similar to that computer game from like the 1980s. that's still gonna work, in, in the future.
And I think that's a great way to preserve data and you can still do so much with that. So I think that's sort of like a third option, uh, instead of going with like hosted or, Very scary self hosted infrastructure. If you make the self hosted infrastructure just that simple to rely on something like, like something that's almost serverless by default, like SQLite, I think that's a, that's a very attractive idea for me. Yeah, no, I agree.
And with everything I'm saying, it's always like you have to exercise your judgment. I'm speaking from the things that I typically have worked on and I think this is where we landed. but yeah, if I was working on something that had different requirements, like, yeah, having like the simplest approach, uh, makes sense. The only reason that we have to do this is because we work on stuff that is like one of our tools is.
Like, uh, like issues, like, kind of like how Sentry offers, like, issues that extracts. So we need to process, like, a large amount of logs, uh, because some people have, like, you know, like, billions of invocations a day. So it's not the type of thing that we can really simplify, like, we can't just give you, like, a single container. And this, because that's going to get overloaded very quickly.
So the options for us are some crazy Kubernetes thing, or we just take advantage of AWS Kinesis and all these things that are built in that are lower overhead. Um, but, you know, if those weren't my requirements, I would probably opt for something similar. And yeah, at the day, it always just comes down to like, have good judgment. With whatever you're doing.
Yeah. I think it's just as web developers, we're just so like in, in our like dependency fury in a way where like, we just like npm install this, npm install that, and we also not just package dependencies, but we also like add infrastructure dependencies left and right to something, I think is a big contributor, contributor. Why the long now aspect of local-first software is so hard to achieve.
So I'm always, uh, trying as hard as possible to avoid dependencies, whether it's package dependencies or whether it's like infrastructure dependencies as much as possible. But it's always a trade off since they do sometimes can really save you a lot of time. Yeah. Yeah. I think for us, we kind of land in this middle ground where we'll commit to one thing. But then we'll reject a bunch of other things.
So, there's this phenomena happening right now, where, uh, you have these large cloud providers that offer these services. The services aren't like, The best, most wonderful things to use. There's a bunch of companies that'll like, make a copy of just that one service, but do it like 10 times better.
It's often really compelling, and like you, I can see why people are tempted to adopt every single one of these, and people end up with a stack where it's like, It's my application and sitting on top of like 10 different other vendors. And these 10 vendors are also startups, you know, like it's unclear if they're going to be around for a while. So for us, we like as tempting as it is. And some of it has nice as some of these offerings are like, we never use those.
We just stick to the most lowest level primitives that we can be sure are going to be around for a long time. And we'll commit to those. Like, we're going to accept the fact that if something happens to them, we're screwed. More likely we're going to die before they do. So that makes sense, but like, you know, we won't, we won't apply that everywhere. So again, it just comes down to like having the right judgment and picking things and low dependency is very important.
¶ Updating a local-first app
That makes a lot of sense. So you mentioned in terms of the deployment cycle, also the update cycle of your application, and now in the. local-first way, there is, uh, primarily you need to think about how do I update the local-first app itself? So some certain things are easy to deploy, like a, a new component version of your UI that's, that's easy.
But now that also all of your data lives in the client, the schema of your data or like the shape of your data is possibly also going to evolve over time. And there's different ways to deal with that. So I'm curious how you've handled that. as well as possibly also the same problem applied to your cloud component. Yeah. So in terms of the data locally, we've never done anything sophisticated here. We've always just blown it all away.
If we have to do some kind of severe breaking change, we will just update like the big version number and the app will erase everything locally and re sync. It's not a huge deal. It's never like massive amount of data. I think I can see a world where this is, and already it's actually not too different than changing your back end database schema because you have similar issues, right?
Like you want to make backwards compatible changes as much as you can because you don't want to have to like force changes in your application code and force deployments of those together. So similarly, most of the time you can do something backwards compatible. If you totally mess something up, which obviously happens sometimes, yeah, we just blow it away. We haven't really found the need to do anything more sophisticated in what we have, like. Migration scripts that run locally or anything.
I can see how that's a case for if you're like running, I guess, if it's like end to end encrypted or something where you can't really do that and the true state of the data is what's on the person's device. I can see how you have to like think about that a little bit more. I think it kind of reminds me of WhatsApp a little bit because like with WhatsApp, it feels like you're like shuffling your data across your devices. You get a new device, you like Move the data from one to another.
So I imagine they deal with things like this, but yeah, personally don't have a ton of experience with that. Yeah, I mean WhatsApp is also one of the OG local-first apps that really like followed this pattern out of necessity that defined why WhatsApp is so attractive. But I think the, the trade off that you've landed on, I think makes a lot of sense.
It's very pragmatic and given that you're in this hybrid mode of like some parts of your app are cloud oriented, some parts of your app are local-first, now you can also reap the benefits of the best of both worlds that allows you to move quickly. I think if at some point you want to move further on the local-first side. Spectrum, then I think maybe you can't quite afford just blowing all the local data away.
I'm kind of exploring some patterns in that regard where I'm kind of keeping two versions of the data around locally. One version that is more of like an event log. And one that is more of like a typical app database. And the app database is just a projection on aggregate from the event log. So I also get the benefits and luxury of, in the case of a schema migration, I can just blow away the app DB. But instead of talking to the server, I can just replay it locally.
Sometimes takes a little bit of time, but, uh, you get a lot of different options here that you can choose depending on your requirements and how much time you can afford to put some effort into. Yeah, yeah, that makes sense. And what's interesting is this feels very different, but it just kind of echoes the same things you do with like a database in the back end. It's ultimately the same thing. It just happens to be that the node of the database is running.
In someone's machine, so you end up with like almost all the same scenarios and options.
¶ New challenges with local-first
Did you run into any situation where you feel like, okay, this was really easier in the way how you've built software in the past that's now like either currently harder with local-first or like just categorically inherently harder with local-first? Well, it's really that one scenario I talked about before where you do want to block the user. Since the default is inverted, whenever, like you never, you never think like holding the user up until the backend verifies something has happened.
99 percent of developers would never think about that as complicated. It's like, obviously, that's like how everything works. But when you invert the default, that becomes a little bit more challenging and Yeah, we just have to come up with, like, certain patterns around, like, if you do an action, and a server rejects the action, the natural s thing that'll happen is your state locally will revert to the state it was at before you did the action.
But that state has no record of you trying to do the action, so the UI won't have like any error or like information about this. So, doing an action where there was an error on the backend and preserving that error, it just ends up being more steps than it would be otherwise. But again, like we talked about, like this is a minority of cases, so it doesn't come up, but it's just a funny thing where. Something so straightforward in the traditional situation is like so weirdly complicated in this.
So that's one thing that comes up. I think the second thing that comes up is Kind of going back to this thing about like the doing migrations of data locally or the schema changes, there's always a chance, and it's happened more when I was earlier on in doing this stuff. So I think it was just me, like, not doing stuff well, or like not having the right approach to certain things. But, um, Sometimes you would just end up with, like, data that was bad and messing up the user's state locally.
And with any other app, hitting refresh means, start over, you're, like, rebooting your computer, you're, like, back to, back to ground, like, back to zero. But with local-first, you might hit refresh and the local data is still there. That's causing the bug, preventing, like, you know, the fix from being applied. So, just like the, you lose the ease of, like, wiping everything and there's no, like, built in user.
Habits around like, oh, like, let me try clearing my index DB and my browser, right? That's like not a thing that anyone thinks about. So just like guarding those things so you never end up in a state where it's unrecoverable. I think that's, that's for good. Again, it's usually handled on a lower level, so you're not dealing with this constantly, but. It is a technical possibility. Yeah, I like that, uh, that observation a lot. This is also something I'm currently thinking a lot about.
How can I make that easier through LiveStore? Since I also, as working both on LiveStore and Overtone, I do discover those behaviors, whether it's like still me as a developer driving the user experience where it didn't pop open the console and like throw away, like all the, the locally persisted data. But I can't expect an app user to do the same thing.
So I need to build like some sort of, um, guards or some sort of detection of like, Hey, it seems like the app is in sort of like a really tough spot. What should we do about this? Should we try to just like delete a little bit of stuff that might cause the blockage? Or what should we do about that?
And so where, where I'm currently at is I think a mixture of user experience affordances, as well as technical detections, whether something is wrong, I think transactions can help with that in regards to the technical abilities, but then in terms of the user experience, similar to like a React error boundary that you have to detect whether something has gone wrong.
I want to have a similar pattern that allows you to, you know, Click a button that wipe everything, but it before it does wipe everything, download a snapshot of your full database that in case something catastrophic happens, you can, you still have that. You can hand that to an app developer and you can also later re import your app state from there so that you can go back where you've left off in case you wiped it.
So those are like some, some primitives and some, some patterns I'm exploring right now, but I'm, I'm excited to see what other figures out. Yeah, that makes sense. It's also great that you're like, building an actual thing with the tool. There's just like, no better way to like, really understand what problems you need to solve. With SST, like, our console is built with SST. So, any change we make comes from problems we discover. The actual product that we're building.
That's why it's so fun to work on like, developer tools, because you are the user. So you're never like, how do I get in the mind of a user? Like, that's, that problem isn't there. Like, you are the user. It's very easy to figure out what problems there are, um, and discover that. You kind of skip that whole, like, discovery process that usually needs to happen. So if you have someone who's currently like, by default, building a new app with something like Next.
js or Ruby on Rails, and they, they hear about your story and now they're like curious about local-first, but they're not quite sure where to get started. Do you have some tactical suggestions for them? And also in terms of mindset, what do they need to. switch in their brain, uh, that they're successful and can hit the ground running in a local-first mindset.
¶ Advice for builders
Yeah. You know, it's, I don't think I've really thought about like, what's the right onboarding path for people doing this type of thing. I'm like, I know the tools that I use, like I said, we're, we're, we've centered around Replicache at this point, but you know, local-first website obviously has a ton of other options. And there's a spectrum of like what you care about and like how easy it is and how incremental you can add it and things like that.
But the mindset I think really is, it really has nothing to do with local-first. And I think it's a good mindset for everyone. I feel like there has been this shift away from like caring about UX and almost, I feel like there's like a weird proudness to like not caring about UX, which I think is fundamentally wrong. And what I mean by that is. There's a whole meme of like, Oh, if you did this, you shipped too late. Or like people are proud of like shipping bad stuff.
Cause they're signaling that like, I'm just someone that like tries to get stuff done. I'm not like wasting time on stupid stuff, which the Linear people had a great write up about this and it's this whole concept around the idea of an MVP is stupid and it's been stupid for a while, but for some reason, people haven't realized this.
I definitely will acknowledge maybe back when I was starting out, like in the startup world, like, I guess it's been like 10 or 11 years, things were definitely different back then where there wasn't a lot of software. So anything was better than nothing. So you could really ship something pretty quick and like get validation and move on. It's like that whole like mindset kind of made sense back then. But it hasn't been that way in a long time.
So many people ship like something they built in a week and they're like, Oh, it didn't work. Let me move on to the next thing. To be honest, nothing good can really be shipped that fast. It's just not possible. Uh, and you really need to have a strong sense of here's a problem I'm solving. I have good taste around it. I'm going to put in a lot of effort and make a really great experience. And. Whether or not my idea is going to work, you can't know that until you've hit that bar.
And as a Linear people wrote up a whole thing about this on like, we're so obsessed with these like little tiny MVPs. And that's like the weird default that we're all like kind of proud to say that we follow that. Um, and the mindset shift is with local-first, it's getting easier all the time, but it's going to be a lot harder than a lot of things that you've done before because you have so much time to focus on these little details. And you really have to focus on details.
You have to enjoy Letting them and caring about them and making that feel good because that's what that's where the bar is now, whether people realize it or not, the fact is Linear, Linear exists now that it exists. How can you possibly ship anything not at that level? Right? How, like, sure, in the short term, I'm sure you can, but the long term, obviously that becomes a new bar.
So, for me, the mindset shift is like, there is a new bar go use products that are like this, like, Linear is one, like, superhuman is like a smaller example. of stuff that's kind of gotten bigger. That is the bar, and you have to accept that that is a bar. And if you really accept that, then you'll be motivated to like go and figure out all this stuff. Yeah, it's quite different from where I think the mindset is currently.
I think you've very succinctly Summarized my last two and a half years working on Overtone. I'm exactly inspired by that high bar. Sometimes I might even raise it a bit higher just for, for the challenge, but it's so fun to see like all of those details just compounding and, and I like much smoother product experience. And, uh, yeah, I love the the blog posts that you've pointed out from the, the Linear blog.
And I think the only exception really to where the bar needs to be higher is like for just categorically new things. Maybe an exception here is like AI products, uh, like AI products. There is no, no prior version to that. So maybe it is really speed that, that matters most, but I think for every software that replaces something that was there before. I think it's, you have to really, really care about craft. Um, Arc is another example as like to really innovate on a browser.
Like the browser is still the same, what it is before, but now it's like all the compound effect of all of like getting the details right. And that really, that really excites me. So thank you so much for that wonderful summary. Yeah. And there's so much opportunity to do this. Like I'm unhappy with so much of the things that I use every day. So yeah, I just want more people to get excited about doing this type of thing.
¶ Outro
Thank you so much. This has been really, really fun. Thank you so much for coming on. Yeah, appreciate having me. I, I, it was good. I actually don't get to get into some of these details. I don't, I've actually never gotten a lot of these details before. So yeah, it was great. Thank you for listening to the localfirst.fm podcast. If you've enjoyed this episode and haven't done so already, please subscribe and leave a review wherever you're listening. Please also tell your friends about it.
If you think they could be interested in local-first, if you have feedback, questions or ideas for the podcast, please get in touch via hello at localfirst.fm or use the feedback form on our website, special thanks to Expo and Crab Nebula for supporting this podcast. See you next time.
