localfirst.fm

A podcast about local-first software development

Listen

Conference

#24 – Ben Holmes: Astro, Simple Sync Engine & Warp


The guest of this episode is Ben Holmes, a senior web developer and educator known for his whiteboard videos. After having spent most of his career building server-centric applications, Ben recently explored local-first software by building a simple sync engine which we talk through in-depth. 

Mentioned in podcast:

Links:

Thank you to Jazz for supporting the podcast.

Transcript

Intro
00:00What if I'm on my phone, I type a thing and then I go back to
00:02my computer and I try to see it?
00:04Is it gonna merge it together?
00:06Is it gonna break?
00:07I have no idea because.
00:10Client side stores don't help you with those problems.
00:12They're not designed for those problems.
00:14They're designed for like ephemeral state that can disappear at a moment's
00:18notice if it needs to for some reason.
00:21so that led me down exploring local-first technologies and I found pretty quickly
00:26how capable SQLite is for these use cases, including in the browser.
00:32Welcome to the localfirst.fm podcast.
00:34I'm your host, Johannes Schickling, and I'm a web developer, a
00:37startup founder, and I love the craft of software engineering.
00:41For the past few years, I've been on a journey to build a modern, high quality
00:45music app using web technologies, and in doing so, I've been falling down the
00:49rabbit hole of local-first software.
00:52This podcast is your invitation to join me on that journey.
00:55In this episode, I'm speaking to Ben Holmes.
00:58A senior web developer and educator known for his whiteboard videos
01:03after having spent most of his career building server centric applications.
01:07Ben recently explored local-first software by building a simple sync engine
01:11from scratch before getting started.
01:14Also, a big thank you to Jazz for supporting this podcast,
01:18and now my interview with Ben.
01:20Hey Ben, so nice to have you on the show.
01:22How are you doing?
01:24Hey, I'm doing great.
01:26Yeah, how are you doing?
01:27I'm doing fantastic.
01:29Super excited to have you on the show.
01:31I'm very certain that most of the audience already are familiar with who
01:34you are since you have quite a reach on, Twitter, x, other platforms, et
01:40cetera, where I think you're doing an excellent job of taking like novel
01:45concepts and breaking them down in a very simple and approachable way.
01:49And I think you've done the same for some local-first related topics recently.
01:54So maybe some of the audience are already familiar with your work in that regard.
01:59But, for those who are new to you, would you mind giving a background who you are?
02:04Yeah, totally.
02:05I'd be shocked if everyone knows, but if you've seen a guy with a whiteboard
02:09around the internet, it might be me, especially if it's a vertical video.
02:13so I've been doing a lot of work in just the engineering space for a long time.
02:20So been at Astro for a few years and we've been building the
02:26framework for content sites.
02:28And I know actually you have some experience working with Contentlayer
02:31and other things, but content sites are a nice place to get started
02:34because they're a nice, well-defined use case for web technologies and
02:39Astro was trying to spearhead being like the simplest way to do that.
02:43so was able to contribute to that for a long time and also produce a lot of
02:48videos on learnings in the process.
02:51So it started with taking a rock band microphone and a whiteboard outta my
02:55closet and just recording stuff and just seeing where it went and been doing it for
03:00years now and kind of honing the craft.
03:03And we've covered all sorts of topics, including, different
03:07web frameworks, Tailwind tips, database providers using SQLite.
03:12Most recently putting SQLite in the browser, which is gonna be
03:16very relevant today, I'm sure.
03:18and most recently I've made the jump to Warp Terminal, so I'm gonna
03:23be, well, I have joined their team as a product engineer and we're
03:28sort of spearheading the future of bringing AI and agent workflows to
03:33everything you do in the terminal.
03:35So if you forget a command, need to kill a port, need to go through
03:38a full get workflow or even edit files in your projects, you can just
03:43ask Warp to do that for you or fall back to all the features that make
03:46Warp just a really nice terminal.
03:48It's a really great fit.
03:49Been working with them for a while and now get to do that full time.
03:53We'll continue to do all the videos that you see around the internet.
03:56That is awesome.
03:57I'm certainly coming back to Warp since I wanna learn more about that as well,
04:01but taking things one step at a time.
04:04you've been digging into local-first related, you've been mentioning
04:08SQLite running SQLite in the browser.
04:10Looking into that over the course of the last year or so.
04:14I'm very curious, like what led you to explore that since with Astro, et
04:19cetera, you're probably, that's like a different part of building websites,
04:24really where you maybe like Astro is famous for introducing the island
04:28architecture where you go like very light on bringing JavaScript into the, website.
04:35And if it's almost like the other extreme where you want to bring a lot of
04:40JavaScript into the web app, deemphasize the server part, and also more on
04:45the spectrum from website to web app.
04:48Certainly much heavier on the web app spectrum.
04:51So yeah.
04:51Can you share more what led you to, to this path?
04:55well the past year has been kind of like an existential crisis of how are you
04:59supposed to build websites, and I think we all went through that as an industry,
05:03as things kind of shifted back from client side, heavy react apps towards things
05:09that are more server rendered and people are slowly trying to question that idea
05:14again and see what we can learn from storing more information in the client
05:19and sinking it back to your servers.
05:21So I also noticed a wave that's completely opposite.
05:26Of local-first apps, which would be something like HTMX, where everything is
05:32purely server driven, state stateless.
05:36Everything is from like the REST API protocol.
05:40And there's a really nice simplicity to it where a state lives in one
05:45place, the server's a source of truth for everything, and you use different
05:50return values for each HTML sheet to decide what is going to render next.
05:55And you accept that there's going to be network latency for most interactions
06:00on the site, except for very small dropdown toggles and the like.
06:04But anything that involves state is always going to go back to
06:07the server and map it back.
06:09That obviously has trade-offs that people have tried to get away from
06:12with client side architectures.
06:14But the reason it's so nice is you don't have to think about, you have
06:18this server state, you have this client state, and you're constantly trying
06:22to keep them sort of melded together.
06:24And I think that's something that a lot of server side rendered applications have
06:30run into most recently react trying to add on, like use optimistic hooks and
06:37libraries like TRPC, letting you show data optimistically and then replace it
06:41with the server value when it comes in.
06:43And these are important principles, but it's a lot of manual effort to first send
06:49a request to the server and also keep the client in some optimistic version
06:53of that to cut down on network latency.
06:56You're having to pull levers on both sides and you don't really
06:58know where the truth lives.
07:00So, from my experience that's led to a lot of manual work, writing and
07:06rewriting local stores, maybe with Redux back in like the mid 2010s now
07:11using, query hooks, even GraphQL if you're racing for those kinds of tools.
07:16So it's, very messy using those things that are isomorphic, AKA, things that run
07:23both on the server and on the client, and trying to think of it in the same way.
07:28So one response to that is just, we don't need client side JavaScript.
07:32We're gonna do everything on the server.
07:34And that's very easy to understand because it goes back to like how
07:38rest was designed in the eighties.
07:40And then there's the other reaction, which is the, to other side, the
07:43client is the source of truth for pretty much everything that's going on.
07:46And the server's just a broker to keep different clients in sync
07:50and to push changes so everyone can stay up to date or even doing
07:54decentralized servers if you go further.
07:57But it's that other side of the coin.
08:00Of we want one source of truth.
08:02We don't want to think about, we have this server and this client and we constantly
08:05have to write logic to glue them together.
08:07No, you either store all the state on the server, kind of like an HTMX style,
08:12or you store all the state on the client using local-first technologies.
08:16And having explored the first one for a long time, since Astro is meant to be
08:21like a static website or server driven website, I was excited to explore the
08:26other side of storing everything on the client, keeping it up to date, and then
08:30figuring out how synchronization happens.
08:33That's also been around for a long time.
08:35If you look back to just like the first calendar app on your phone or on your
08:40computer, this challenge has been around since probably SQLlite was created.
08:44but it's only now that web devs are starting to get a lot of footholds
08:48to also apply this to websites if that's something that you need.
08:53I think you've summarized it really well.
08:55And, I would go even as far as saying there is an elephant in the room that
09:00most people are aware that there is an elephant, but they really don't
09:04have yet the, the right terminology.
09:06And I would say the term here of the elephant is distributed systems.
09:10We have distributed states and distributed systems are really, like,
09:14that's a core discipline of computer science that doesn't really have
09:19just like the good answer, but it's a really, really hard problem alongside
09:24of like naming things and so on.
09:26But, that part, like everyone who's building a web app,
09:30anything that has state.
09:32On the server, like even in the server, typically you have by definition
09:37also already a distributed system where you have states in your API
09:41throughout the request lifecycle, but then you also have it in the database.
09:45You might have like concurrent requests.
09:48So there you already have a distributed system.
09:50Typically there it's much less bad because they're like, you can just
09:55trust the server already less.
09:57So you just trust the database.
09:59So you basically push it all down to the single choke point.
10:02But if you wanna trust the client even more now all the distributed
10:07parts are get the distance grows and therefore the divergence.
10:12And I have in university, when I studied computer science, I don't
10:15think I've actually taken a class on distributed systems and I've missed a
10:20memo where someone would've instilled it in me is like, Hey, everything
10:24that are you gonna build will suffer from distributed systems, make sure to
10:28understand this problem properly and then design the architecture around it.
10:31And I think most web devs are not aware of that.
10:35And what you've just laid out, I think is exactly suffering from this problem.
10:41And this is what I think where some framework creators are making
10:45the very smart decision to empower the server as much as possible.
10:50Because if you live by that sort of maxim, at that point, the implied
10:57damage that you can cause by building it in a certain way is minimized.
11:02And, my journey over the last five years or so has been almost like
11:06intentionally letting the pendulum swing to the most other extreme where all the
11:11state is governed by the client, since I'm pretty convinced that the middle
11:16ground is just pain and suffering.
11:19So I'm pretty convinced that you should really analyze the use case that you have.
11:24And if it's a daily driver productivity app, you probably wanna move as much
11:29state to the client as possible.
11:31Where it happens, like you produce most of your state in your calendar app or in
11:35your Notion, or in your other notes app or whatever you want to have it declined.
11:41And, if it's something like New York Times, then you don't
11:45produce any of that date.
11:46So it should be on the server.
11:48And, I, feel like more people should, start with that assumption, then
11:52build an architecture around it.
11:54But, yeah, I think you summarized it super well.
11:58Yeah.
11:58And it is tough.
12:00To prescribe either side because there are neat buckets like content sites
12:06and client side apps like Notion.
12:09But the classic example is, well what about e-commerce where things are
12:15server driven until you're in the add to cart flow and now it's client driven.
12:18So what do you do then?
12:19How do you architect it?
12:21We actually met this challenge building the Astro storefront front template,
12:26which was mostly server driven with some client side components sprinkled in.
12:32I think the answer there was, it's still fine to leave things server
12:36side and not rely on optimistic updates too much, except for like
12:42small examples like increase and decrease quantity in your cart.
12:47Do you want that to feel instant and happen on a delay or a debounce but
12:51everything else is server driven.
12:53There is no perfect architecture, I guess is what I'm saying.
12:56It's more painful when you really try to blend it 50 50 and nail every use case.
13:01But if you can keep it 80 20, where 80% is in one realm and 20% is business logic in
13:07the other realm, like 80% server driven, 20% client side complexity or less, then
13:13you're kind of minimizing the footprint of pain that you could run into since I've
13:18certainly noticed that with some of the newer patterns and older patterns with.
13:23Client side apps.
13:24but there was a second thing you mentioned about distributed
13:26systems that I, totally agree with.
13:29I was reading the book Designing Data Intensive Applications, the big
13:35O'Reilly book, probably the first one you find looking up like CS principles.
13:40And I think there was one section about distributed systems where it walked you
13:44from like single leader replication.
13:47You got one database and if you want to have some caches to make reads
13:52faster, you just put all your rights to one database and then it'll replicate
13:56the reads out to everyone else.
13:58So you can't write to a bunch of different regions.
14:00You can write data to one region and then it'll sort of.
14:04Push out all of the clones and allow eventual consistency to work.
14:09but there are cases where that doesn't work anymore.
14:12Like in Notion, I cannot wait for the server to update the document.
14:16It's just gonna update when I'm typing and when I add blocks and when people
14:19are invited to join the document.
14:21All that stuff matters.
14:22So you need some way to be able to write to maybe the nearest node.
14:28Like if you go fancy with CloudFlare, you could have really localized,
14:31durable objects that are two feet away from your computer and that reduces
14:35latency in some sort of magical way.
14:38but then it kind of explains well if you just put a data replica just on the
14:43client device, that's another version of multi leader replication, where
14:49instead of replicating in some really edge node, you're just replicating
14:53on the person's computer and everyone is like a mini server unto itself,
14:57where you just read and write data and they'll all report back to some
15:01centralized source of truth later on.
15:04So when you're working with, like, is it a distributed system?
15:07It is.
15:08Even when it's running on your device, if there's some sort of synchronization
15:12layer, you're just moving it closer and closer and closer to the computer
15:15until it iSQLiterally in the computer.
15:18It's inside the house.
15:20I've, heard a, very interesting framing of sort of like that
15:24elephant in the room and like that problem that we just talked about.
15:27I think it's by Carl who worked on SQLSync and recently, released Graft,
15:32which is another fascinating project.
15:35And he has written a blog post about, don't recall the exact name we're gonna
15:39put it in the, show notes, but it was basically along the lines of like, your
15:43application is a database and that's bad.
15:47I gotta look it up.
15:48what the, exactly the title was.
15:49But it was basically, he's making the point that every app that is sufficiently
15:56getting more complicated will basically build its own version of a ad hoc database
16:03and if you're just using React useState or something else enough, and then
16:08you try to add persistence, et cetera.
16:10What your app is basically becoming is a poor implementation of a database
16:15where like all the things that a database does a lot of like R&D great
16:21work to make it fast, to make it correct, to make it like nicely, like
16:27transactionally correct, et cetera.
16:29All of those things you're now trying to handle through, useState and useEffect
16:35and like when this thing changes, also change that thing, et cetera.
16:40And, I just thought that framing was so elegant and, his conclusion, which
16:45I agree with is like, hey, if let's try to make the app about what the app tries
16:50to do and let's leverage a database so we can focus on the actual like
16:55features, et cetera we want to implement.
16:58And I think that's another kinda articulation of the elephant in the room
17:03that we're accidentally and without being aware of it Building databases as that
17:09are sort of camouflaged as apps and, we should embrace it more if we're starting
17:14to see those signs of those problems.
17:17So you've mentioned that you've gone down this path, a lot out of curiosity and
17:22just because you've, pretty exhaustively explored the more server centric
17:27paths, can you share more about like what were your frustrations and pain
17:31points that you felt where you wanted to go more, like embrace the client
17:35more, but still trying to do that with the more server centric architecture.
17:41So you've mentioned optimistic state, et cetera, maybe can motivate
17:45this through a concrete app you wanted to build where you just felt
The pain points of embracing the client
17:51Yeah, I mean I've played with all sorts of side projects as we all have.
17:56and I was working on a few different things.
18:00One was like a localized note taking app, and I was also playing with local
18:06LLMs and other pieces to add vector search into a local environment because
18:12I was running up on the limits of using Notion and being very frustrated with
18:17loading spinners and offline warnings when I was trying to use the app.
18:21I hear that's being changed and rectified as they build out their
18:26own, like SQLite replication.
18:29I know that's in there.
18:29There's some fascinating videos about that on the internet.
18:33but I still do see the value of just open up Apple Notes, you type things and it's
18:39just kind of there in a SQLlite store.
18:40You can find it on your file system.
18:42All your Apple Notes are just in a SQLlite store and it's great.
18:45so I thought, well, it seems like that's the answer.
18:48If I were to go the traditional server route, I would sit here
18:51waiting for all these updates to persist, which just wouldn't work.
18:55Or I'd be sort of gluing together a bunch of useState calls and figuring
18:59out how do I update this server and what if someone else updates it?
19:02What if I'm on my phone, I type a thing and then I go back to
19:04my computer and I try to see it?
19:06Is it gonna merge it together?
19:08Is it gonna break?
19:09I have no idea because.
19:12Client side stores don't help you with those problems.
19:14They're not designed for those problems.
19:16They're designed for like ephemeral state that can disappear at a moment's
19:21notice if it needs to for some reason.
19:23so that led me down exploring local-first technologies and I found pretty quickly
19:28how capable SQLite is for these use cases, including in the browser.
19:34you can load up SQLite with like a wasm build as you're
19:38very aware and use it even with.
19:41Like very nice SQLite libraries.
19:45If you wanted to use Drizzle for example, which is a common SQL querying library
19:50and JavaScript, it matches onto the browser version of SQLite perfectly.
19:55So you can actually make declarative, find many notes and
19:59it'll just do the little SQL query.
20:01It'll join it up with all the authors of the post and it'll
20:04just give it back to you.
20:05Kind of like you're on the server, but you're on the client.
20:08And you can use client side ORMs or query builders, whatever
20:12your flavor of preference.
20:14So that was very empowering to see.
20:16Yeah, you can bring all of these niceties you get from server side data
20:20querying and bring it into the client.
20:23And I tried to stretch it a little bit further by asking,
20:26what about SQL extensions?
20:28Could I add a vector search plugin, for example?
20:32The answer is yes.
20:33There actually is a vector search plugin that I think is developed
20:36by someone on the Mozilla team.
20:38So it is pretty battle tested in different languages.
20:41I think they're sponsored, yeah I've had the chance to meet them in October
20:47when I was in LA super lovely person.
20:50And, they have some sponsorship from the Mozilla team currently.
20:55Nice.
20:56Yeah, and I was playing with the, rust flavor of that since, well now
21:01I'm working at Warp, and Warp is a rust powered terminal, so naturally I
21:05need to get up on the Rust knowledge.
21:08And also if you build apps with Tori, which is a native desktop application
21:13tool that uses Rust as well, so.
21:16Side tangent, but it is nice to use tools that could work in JavaScript as well
21:19as rust in very efficient languages.
21:22so I reached for that.
21:23I put vector search in.
21:24I was also able to run an entire LLM across the data in the
21:29browser and just load up like the entire vector search setup thing.
21:35and I just said, all right, I'm gonna backport all of my
21:38markdown files into this thing.
21:40I'm gonna vectorize all of them in the browser.
21:42I'm gonna search them in the browser.
21:44And I was able to get it working with like next key search.
21:48And it was absolutely mind blowing.
21:49Like this is like an actual AI powered search tool.
21:53And you can get like with every keystroke new results
21:57And is isn't that wild like that this machine that we have like sitting
22:01on our laps or like this machine here in my hands that is capable
22:07of all of those things and just the way how we kind of build web apps.
22:11Over the last decade or so, we've kind of forgotten or denied the
22:16capabilities of our client devices.
22:18And we only just like trust the server and we've almost like, why did no
22:22one tell us that this is possible?
22:25And that's so magical when you see that this is working and just the
22:28stuff that's currently, in the works with like web, LLM, et cetera, where
22:32it can run like a full blown like Llama model locally in your browser.
22:37running on web GPU is absolutely wild to the capabilities that we have.
22:43But I think what's holding us back is where does our data
22:47live and everything else.
22:48Kinda like, it's almost like a second order effect from where the data lives.
22:52And this is what, your anecdote really nicely highlights of like, you go with
22:57SQLite with your data and then you like bring in another superpower of like SQLite
23:02Vec with the vector embedding, et cetera.
23:05And I think it all starts with where the data is, how your
23:08application is being shaped.
23:10Yeah.
23:11And it is good to find that.
23:13Just common data layer.
23:15SQLite is the easy answer.
23:17PG light is a more robust exploration of bringing Postgres to local devices.
23:22That's a bit earlier on.
23:24but I think we're entering a world where software is just so easy to spin
23:28up that you will very quickly have a web client, a mobile app client,
23:33a desktop client, and they're all talking to the same sync server.
23:38And when you're in that world, it's nice to just reach for SQLite.
23:40'cause I can run SQLlite on my iPhone.
23:42I can run SQLlite on my Android device, I can run it in the browser and I
23:46could run it in a desktop application.
23:48So as long as you just have this concept of clients write to SQL servers and
23:54those SQL servers have some way to talk to each other, then you can build
23:58these multi-platform applications very quickly, even across different languages.
24:04And just figure out what that sync layer looks like, which
24:07we can probably talk about.
24:09that would've been my next question since, I think what you started with
24:12was probably without the sync part yet, where you can just locally in
24:17your browser web app, you successfully ran SQLite using the wasm build.
24:23Then you brought in SQLite Vec, you could, get the AI magic to work.
24:27You saw like how insanely fast everything feels.
24:30You write something, you reload the browser.
24:33Even if you're offline, it's all still there.
24:35Great.
24:36But now you're thinking, okay, it works on like local host 3000.
24:41how do I get it deployed and how do I get it so that, if I accidentally
24:47open this in a cognitive tab and I close it, poof, everything is gone.
24:52and how do I get it show up on my, phone so this is kind of
24:56collaborating with yourself, but on also collaborating with others, which
25:00we maybe punt on that for a moment.
25:03But yeah.
25:03How did you go from, it works locally almost like if you use like just
25:09local storage to trying to share the goodness across your devices.
From working locally to working across multiple devices
25:15Well, I think we both have the privilege of just ask Andrew on the Rocicorp team
25:19and he'll point you to some resources.
25:21if you don't have that, I do recommend that.
25:25Well, the one approach that I used was just reading through the Replicache docs.
25:31That explain how their sync engine works on a very high level.
25:35And Replicache is a batteries included library, well some batteries included
25:42library that sets up a simple key value store for you where you can write and read
25:48keys on the client and you can author code that will post those changes to a server
25:54that you own and pull changes back from the server whenever changes are detected.
25:59And you can implement that on a SQLite or anything else if you just have
26:04their simplified mental model, which is very inspired by just how Git works.
26:09So in Git, if you had to change locally and you wanted to push it up to the
26:14main line, you would run Git push.
26:17And the same kind of thing happens with their sync engine service, where
26:21anytime you make a change locally and you can decide what that means,
26:25maybe it's a debounce as you're editing a document, maybe it's when
26:30you click on a status toggle in Linear and you wanna change that status.
26:34Whenever the trigger is, you can use that to call push, which would probably call
26:40an endpoint written on your own server.
26:43That's simply a push endpoint and that can receive whatever event you ran
26:49on the client so that the server can replay it, back it up into its own
26:53logs and tell other clients about it whenever they try to pull those changes
26:58back down so you can run your own little push whenever you make a change.
27:03And then other clients on an interval or a web socket or some
27:07other connection can pull for data whenever they want to get the most
27:11recent changes made by other people.
27:14Now the question would be, what am I pushing and what am I pulling?
27:18Like what data?
27:20Needs to be sent across.
27:22And there are a couple different methods.
27:25I know Replicache uses a data snapshot mechanism.
27:30I don't fully know the intricacies of it, but I know that because they use
27:35like a key value storage, which is much simpler than like a full-blown database.
27:39They can take a snapshot of what the server looks like right now and then the
27:44client has its own copy of that server snapshot and it can just run a little
27:47diff the same way you'd run a get diff to see what code changes you have made.
27:52It can run that diff and you can see, all right, I added this key.
27:55All right, updated this status flag, and then tell the server,
27:58this is the diff, this is the change that was made to the data.
28:02And the server can receive that request and say, okay, this is the change
28:06that I need to make against my copy.
28:08And then I will tell other clients to also apply that diff whenever they pull.
28:13it's a very Git inspired model, and it works if you're able to diff data easily.
28:19If you are working with like a full blown SQLlite table, you run table
28:23migrations, tables, change shapes, that's a very hard thing to keep track of.
28:27So another option that I implemented for the, learning resource I
28:33created called Simple Sync Engine.
28:35If you find that on my GitHub, probably in the show notes, you can see that.
28:40But it was meant to be a very basic implementation of this pattern
28:43that uses event sourcing instead.
28:46So rather than sending a diff of how the data should change, instead sends
28:52an event that describes the change that is made like a function call or
28:57an RPC, however you wanna think about it, where you would tell the server
29:01I updated the status to this value.
29:05And in our implementation we create these little like function helpers that
29:09can describe what those events are.
29:11So you might have like an add status event.
29:13So like the type of the event is add status and that accepts a few arguments.
29:19It accepts the status you wanna change it to, and the ID of the
29:22record that has that status currently.
29:25So the server receives that event, it sees, okay, the type was set status.
29:30I see two arguments here.
29:31The ID of the record.
29:33And the new status that should be applied.
29:35I'm gonna go ahead and run that event as a database update.
29:39So it has that mapping understanding of, I received this event, I know that
29:43maps to this SQL query, so I'm gonna go ahead and make that change on my copy.
29:48And then whenever people poll, you can send that event log out, or you
29:52can send whatever events the client hasn't received up until that point.
29:57You can kind of think of those like Git commits where you're pulling
30:00the latest commits on the branch and the server's able to tell you, here
30:04are the latest events that were run.
30:06Go ahead and run those on your replicas so both the server
30:10and client know what events.
30:12Actually mean, like this event means I need to make this SQL query.
30:16And it's able to do that mapping.
30:17and you can have a bit of freedom there.
30:19If you had like a very specific kind of data store on the server, like
30:24MongoDB, you could customize it to say, whenever I receive this event, it
30:28means this MongoDB query and this call to the Century error logging system,
30:33or whatever middleware you wanna do.
30:35As long as server and client agree on what events exist and what changes they
30:40make in the data stores, then everyone can be on the same page whenever
30:44they're syncing things up and down.
30:46you're very familiar with event sourcing as well.
30:49I'm curious if there's things that I've missed or important edge cases
30:53that we should probably talk about.
30:55I think you very elegantly, described how simple the foundation of this can be and
31:00hence the name of like Simple Sync Engine.
31:03I think this has served as a great learning resource for you and
31:06I'm sure for many, many others.
31:08And once you like, start pulling more on that thread, you realize, oh shit like,
31:13okay, that thing I didn't think about.
31:15Oh, what about this?
31:16So you've mentioned already the reason why you preferred, event sourcing over
31:22the snapshot approach because like with SQLlite, What would you actually compare?
31:27This is where I would give a shout out again, to Carl's work with Graft.
31:32this is what he's been working on.
31:33We should have him on the show highlighting this as well.
31:36But, where he's built a new kinda sync engine that, is all about that
31:41diffing of like blocks of storage.
31:45I think all focused around SQLite and that gives, that does the heavy work
31:50of like, diffing and then sending out the minimal amount of changes
31:55to make all of that efficient.
31:56Since there's this, famous saying of like make it work, make it right
32:02or correct and then make it fast.
32:04And working on all of this has really given me a deep appreciation for all
32:09of this since like, and I'm sure you probably also went through those stages
32:15with the Simple Sync Engine, like with making something work in the first place
32:19was already quite the accomplishment.
32:21But then you also realize, ah, okay, in those cases this is not quite yet correct.
32:25And then you could go back and like try to iterate and then you also realize,
32:30okay, so now it has worked for my little to-do app, but if I, now, depending
32:36on the architecture, if I roll this out and put in hack news and suddenly
32:40have like 5,000 people there on the same time, this thing will break apart.
32:45Will A not be correct in some ways you didn't anticipate and
32:48will also not be fast enough.
32:50So now making this fast.
32:52It is at the end of the day is like really reinventing.
32:55It's like your app is becoming a little database and you want
33:00to like move as much of that database burden to the sync engine.
33:05This is why folks like Rocicorp, ElectricSQL, et cetera, they're
33:09doing a fantastic job trying to absorb as much of that complexity.
33:13But building something like this by yourself really gives you an understanding
33:18and an appreciation for what is going on.
33:21I love the Git analogy that you've used, but just a, a couple of
33:25points just similarly to how, your sync engine works is actually very
33:30analogous to how the Livestore architecture on a high level works.
33:36But I've had to, before I arrived at that, I really wanted to think through a lot
33:42of like the more further down the road.
33:45Like, what if situations since, one that I'm curious whether you've
33:50already run into, whether you resolved in some way or left for the
33:53future, is how would you impose a total order of your change events?
33:59So this is where When you have, like, let's say this to-do app
34:04or like a mini Linear, app.
34:06Let's say you create an issue and then you say you, complete the issue or
34:12you toggle the issue state, et cetera.
34:14It can mean something very different, if one happens first and then the
34:19other, or the other way around.
34:21And for that, where you have your events of like, hey, the issue was created,
34:26the issue status was changed, this, the issue status was changed, that,
34:30the order really matters in which way in which order everything happened.
34:36And, this might be not so bad if you're the absolutely only person using your
34:40app and typically only on one device.
34:43But when you then do stuff between multiple devices or multiple
34:46users, then it's no longer in your single user's control.
34:50That stuff happens concurrently.
34:53And then it really matters that everyone has the same order of the events.
34:58And this is where you need to impose what's called a total order.
35:02And I'm very curious whether you've already, hit that point, where you thought
35:06that through and whether you found a mechanism to impose it since there's many,
35:10many downstream consequences of that.
Imposing a total order
35:13Right.
35:13And I definitely did hit it and.
35:16You will find in the resource, it's not addressing that issue right now.
35:20It's in a very naive state of when change is made, send, fetch, call to server.
35:26And there are a few problems with that, even if you're the only client.
35:30Because first off, if you send one event per request, it's very possible that you
35:35send a lot of very quick secession events in like an order, and then they reach the
35:41server in a different order because maybe you pushed the first change on low network
35:47latency, the next one on high latency, or actually the reverse of that where
35:50the first change hits after the second change because that's just the speed of
35:54the network and that's what happened.
35:56And also you need to think about offline capabilities.
36:00If you push changes when they happen, how do you queue them
36:04when you're not connected to the internet and then run through that
36:07queue once you're back online?
36:09That's another consideration you kind of have to think about.
36:12Could be solved with just like an in-memory event log and
36:15just kind of work with that.
36:16But you still have the order issue.
36:19I'm familiar with atomic clocks as a method to do this.
36:23There are even SQLite extensions that'll sort of enforce that, having
36:28not implemented atomic clocks.
36:30Is it kind of this silver bullet to that problem or are there more
36:34considerations to think about than just reaching for something like that?
36:38Right.
36:39I suppose you're referring to vector clocks or logical
36:42clocks on a more higher level?
36:43Yeah.
36:44since the atomic clocks, at least my understanding is like that's
36:47actually what's, at least in some super high-end hardware is like
36:51an atomic clock that is, like that actually gives us like the wall clock.
36:56So Right, right now is like.
36:58Uh, 6:30 PM on my time, but this clock might drift, and this
37:03is what makes it so difficult.
37:04So what you were referring to with logical clocks, this is where it basically,
37:09instead of saying like, Hey, it's 6:30 with this time zone, which makes
37:14everything even more complicated, I'm keeping track of my time is like 1, 2, 3.
37:20It like might just be a logical counter, like much simpler
37:24actually than wall clock time.
37:26but this is easier to reason about and there might be no weird issues of
37:31like, Daylight saving where certainly like the, the clock is going backwards
37:36or someone tinkers with the time, this is why you need logical clocks.
37:40And, there, at least the mechanism that I've also landed on to
37:44implement, to impose a total order.
37:47But then it's also tricky, how do you exchange that?
37:50how does your client know what like three means in my client, et cetera?
37:54And the answer that I found to this is to like that we all trust.
38:00A single, authority in the system.
38:02So this is where, and I think this is also what you're going for, and with the Git
38:07analogy, what we are trusting as authority in that system is GitHub or GitLab.
38:13And this is where we are basically, we could theoretically, you could
38:17send me your IP address and I could try to like pull directly from you.
38:20It would work, and that would also work with the system that you've built.
38:25However, there might still be, they're called network petitions,
38:29where like the two of us have like, synced up, but some others haven't.
38:33So as long as we're all connected to the same, like main upstream node, that
38:39is the easiest way to, to model this.
38:41An alternative would be to go full on peer to peer, which makes everything
38:46a lot, lot, lot more complicated.
38:49And this is where like something, like an extension of logical clocks called
38:53vector clocks, can come in handy.
38:55you've mentioned the, the book, designing dataset intensive application by Martin
39:00Kleppman had him on the show before.
39:02he's actually working on the version two of that book right now, but he's also done
39:06a fantastic free course about distributed systems where he is walking through all of
39:12that, with a whiteboard, I actually think so, I think does what, what the two of
39:18you have very much like you've both nailed the, craft of like showing with simple
39:24strokes, some very complicated matters.
39:27so highly recommend to anyone who wants to learn more there.
39:31Like, learn it from, from Martin.
39:33He's, like an absolute master of explaining those difficult
39:37concepts in a simple way.
39:40But, yeah, a lot of things go kind of downstream from that total order.
39:45So just to, go together on like one little journey to understand like a downstream
39:51problem of this, let's say we have implemented the queuing of those events.
39:56So let's say you're currently on a plane ride and, you're like.
40:00Writing your blog post, you're very happy with it.
40:03You have now like a thousand of events of like change
40:07events that captures your work.
40:09Your SQLite database is up to date.
40:12but you didn't just create this new blog post, but you maybe while you're still at
40:16the airport, like you created the initial version with it with like TBD in the body.
40:21And your coworker thought like, oh, actually I have a lot of thoughts on this.
40:26And they also started writing down some notes in there.
40:29And now, the worlds have like, kind of drifted apart.
40:33Your coworker.
40:35Has written down some important things they don't want to lose,
40:38and you've written down some things you are not aware of the other ones
40:42neither are they, and at some point the semantic merge needs to happen.
40:48But how do you even make that happen in this sync engine thing here?
40:52And this is where you need the total order, where you basically, in the worst
40:57case, this is what decides, like who, gets a say in this, who gets the last say, in
41:04which order those events have happened.
41:07The model that I've landed on, and I think that's similar to what Git
41:12does with rebasing, is basically that before you get to push your own stuff,
41:18you need to pull down the events first, and then you need to reconcile
41:22your kind of stash local changes.
41:26On top of the work that whoever has gotten the, who got lucky enough to push
41:32first without being told to pull first.
41:35So in that case, it might have been your coworker because they've
41:39stayed online and kept pushing.
41:41And now it sort of like falls on you to reconcile that.
41:46And I've implemented a, like an actual rebase mechanism for this,
41:51where you now have this set of new events that your coworker has
41:56produced and you still have your set of events that, reflect your changes.
42:01And now you need to reconcile this.
42:03So that is purely on the.
42:05Event log level, but given that we both, want to use SQLite now, we don't
42:12need to just think about going forward with SQLite, but we also now need to
42:17think about like, Hey, how do we go?
42:19Like in Git you have like, you have this stack of events, right?
42:24So you have like a commit, which has a parent of another commit, which
42:27has a parent of another commit.
42:29It's very similar to how your events and this event log look like, except it's now
42:36no longer just one event log, but you also get this little branch from your coworker.
42:41So now you need to go to the last common ancestor.
42:44And from there you need to figure out like.
42:46How do I linearize this?
42:49I've opted for a model where everything that was pushed once cannot be
42:53overwritten, so there's no force push.
42:55So you basically just get to append stuff at the end.
42:59But, in order to get there, you need to first roll back your own stuff, then
43:05play forward what you've gotten first.
43:08and then on top add those.
43:10And the rolling back with SQLite is a, thing that I've like put a lot of
43:15time into where I've been using another SQLite extension, called the SQLite
43:21Sessions extension, which allows you, per SQLite write, to basically, record
43:27what has the thing actually done.
43:30So instead of storing, insert.
43:33Into issues, blah, blah, blah.
43:35when running that, you get a blob of let's say 30 bytes, and that has
43:40recorded on SQLite level, what has happened to the SQLite database.
43:46And I store that alongside of each change event, that sits in the event log.
43:53And the very cool thing about this is, I can use that to replay it
43:57on top of another database, but to kind of catch it up more quickly.
44:01But I can also invert it.
44:03So now I have basically this like, let's say 20 events.
44:07And for each, I've recorded what has happened on SQLite level,
44:11and now I can basically say.
44:13When I need to roll back, I can revisit each of those, invert each of those
44:17change sets, apply them again on the SQLite database, and then I'll end up
44:22where I was before and that's how I've implemented rollback on top of SQL Lite.
44:27So this is as mentioned when you're going, down the, rabbit hole
44:32of like imposing a total order.
44:34There's a lot of downstream things you need to do that makes
44:37this even more complicated.
44:39But, from what I can see, you're, on the right track if
44:43you wanna pursue this further.
44:45Yeah.
44:45And I do have a rebasing mechanism in place in mind that's more,
44:52just kind of a sledgehammer.
44:53I got two SQLite databases in mind.
44:56in the same way that on Git you have like your local copy of the main line and your
45:00local copy of your work, there's always this local copy of Main, that's just
45:05whatever events have come from the server.
45:07So this is the source of truth that the server has told me about and that was
45:12something I forgot to mention earlier.
45:13Explaining all of this is the server is the source of truth.
45:16It has that main line of the order of all of the events, and that is
45:21what all the clients use to trust.
45:23But yeah, it has like that local copy, and then when it pulls from
45:27the server, it'll update that copy.
45:29It'll look at all the events that are kind of ahead in the client,
45:33and then it'll say, okay, I'm gonna roll back my client copy of my
45:39branch to whatever the server is.
45:41And it's literally just a file right call.
45:43So it just overwrites.
45:46Your like client SQLlite file with a copy of the server one.
45:50And then we look at the events that the server didn't acknowledge yet
45:53and then we replay those on top as a very basic way to pull and make
45:58sure, because it's very possible that you made some changes locally that
46:02the server hasn't acknowledged yet.
46:04Like you've pushed them up still in process and you pull down the
46:08latest changes and you don't see all of that stuff that you pushed
46:11up yet because of network latency.
46:14So this sort of avoids that problem where you pull down from the server
46:18and now you need to replay whatever you did on the client that the
46:21server hasn't acknowledged yet.
46:23It hasn't received that network request.
46:25So that was a very basic need to have some rebasing, but it does
46:30get a lot more complicated when you have collaborators on a document.
46:34I've seen a few different versions of this.
46:37CRDTs is the fun, like magic wand.
46:40It does everything.
46:42but there are also solutions from Figma, for example, where they
46:47say everything in Figma is kind of its own little data structure.
46:50Like you can put some text and that's its own little data field.
46:54You have rectangles.
46:54Those are a data field.
46:56And whenever you update a rectangle, like you update the pixel width of
47:01a rectangle, that's like an update event on some SQL table that stores
47:05all the rectangles for this document.
47:07So whenever you make that update, it'll update the pixel value of whatever
47:12that row entry is, and then it'll push it up for other people to receive.
47:17And when you pull it down, it's last right wins.
47:20In other words, whoever the last person is in that order that the
47:24server decided on that total order.
47:26That's a new word I know about now.
47:28Didn't know it was called total order, but yeah, that, once you pull it down,
47:31whatever the server said was the order of events, that's gonna be the final
47:35state of that rectangle on your device.
47:38The only time it becomes a problem, and you may have experienced this, if you're
47:41ever working on like a fig jam together with a bunch of people, if you're all
47:45typing in the same text box, everyone's just like overriding each other and a
47:48text box glitches out and changes to whatever's on the other person's screen.
47:52You can't see people's cursors because you're fighting to update
47:55the exact same entry in the database and it can't reconcile those changes.
48:00so it only works up to, like you're editing different things
48:04in the file and you're not really stepping on each other too much.
48:08As soon as you're stepping on each other trying to edit like the same text field,
48:12then you wanna reach for something that's very, very fancy, like CRDTs.
48:17Which will try to merge elegantly all of the changes that you're
48:20typing into the same database field.
48:23It's maybe over-prescribed because of how powerful it is, but for those specific
48:28scenarios, it's really nice to reach for, and we can talk about them if you want.
48:32I only have a high level understanding of what CRDTs do, but it would be
48:36something to apply that kind of problem.
48:39my takeaway from where to apply, CRDTs versus where I would apply event sourcing
48:45is, CR DTs great for in two scenarios.
48:51One, if you don't quite know yet where you want to go.
48:54And where in the past you might've reached for, let's say, Firebase to
48:59just like have a backend of service.
49:00You know, you might want to change it later, but you just, for now,
49:04you just want to get going and, you can, particularly if you
49:08don't have like a strict schema across your entire application.
49:12So you just try to like, not go off the rails too much, but at least the
49:17data is like, mostly, like across the applications in a good spot.
49:22But as you roll this out in production, and, we are shipping
49:26an iOS app as well, that someone is, running an old version on.
49:31Now you don't quite know, oh, this document, this data document that has
49:35been synced around here, this might not yet have this field that the
49:39newer application version depends on.
49:42So now you have, like, this is where time drifts in a more significant
49:47way and in the more traditional application architecture approach
49:52you would, this way you don't trust the client in the first place.
49:54Then you have like your API endpoint and the APIs, versioned, et cetera, and
49:58everything is governed through the, API.
50:01But now you also need to tame that problem somehow.
50:03So at this point you're already, going a little bit beyond where I
50:07think CRDTs shine right now, which brings me to my next kind of more
50:12evergreen scenario for CRDTs, which are like very specific, tasks.
50:19And so text editing, particularly rich text editing.
50:22Is such a scenario where I think CRDTs are just like a very, very good, approach.
50:28There's also like, you can also use ot, like operational transform, which
50:32is, somewhat related under the covers, works a bit differently, but the way how
50:37you would use it is pretty similarly.
50:40And, related to rich text editing is also when you have like complex
50:45list structures where you wanna move things within the list.
50:49So if you want to go for the, Figma scenario, let's say you change the
50:55order of like multiple rectangles, like where do they sit in that layer order?
51:01how do you convey how you wanna change that?
51:04You could always, have like maybe an array of all the IDs that give
51:08you this perfect order, but if this kind of happens concurrently,
51:13then you need to reconcile that.
51:14So that's not great.
51:16And this is where CRDTs are also like a very, special purpose
51:20tool, which works super well.
51:23And so what I've landed on is use event sourcing for everything except
51:28where I need those special purpose tools, and this is where them reach
51:33for CRDTs or for something else.
51:35That's kind of the conclusion I, took away if you like the event sourcing approach.
51:41But, I think ultimately it really comes down to what is the application
51:46that you're building and what are, like, what is the domain of what
51:51you're building and which sort of trade-offs does this require?
51:54So I think in Figma.
51:56The real timeness is really important and it is recognized that those different
52:02pieces that are floating around, they're like pretty, independent from each other.
52:07So, and if they're independent, then you don't need that total order
52:10between that, which makes everything a lot easier in terms of scalability,
52:14in terms of correctness, and then you don't need to rebase as much.
52:18distributed systems is the ultimate case of it depends.
52:22and I think trying to build one like you did, I think is a very good way
52:28to like build a better understanding.
52:30And also I think that opens your eyes of like, ah, now I understand why Figma
52:35has this shortcoming or Notion if we are trying to change the same line, change the
52:40same block as where last writers, applies.
52:43Whereas in Google Docs, for example, we could easily change the, same word even.
52:49And it would reconcile that in a, in a better way.
52:52But, maybe you have some advice for people like yourself when you're
52:57just getting started on that journey.
53:00What would you tell people what they should do maybe shouldn't yet do?
53:05today 2025?
53:07There's more technologies out there now.
Beginner advice
53:12Depends on the type of learner you are.
53:13Sometimes some are very.
53:16outcome driven, like I need to see an app running in production for me to
53:21really get excited about this Tech.
53:24Other people are very first principles driven.
53:26Like I want to like screw in every nut and bolt myself to
53:30get excited about this thing.
53:32I tend to fall into the first camp where I think it is very useful to just
53:35look at the docs for something like Replicache and see how would you implement
53:39this kind of protocol step by step.
53:42Like how would you set up the event sourcing?
53:45How would you put the SQLlite store in the browser in the first place?
53:48Like what capabilities are there?
53:50And then try to think through those edge cases.
53:53As you run into them trying to build something, I use Linear as my sort
53:58of learning example, but you could use pretty much anything you want.
54:02so that's definitely one approach.
54:03Now there's just so many resources for how these things work under
54:07the hood that you can easily learn about the intricacies yourself.
54:11Another, resource is the talks given by who's the engineer at Linear.
54:18I think you've had him on the show.
54:19Tuomas?
54:20Yes.
54:21Yeah.
54:21He gave a few really helpful talks about how the Linear sync engine works on a high
54:28level, and that one's more opinionated.
54:30It reaches for technologies like MobX, which is a react specific state
54:36store, and also MongoDB for documents.
54:39but you still get a high level of how they think about the
54:41problem, which is really nice.
54:43the other option, if you're really results driven, you wanna
54:46see a local-first step running.
54:48You can reach for all sorts of frameworks and libraries at this point.
54:52Zero is the one that I've played with most recently, and it is Alpha Software you'll
54:58run into, it holds your hands, plugging in every battery and setting up everything.
55:03But error codes could be very confusing.
55:06but luckily their Discord is very welcoming and will answer any question
55:10that you have since their only goal is for everyone to get excited about
55:13the tech and use it in production.
55:15So I think Zero is a really great starting point as just, I wanna build an app.
55:20I'm gonna reach for a library.
55:22It will give you a query builder.
55:24So instead of writing raw SQL, it'll help you write SQL queries
55:28with some JavaScript functions.
55:30And it also works you through very common problems that you do hit at some point.
55:35And the big one is data migrations and, well, not data migrations, schema
55:38migrations, because when you have a data store on the client and you have a source
55:44of truth on the server everyone has to agree on how that data is shaped if
55:49you're using a SQL model and not something that's Firebasey as you were mentioning.
55:54So in those cases, you have to know like the four or five step
55:57process of update the server schema to add the new field, then update
56:02the client to add that new field.
56:04And then if you're trying to delete an old field for some reason, you would
56:08need to execute those on client, then server in the correct order, and then
56:13manage a database version so that if a client tries to connect with really,
56:17really old application code, the server can say, sorry, I only accept people who
56:22are on version five of this SQL schema.
56:26You're on version three, so I'm just gonna hard refresh your webpage and
56:29get you up to the latest version.
56:31all of these challenges are really interesting to think about and Zero
56:35helps you think through them out of the box and presents docs on all of these
56:40problems before you run into them.
56:42but I happen to be the type that wants to run into as many brick
56:45walls as possible without someone telling me what to worry about.
56:49I just wanna worry about it.
56:51so I think the Simple Sync Engine resource is great just because it
56:57doesn't do very much and there's a lot left up to the reader to go off
57:01and try to run into those challenges.
57:03I'm sure splunking through like the LiveStore implementation, I would
57:07find 50 ways that I could improve what I'm doing to get to that next step of
57:12like resilience, schema, migrations.
57:14I literally didn't even touch schema migrations.
57:17there's so much that you need to think about that just crawling
57:20through open source libraries is really, really helpful with, so
57:23that's my preferred learning approach.
57:25I just like going that way.
57:26I completely agree.
57:27And I also like, it's, it's sort of a bit of convincing yourself,
57:31is this entire thing worth it?
57:33And what I always appreciate if someone knows a little thing about
57:38me and then tells me, you know what?
57:41I don't think this is for you.
57:43I wouldn't hold anything back for someone who wants to look into this.
57:47to say like, this might not be what you're looking for.
57:49If someone is very happy with like building web apps with Vite Astro NextJS,
57:57et cetera, and they're productive, they're building this, e-commerce
58:02platform, or they're building a more static website, I don't think there's
58:08anything really where local-first would change their work situation.
58:14But if they're frustrated with like actual apps that they use day to day, when you're
58:21frustrated like yourself, when you're frustrated with Notion being too slow,
58:25et cetera, and you're building those more productivity daily driver apps yourself.
58:30For me that was like a music app.
58:32I got frustrated with Spotify and other music apps.
58:35I think this is the, right scenario where like local-first has something to offer,
58:40but, and I think it has also the potential to become a lot simpler and easier over
58:47has already become a lot simpler and easier or the past couple of years, and
58:51it's gonna be even more so in the future.
58:53And there will be use cases where it's actually simpler.
58:57To use local-first to build something, then using Next for something.
59:02but that won't apply to all scenarios.
59:05And so it is not a silver bullet.
59:07the closest thing you'll get to a silver bullet is the right architecture for
59:12the right application scenario, but by default there is no silver bullet.
59:17Neither is local-first.
59:19And I think someone should evaluate, Hey, is this even for me?
59:23that's, I think should be the starting point.
59:25Yeah, and a meta comment just because now I'm in the agent coding space, Warp
59:34is getting more capable by the day of actually editing files and scaffolding new
59:38applications for you, from the terminal.
59:41I've found it's less valuable to know the syntax of how all of these libraries work
59:47and a lot more valuable to just know high level, what are they doing, what's the
59:51architecture and how would I debug it?
59:53because these agents are very good at spitting out the syntax,
59:56if you draw a very clear picture.
59:59So if you go off and read designing data intensive applications, and you
1:00:03start diagramming to yourself how all of these systems are distributed, you
1:00:06could bring that diagram to Warp or just the cloud website if you want, and
1:00:12say, I wanna build this kind of app.
1:00:13Here's how the architecture's gonna work.
1:00:15This is gonna talk to this, and I know about this library.
1:00:19I know LiveStore uses event sourcing, so I would like you to implement that
1:00:24and use React, but follow the handrails because I understand the architecture.
1:00:29It'll give you a way better application than if you were to just say, give
1:00:32me a local-first app with React.
1:00:35It would probably maybe not struggle in the beginning, but definitely
1:00:38struggle as you try to figure out what it is doing or debug whatever sort
1:00:43of system level issues you're having.
1:00:45I fully agree.
1:00:46And given that the both of us are not just application developers but also
1:00:50tool creators, we spend a lot of time thinking about like, how do I leverage
1:00:55the degree of freedom that I have here in the API, the way how I design the API
1:01:00that is intuitive for someone that they like, ideally that this becomes like a
1:01:05pit of success where they intuitively use it the right way, but also if they
1:01:09use it the wrong way, how do they notice?
1:01:11Do they notice like as early on as possible through type safety or only
1:01:17if they're already in production and they felt like, wait, no,
1:01:20this, like, this was a path that I've wrongly taken six months ago.
1:01:24so you want to design all of this in a way that you like learn as early as possible
1:01:30whether you're in the right track or not.
1:01:31And I think you can't get better than simplicity than going for simplicity.
1:01:36And this is why I love the path that you've taken with the Simple Sync Engine.
1:01:42Through the push pull model because that's already, that is deeply familiar for
1:01:46developers and that is how we're using Git and that has really been proven.
1:01:51And there you can't really get much simpler than that.
1:01:54And I think simple is great for everyone.
1:01:57and once we have a simple foundation, we have a reliable foundation.
1:02:00We can build fast and nice things on top of it.
1:02:03But particularly mentioning AI systems, I make a lot of design trade offs
1:02:08now differently, where I care less about how much effort it will be
1:02:12to write or to discover that thing.
1:02:15Since we have now LLMs, do TXT, et cetera, I care a lot more about like,
1:02:20how does, how will you even spot where like this doesn't seem right.
1:02:24The robot has given me something weird and just doesn't match my,
1:02:29like, primitive understanding of how this entire thing fits together.
1:02:33And that should also help the robot to like not go in the wrong
1:02:37direction in, in the first place.
1:02:39So, yeah, I love that.
1:02:41Like, and going for a simple design decision, the simple like overall
1:02:47system architecture that's gonna help you as a future programmer, observing
1:02:52little robots, building things, a lot more to know what's going on.
1:02:57So maybe we use that as a last segue to hear a little bit more
1:03:01about what you're doing now at Warp in regards to agents, et cetera.
1:03:06I've been using Warp for, a little bit.
1:03:09I still use it, for my standalone, terminal, but most of my terminal
1:03:15work is also happening within Cursor, which is integrated in like the
1:03:20agent thing and Cursor, et cetera.
1:03:22Maybe can, yeah, help me a little bit of like how I bring those two together
Warp
1:03:29Yeah, and it's an interesting world of sort of agentic coding solutions.
1:03:35It feels like there's a new approach to it every other day.
1:03:38before joining Warp, I was also using Kline a lot, which is a VS Code
1:03:42extension that's fully open source that will, from my experience, give
1:03:47you a more quality agent output.
1:03:49Since it has these two phases, you can flip on a plan switch and
1:03:54it'll use a reasoning model to take whatever you tell it and turn
1:03:59it into like a step-by-step plan.
1:04:02And you could walk through like, no, that architecture doesn't make sense,
1:04:04or Let me upload this six cal draw.
1:04:06I actually want to work like this.
1:04:07And you can go back and forth on like a design doc and then you can
1:04:11flip it to act mode and that engages Claude to go build that for you.
1:04:15And it's very hit or miss actually doing like the code edits.
1:04:19We're all struggling with that.
1:04:21but it was like this really nice mental model of, oh yeah, we're
1:04:24gonna plan it out together.
1:04:26We're gonna design jam, how this thing's gonna work and then we're
1:04:29gonna go build it and I can just let it run and see how it ends up working.
1:04:34and Warp is doing something similar but not within the confines of VS code.
1:04:39And also with the addition of a voice button that I've been using a lot more
1:04:43recently because you can talk faster than you can type is generally what I found.
1:04:48So I can just speak into my terminal, here's how I want the app to
1:04:52work, this, that, and the other.
1:04:54And then it will, depending on how complicated the question is, it
1:04:58will reach for the planning step.
1:05:00Otherwise it'll just give you an answer right away.
1:05:01So if it, see that's kind of complicated, let me plan it out.
1:05:04It'll give you that same kind of like document, here's how it's going to work.
1:05:08And then you can say, okay, do it.
1:05:10And then it will go off and tell Claude to make file edits and do other
1:05:15things on your machine, which is much further than Warp has gone in the past.
1:05:19and I've really enjoyed using this to build Swift apps recently.
1:05:23Since I was just fascinated with like, how could I build a really
1:05:27slick desktop client like ChatGPT there's this desktop shortcut to
1:05:31like pull up a little chat bar.
1:05:33Like I want something that integrated.
1:05:36I don't wanna be confined to Google Chrome anymore.
1:05:38I wanna break out of it.
1:05:40but if you open up XCode, you're just met with this like decade old auto
1:05:45complete that doesn't have anything that you want in order to get stuff done.
1:05:50but Warp is just a terminal.
1:05:51So I'm like, okay, I'll just open the Swift project in Warp
1:05:54and say implement this feature.
1:05:56And it doesn't it, it can literally just enter any directory that you
1:06:00have and just start doing things.
1:06:02I've also used it to like migrate my open source projects from a mono repo to a
1:06:07set of micro repos on my system and says, oh yeah, I'll just make a new directory.
1:06:12I'll move all those files over and I'll make the necessary file edits with Cloud.
1:06:17Very like hit or miss quality.
1:06:19We're dialing it in.
1:06:20But this idea of you're not constrained to the IDE anymore, you can kind
1:06:25of just pull up your terminal and ask it to modify anything from the
1:06:30simplest request of, help me get revert, whatever the heck I just did.
1:06:34And it'll help you get to something more complicated, like, why
1:06:38isn't my Postgres server running?
1:06:40And then it'll check your Homebrew installation.
1:06:43And then you can take it one step further to, I actually want to fix this
1:06:47error I see in my dev server right now.
1:06:50Because you're running the dev server in your terminal, it can say, all
1:06:53right, pause the server, debug, debug, debug, restart the server.
1:06:57And then if something fails again, I'll go back to debugging.
1:07:01So it's like watching your terminal session and figuring
1:07:04out how to help you do something.
1:07:06It feels like it's this natural next step of let's go from you're in an
1:07:10editor typing code quickly to like, this is a general purpose tool on
1:07:14your machine to edit all the software that you're writing in any setting.
1:07:19so I'm just very excited about that kind of future.
1:07:22And we've been moving very, very quickly towards it.
1:07:25Just in the past month, it's gone from barely usable to, I'm actually using
1:07:30this a lot for projects in a language that I don't even know how to speak.
1:07:34Swift.
1:07:35it's been kind of crazy how far you can get from zero to one
1:07:39without a lot of field knowledge.
1:07:41And intuitively this makes a lot of sense since like Eternal is kind of like the
1:07:46OG chat up in a way where like all the way back to like IRC, et cetera, but
1:07:51also now with, using ChatGPT a lot or, or other LLM chat products, like yes,
1:07:57you're, chatting with the thing, like you write a command, the command happens
1:08:01to be like plain English or another language, and you get something back.
1:08:06But the, kinda like back and forth.
1:08:08And the interplay is very similar and it makes so much more, so much sense that you
1:08:14now bring that into the terminal as well, where you get the best of both worlds.
1:08:19You can like ride out things in a fuzzy way.
1:08:22The terminal helps you to like put that into proper computer speak.
1:08:27but then you also get the efficiency and the correctness from what you can do in
1:08:32a terminal and with all of like just a top-notch craft that you get within Warp
1:08:38as a terminal with like blocks, et cetera.
1:08:41So yeah, highly recommend everyone to, give it a try.
1:08:44Yeah.
1:08:44And it's free to reach for all those things.
1:08:47And anyone who is just bothered by AI in their terminal, you can turn it
1:08:50off and just use Warp as a really good terminal, which is how I started using
1:08:55it way back, probably like, 2021, I think is when it said I created my account.
1:09:01I just used it because I wanted something that looked nice and
1:09:04now it's going a lot deeper.
1:09:06And yeah, the, chat app analogy is perfect.
1:09:09There's literally a toggle between typing out commands and asking it a question,
1:09:14and it'll even flip back and forth based on like natural language, which is fancy.
1:09:19I mean, I'll just hit the keyboard shortcut, but why
1:09:22not make it a little flashier?
Outro
1:09:24That is awesome.
1:09:25Well, I've already seen, a bunch of your recent videos,
1:09:28about content related to that.
1:09:30I'm looking forward to many more of those.
1:09:32Is there anything else that you wanna share related to your local-first,
1:09:38explorations or otherwise?
1:09:40Yeah.
1:09:41so my profile is bholmesdev everywhere.
1:09:44So any learning resources I've put out like videos on local-first and
1:09:50conference talks, bholmesdev on YouTube and Twitter and Bluesky.
1:09:55And also on GitHub, so these Simple Sync Engine project I mentioned,
1:09:59that's on my personal GitHub.
1:10:00Also under bholmesdev.
1:10:02You should see it as one of the star repos if you look up the profile.
1:10:06That's it.
1:10:06Perfect.
1:10:07I've also put it in the show notes, so everything, you'll find it there as well.
1:10:11But, I'm really, really excited you have put in the effort to create this project,
1:10:17because I think there's no better way to learn something than trying to do it.
1:10:22And you've done that and you've allowed other people to follow
1:10:25your footsteps, and I think that's a fantastic learning resource.
1:10:29So thank you so much for doing that and for, yeah, helping others learn
1:10:34and, sharing what you've learned and for coming on the show today.
1:10:37Thank you.
1:10:38Yeah, thanks so much.
1:10:39This was like a really far reaching conversation.
1:10:42I hope it turns out good.
1:10:43Thank you for listening to the localfirst.fm podcast.
1:10:46If you've enjoyed this episode and haven't done so already, please
1:10:49subscribe and leave a review.
1:10:51Please also share this episode with your friends and colleagues.
1:10:54Spreading the word about the podcast is a great way to support
1:10:57it and to help me keep it going.
1:10:59A special thanks again to Jazz for supporting this podcast.