
localfirst.fm
·E24
#24 – Ben Holmes: Astro, Simple Sync Engine & Warp
Episode Transcript
1
00:00:00,000 --> 00:00:02,460
What if I'm on my phone, I type
a thing and then I go back to
2
00:00:02,460 --> 00:00:04,170
my computer and I try to see it?
3
00:00:04,440 --> 00:00:06,300
Is it gonna merge it together?
4
00:00:06,300 --> 00:00:07,320
Is it gonna break?
5
00:00:07,350 --> 00:00:09,000
I have no idea because.
6
00:00:10,035 --> 00:00:12,225
Client side stores don't
help you with those problems.
7
00:00:12,225 --> 00:00:13,875
They're not designed for those problems.
8
00:00:14,144 --> 00:00:18,884
They're designed for like ephemeral
state that can disappear at a moment's
9
00:00:18,884 --> 00:00:21,045
notice if it needs to for some reason.
10
00:00:21,518 --> 00:00:26,198
so that led me down exploring local-first
technologies and I found pretty quickly
11
00:00:26,198 --> 00:00:31,568
how capable SQLite is for these use
cases, including in the browser.
12
00:00:32,096 --> 00:00:34,376
Welcome to the localfirst.fm podcast.
13
00:00:34,976 --> 00:00:37,946
I'm your host, Johannes Schickling,
and I'm a web developer, a
14
00:00:37,946 --> 00:00:41,036
startup founder, and I love the
craft of software engineering.
15
00:00:41,696 --> 00:00:45,416
For the past few years, I've been on a
journey to build a modern, high quality
16
00:00:45,416 --> 00:00:49,766
music app using web technologies, and
in doing so, I've been falling down the
17
00:00:49,766 --> 00:00:51,626
rabbit hole of local-first software.
18
00:00:52,226 --> 00:00:55,106
This podcast is your invitation
to join me on that journey.
19
00:00:55,643 --> 00:00:58,403
In this episode, I'm
speaking to Ben Holmes.
20
00:00:58,748 --> 00:01:02,828
A senior web developer and educator
known for his whiteboard videos
21
00:01:03,368 --> 00:01:07,148
after having spent most of his career
building server centric applications.
22
00:01:07,448 --> 00:01:11,918
Ben recently explored local-first
software by building a simple sync engine
23
00:01:11,918 --> 00:01:14,678
from scratch before getting started.
24
00:01:14,888 --> 00:01:18,278
Also, a big thank you to Jazz
for supporting this podcast,
25
00:01:18,638 --> 00:01:20,288
and now my interview with Ben.
26
00:01:20,817 --> 00:01:22,707
Hey Ben, so nice to have you on the show.
27
00:01:22,707 --> 00:01:23,367
How are you doing?
28
00:01:24,447 --> 00:01:25,977
Hey, I'm doing great.
29
00:01:26,217 --> 00:01:27,027
Yeah, how are you doing?
30
00:01:27,957 --> 00:01:29,037
I'm doing fantastic.
31
00:01:29,037 --> 00:01:31,017
Super excited to have you on the show.
32
00:01:31,159 --> 00:01:34,939
I'm very certain that most of the
audience already are familiar with who
33
00:01:34,939 --> 00:01:40,651
you are since you have quite a reach
on, Twitter, x, other platforms, et
34
00:01:40,651 --> 00:01:45,181
cetera, where I think you're doing
an excellent job of taking like novel
35
00:01:45,181 --> 00:01:49,690
concepts and breaking them down in
a very simple and approachable way.
36
00:01:49,960 --> 00:01:54,310
And I think you've done the same for
some local-first related topics recently.
37
00:01:54,310 --> 00:01:58,690
So maybe some of the audience are already
familiar with your work in that regard.
38
00:01:59,140 --> 00:02:03,013
But, for those who are new to you, would
you mind giving a background who you are?
39
00:02:04,168 --> 00:02:05,158
Yeah, totally.
40
00:02:05,388 --> 00:02:09,378
I'd be shocked if everyone knows, but
if you've seen a guy with a whiteboard
41
00:02:09,498 --> 00:02:13,188
around the internet, it might be me,
especially if it's a vertical video.
42
00:02:13,542 --> 00:02:19,932
so I've been doing a lot of work in just
the engineering space for a long time.
43
00:02:20,142 --> 00:02:26,142
So been at Astro for a few years
and we've been building the
44
00:02:26,142 --> 00:02:27,792
framework for content sites.
45
00:02:28,032 --> 00:02:31,402
And I know actually you have some
experience working with Contentlayer
46
00:02:31,422 --> 00:02:34,632
and other things, but content sites
are a nice place to get started
47
00:02:34,632 --> 00:02:39,372
because they're a nice, well-defined
use case for web technologies and
48
00:02:39,372 --> 00:02:42,762
Astro was trying to spearhead being
like the simplest way to do that.
49
00:02:43,465 --> 00:02:48,655
so was able to contribute to that for
a long time and also produce a lot of
50
00:02:48,655 --> 00:02:50,995
videos on learnings in the process.
51
00:02:51,085 --> 00:02:55,405
So it started with taking a rock band
microphone and a whiteboard outta my
52
00:02:55,405 --> 00:03:00,865
closet and just recording stuff and just
seeing where it went and been doing it for
53
00:03:00,865 --> 00:03:03,235
years now and kind of honing the craft.
54
00:03:03,625 --> 00:03:07,655
And we've covered all sorts of
topics, including, different
55
00:03:07,655 --> 00:03:12,800
web frameworks, Tailwind tips,
database providers using SQLite.
56
00:03:12,815 --> 00:03:16,295
Most recently putting SQLite in
the browser, which is gonna be
57
00:03:16,295 --> 00:03:18,095
very relevant today, I'm sure.
58
00:03:18,588 --> 00:03:23,418
and most recently I've made the
jump to Warp Terminal, so I'm gonna
59
00:03:23,418 --> 00:03:28,458
be, well, I have joined their team
as a product engineer and we're
60
00:03:28,548 --> 00:03:33,888
sort of spearheading the future of
bringing AI and agent workflows to
61
00:03:33,918 --> 00:03:35,328
everything you do in the terminal.
62
00:03:35,418 --> 00:03:38,748
So if you forget a command, need
to kill a port, need to go through
63
00:03:38,748 --> 00:03:43,188
a full get workflow or even edit
files in your projects, you can just
64
00:03:43,188 --> 00:03:46,818
ask Warp to do that for you or fall
back to all the features that make
65
00:03:46,818 --> 00:03:48,348
Warp just a really nice terminal.
66
00:03:48,528 --> 00:03:49,578
It's a really great fit.
67
00:03:49,698 --> 00:03:53,238
Been working with them for a while
and now get to do that full time.
68
00:03:53,298 --> 00:03:56,118
We'll continue to do all the videos
that you see around the internet.
69
00:03:56,515 --> 00:03:57,325
That is awesome.
70
00:03:57,325 --> 00:04:01,225
I'm certainly coming back to Warp since
I wanna learn more about that as well,
71
00:04:01,495 --> 00:04:03,925
but taking things one step at a time.
72
00:04:04,271 --> 00:04:08,411
you've been digging into local-first
related, you've been mentioning
73
00:04:08,411 --> 00:04:10,481
SQLite running SQLite in the browser.
74
00:04:10,923 --> 00:04:13,923
Looking into that over the
course of the last year or so.
75
00:04:14,286 --> 00:04:19,626
I'm very curious, like what led you
to explore that since with Astro, et
76
00:04:19,626 --> 00:04:24,833
cetera, you're probably, that's like
a different part of building websites,
77
00:04:24,833 --> 00:04:28,973
really where you maybe like Astro
is famous for introducing the island
78
00:04:28,973 --> 00:04:35,180
architecture where you go like very light
on bringing JavaScript into the, website.
79
00:04:35,630 --> 00:04:40,280
And if it's almost like the other
extreme where you want to bring a lot of
80
00:04:40,280 --> 00:04:45,716
JavaScript into the web app, deemphasize
the server part, and also more on
81
00:04:45,716 --> 00:04:48,086
the spectrum from website to web app.
82
00:04:48,086 --> 00:04:50,756
Certainly much heavier
on the web app spectrum.
83
00:04:51,056 --> 00:04:51,926
So yeah.
84
00:04:51,926 --> 00:04:54,626
Can you share more what
led you to, to this path?
85
00:04:55,053 --> 00:04:59,476
well the past year has been kind of like
an existential crisis of how are you
86
00:04:59,476 --> 00:05:03,046
supposed to build websites, and I think
we all went through that as an industry,
87
00:05:03,286 --> 00:05:09,766
as things kind of shifted back from client
side, heavy react apps towards things
88
00:05:09,766 --> 00:05:14,386
that are more server rendered and people
are slowly trying to question that idea
89
00:05:14,386 --> 00:05:19,006
again and see what we can learn from
storing more information in the client
90
00:05:19,006 --> 00:05:20,836
and sinking it back to your servers.
91
00:05:21,136 --> 00:05:26,476
So I also noticed a wave
that's completely opposite.
92
00:05:26,986 --> 00:05:32,866
Of local-first apps, which would be
something like HTMX, where everything is
93
00:05:32,866 --> 00:05:36,346
purely server driven, state stateless.
94
00:05:36,466 --> 00:05:39,706
Everything is from like
the REST API protocol.
95
00:05:40,366 --> 00:05:45,166
And there's a really nice simplicity
to it where a state lives in one
96
00:05:45,166 --> 00:05:50,656
place, the server's a source of truth
for everything, and you use different
97
00:05:50,656 --> 00:05:55,576
return values for each HTML sheet to
decide what is going to render next.
98
00:05:55,876 --> 00:06:00,346
And you accept that there's going to be
network latency for most interactions
99
00:06:00,346 --> 00:06:04,246
on the site, except for very small
dropdown toggles and the like.
100
00:06:04,306 --> 00:06:07,156
But anything that involves state
is always going to go back to
101
00:06:07,156 --> 00:06:08,746
the server and map it back.
102
00:06:09,166 --> 00:06:12,766
That obviously has trade-offs that
people have tried to get away from
103
00:06:12,766 --> 00:06:14,326
with client side architectures.
104
00:06:14,716 --> 00:06:18,556
But the reason it's so nice is you
don't have to think about, you have
105
00:06:18,556 --> 00:06:22,006
this server state, you have this client
state, and you're constantly trying
106
00:06:22,006 --> 00:06:24,646
to keep them sort of melded together.
107
00:06:24,766 --> 00:06:30,616
And I think that's something that a lot
of server side rendered applications have
108
00:06:30,616 --> 00:06:37,126
run into most recently react trying to
add on, like use optimistic hooks and
109
00:06:37,126 --> 00:06:41,566
libraries like TRPC, letting you show
data optimistically and then replace it
110
00:06:41,566 --> 00:06:43,366
with the server value when it comes in.
111
00:06:43,786 --> 00:06:49,246
And these are important principles, but
it's a lot of manual effort to first send
112
00:06:49,246 --> 00:06:53,986
a request to the server and also keep
the client in some optimistic version
113
00:06:53,986 --> 00:06:56,146
of that to cut down on network latency.
114
00:06:56,176 --> 00:06:58,786
You're having to pull levers on
both sides and you don't really
115
00:06:58,786 --> 00:07:00,466
know where the truth lives.
116
00:07:00,886 --> 00:07:06,451
So, from my experience that's led
to a lot of manual work, writing and
117
00:07:06,451 --> 00:07:11,851
rewriting local stores, maybe with
Redux back in like the mid 2010s now
118
00:07:11,851 --> 00:07:16,478
using, query hooks, even GraphQL if
you're racing for those kinds of tools.
119
00:07:16,868 --> 00:07:23,635
So it's, very messy using those things
that are isomorphic, AKA, things that run
120
00:07:23,635 --> 00:07:27,715
both on the server and on the client, and
trying to think of it in the same way.
121
00:07:28,255 --> 00:07:32,905
So one response to that is just, we
don't need client side JavaScript.
122
00:07:32,905 --> 00:07:34,585
We're gonna do everything on the server.
123
00:07:34,885 --> 00:07:38,575
And that's very easy to understand
because it goes back to like how
124
00:07:38,575 --> 00:07:40,285
rest was designed in the eighties.
125
00:07:40,705 --> 00:07:43,375
And then there's the other reaction,
which is the, to other side, the
126
00:07:43,375 --> 00:07:46,765
client is the source of truth for
pretty much everything that's going on.
127
00:07:46,915 --> 00:07:50,605
And the server's just a broker
to keep different clients in sync
128
00:07:50,635 --> 00:07:54,955
and to push changes so everyone
can stay up to date or even doing
129
00:07:54,955 --> 00:07:56,995
decentralized servers if you go further.
130
00:07:57,415 --> 00:07:59,575
But it's that other side of the coin.
131
00:08:00,160 --> 00:08:02,500
Of we want one source of truth.
132
00:08:02,500 --> 00:08:05,590
We don't want to think about, we have this
server and this client and we constantly
133
00:08:05,590 --> 00:08:07,630
have to write logic to glue them together.
134
00:08:07,780 --> 00:08:11,800
No, you either store all the state on
the server, kind of like an HTMX style,
135
00:08:12,010 --> 00:08:16,600
or you store all the state on the
client using local-first technologies.
136
00:08:16,960 --> 00:08:21,490
And having explored the first one for
a long time, since Astro is meant to be
137
00:08:21,520 --> 00:08:26,680
like a static website or server driven
website, I was excited to explore the
138
00:08:26,680 --> 00:08:30,820
other side of storing everything on the
client, keeping it up to date, and then
139
00:08:30,820 --> 00:08:33,580
figuring out how synchronization happens.
140
00:08:33,910 --> 00:08:35,650
That's also been around for a long time.
141
00:08:35,650 --> 00:08:40,000
If you look back to just like the first
calendar app on your phone or on your
142
00:08:40,000 --> 00:08:44,440
computer, this challenge has been around
since probably SQLlite was created.
143
00:08:44,723 --> 00:08:48,953
but it's only now that web devs are
starting to get a lot of footholds
144
00:08:48,953 --> 00:08:53,453
to also apply this to websites if
that's something that you need.
145
00:08:53,873 --> 00:08:55,623
I think you've summarized it really well.
146
00:08:55,863 --> 00:09:00,963
And, I would go even as far as saying
there is an elephant in the room that
147
00:09:00,963 --> 00:09:04,353
most people are aware that there is
an elephant, but they really don't
148
00:09:04,353 --> 00:09:06,273
have yet the, the right terminology.
149
00:09:06,273 --> 00:09:10,806
And I would say the term here of
the elephant is distributed systems.
150
00:09:10,926 --> 00:09:14,616
We have distributed states and
distributed systems are really, like,
151
00:09:14,766 --> 00:09:19,296
that's a core discipline of computer
science that doesn't really have
152
00:09:19,296 --> 00:09:24,609
just like the good answer, but it's a
really, really hard problem alongside
153
00:09:24,609 --> 00:09:26,469
of like naming things and so on.
154
00:09:26,829 --> 00:09:30,219
But, that part, like everyone
who's building a web app,
155
00:09:30,279 --> 00:09:32,469
anything that has state.
156
00:09:32,949 --> 00:09:37,659
On the server, like even in the server,
typically you have by definition
157
00:09:37,659 --> 00:09:41,979
also already a distributed system
where you have states in your API
158
00:09:41,979 --> 00:09:45,729
throughout the request lifecycle, but
then you also have it in the database.
159
00:09:45,939 --> 00:09:48,249
You might have like concurrent requests.
160
00:09:48,429 --> 00:09:50,739
So there you already have
a distributed system.
161
00:09:50,739 --> 00:09:55,239
Typically there it's much less bad
because they're like, you can just
162
00:09:55,269 --> 00:09:56,979
trust the server already less.
163
00:09:57,189 --> 00:09:58,879
So you just trust the database.
164
00:09:59,289 --> 00:10:02,349
So you basically push it all
down to the single choke point.
165
00:10:02,709 --> 00:10:07,599
But if you wanna trust the client
even more now all the distributed
166
00:10:07,599 --> 00:10:11,799
parts are get the distance grows
and therefore the divergence.
167
00:10:12,189 --> 00:10:15,776
And I have in university, when I
studied computer science, I don't
168
00:10:15,776 --> 00:10:20,306
think I've actually taken a class on
distributed systems and I've missed a
169
00:10:20,306 --> 00:10:24,236
memo where someone would've instilled
it in me is like, Hey, everything
170
00:10:24,236 --> 00:10:28,181
that are you gonna build will suffer
from distributed systems, make sure to
171
00:10:28,181 --> 00:10:31,961
understand this problem properly and
then design the architecture around it.
172
00:10:31,991 --> 00:10:34,841
And I think most web devs
are not aware of that.
173
00:10:35,621 --> 00:10:40,651
And what you've just laid out, I think
is exactly suffering from this problem.
174
00:10:41,131 --> 00:10:45,691
And this is what I think where
some framework creators are making
175
00:10:45,691 --> 00:10:50,011
the very smart decision to empower
the server as much as possible.
176
00:10:50,191 --> 00:10:56,722
Because if you live by that sort of
maxim, at that point, the implied
177
00:10:57,022 --> 00:11:01,762
damage that you can cause by building
it in a certain way is minimized.
178
00:11:02,302 --> 00:11:06,339
And, my journey over the last five
years or so has been almost like
179
00:11:06,369 --> 00:11:11,979
intentionally letting the pendulum swing
to the most other extreme where all the
180
00:11:11,979 --> 00:11:16,449
state is governed by the client, since
I'm pretty convinced that the middle
181
00:11:16,449 --> 00:11:18,639
ground is just pain and suffering.
182
00:11:19,222 --> 00:11:24,112
So I'm pretty convinced that you should
really analyze the use case that you have.
183
00:11:24,532 --> 00:11:29,062
And if it's a daily driver productivity
app, you probably wanna move as much
184
00:11:29,062 --> 00:11:30,862
state to the client as possible.
185
00:11:31,012 --> 00:11:35,992
Where it happens, like you produce most
of your state in your calendar app or in
186
00:11:35,992 --> 00:11:41,092
your Notion, or in your other notes app
or whatever you want to have it declined.
187
00:11:41,572 --> 00:11:45,429
And, if it's something like
New York Times, then you don't
188
00:11:45,429 --> 00:11:46,599
produce any of that date.
189
00:11:46,599 --> 00:11:47,799
So it should be on the server.
190
00:11:48,249 --> 00:11:52,862
And, I, feel like more people should,
start with that assumption, then
191
00:11:52,862 --> 00:11:54,422
build an architecture around it.
192
00:11:54,962 --> 00:11:57,806
But, yeah, I think you
summarized it super well.
193
00:11:58,105 --> 00:11:58,555
Yeah.
194
00:11:58,855 --> 00:12:00,175
And it is tough.
195
00:12:00,864 --> 00:12:06,534
To prescribe either side because there
are neat buckets like content sites
196
00:12:06,564 --> 00:12:09,414
and client side apps like Notion.
197
00:12:09,924 --> 00:12:15,054
But the classic example is, well what
about e-commerce where things are
198
00:12:15,054 --> 00:12:18,594
server driven until you're in the add
to cart flow and now it's client driven.
199
00:12:18,594 --> 00:12:19,764
So what do you do then?
200
00:12:19,794 --> 00:12:21,024
How do you architect it?
201
00:12:21,444 --> 00:12:26,454
We actually met this challenge building
the Astro storefront front template,
202
00:12:26,964 --> 00:12:31,554
which was mostly server driven with some
client side components sprinkled in.
203
00:12:32,004 --> 00:12:36,924
I think the answer there was, it's
still fine to leave things server
204
00:12:36,924 --> 00:12:42,819
side and not rely on optimistic
updates too much, except for like
205
00:12:42,819 --> 00:12:47,109
small examples like increase and
decrease quantity in your cart.
206
00:12:47,109 --> 00:12:51,459
Do you want that to feel instant and
happen on a delay or a debounce but
207
00:12:51,459 --> 00:12:52,989
everything else is server driven.
208
00:12:53,289 --> 00:12:56,019
There is no perfect architecture,
I guess is what I'm saying.
209
00:12:56,229 --> 00:13:00,849
It's more painful when you really try to
blend it 50 50 and nail every use case.
210
00:13:01,179 --> 00:13:07,899
But if you can keep it 80 20, where 80% is
in one realm and 20% is business logic in
211
00:13:07,899 --> 00:13:13,599
the other realm, like 80% server driven,
20% client side complexity or less, then
212
00:13:13,749 --> 00:13:18,369
you're kind of minimizing the footprint
of pain that you could run into since I've
213
00:13:18,369 --> 00:13:22,419
certainly noticed that with some of the
newer patterns and older patterns with.
214
00:13:23,064 --> 00:13:24,174
Client side apps.
215
00:13:24,627 --> 00:13:26,577
but there was a second thing
you mentioned about distributed
216
00:13:26,577 --> 00:13:28,917
systems that I, totally agree with.
217
00:13:29,157 --> 00:13:35,487
I was reading the book Designing
Data Intensive Applications, the big
218
00:13:35,487 --> 00:13:39,987
O'Reilly book, probably the first one
you find looking up like CS principles.
219
00:13:40,467 --> 00:13:44,757
And I think there was one section about
distributed systems where it walked you
220
00:13:44,757 --> 00:13:47,607
from like single leader replication.
221
00:13:47,667 --> 00:13:52,347
You got one database and if you want
to have some caches to make reads
222
00:13:52,347 --> 00:13:56,757
faster, you just put all your rights to
one database and then it'll replicate
223
00:13:56,757 --> 00:13:58,257
the reads out to everyone else.
224
00:13:58,287 --> 00:14:00,837
So you can't write to a
bunch of different regions.
225
00:14:00,837 --> 00:14:03,867
You can write data to one
region and then it'll sort of.
226
00:14:04,437 --> 00:14:08,907
Push out all of the clones and
allow eventual consistency to work.
227
00:14:09,374 --> 00:14:12,014
but there are cases where
that doesn't work anymore.
228
00:14:12,224 --> 00:14:16,184
Like in Notion, I cannot wait for
the server to update the document.
229
00:14:16,364 --> 00:14:19,934
It's just gonna update when I'm typing
and when I add blocks and when people
230
00:14:19,934 --> 00:14:21,824
are invited to join the document.
231
00:14:21,824 --> 00:14:22,889
All that stuff matters.
232
00:14:22,889 --> 00:14:28,214
So you need some way to be able to
write to maybe the nearest node.
233
00:14:28,244 --> 00:14:31,814
Like if you go fancy with CloudFlare,
you could have really localized,
234
00:14:31,814 --> 00:14:35,864
durable objects that are two feet away
from your computer and that reduces
235
00:14:35,864 --> 00:14:38,114
latency in some sort of magical way.
236
00:14:38,601 --> 00:14:43,991
but then it kind of explains well if
you just put a data replica just on the
237
00:14:43,991 --> 00:14:49,901
client device, that's another version
of multi leader replication, where
238
00:14:49,901 --> 00:14:53,711
instead of replicating in some really
edge node, you're just replicating
239
00:14:53,711 --> 00:14:57,941
on the person's computer and everyone
is like a mini server unto itself,
240
00:14:57,941 --> 00:15:01,541
where you just read and write data
and they'll all report back to some
241
00:15:01,541 --> 00:15:04,001
centralized source of truth later on.
242
00:15:04,611 --> 00:15:07,641
So when you're working with,
like, is it a distributed system?
243
00:15:07,701 --> 00:15:08,241
It is.
244
00:15:08,241 --> 00:15:12,531
Even when it's running on your device,
if there's some sort of synchronization
245
00:15:12,531 --> 00:15:15,951
layer, you're just moving it closer
and closer and closer to the computer
246
00:15:15,951 --> 00:15:18,171
until it iSQLiterally in the computer.
247
00:15:18,494 --> 00:15:19,844
It's inside the house.
248
00:15:20,294 --> 00:15:24,234
I've, heard a, very interesting
framing of sort of like that
249
00:15:24,234 --> 00:15:27,504
elephant in the room and like that
problem that we just talked about.
250
00:15:27,874 --> 00:15:32,957
I think it's by Carl who worked on
SQLSync and recently, released Graft,
251
00:15:32,987 --> 00:15:34,847
which is another fascinating project.
252
00:15:35,297 --> 00:15:39,664
And he has written a blog post about,
don't recall the exact name we're gonna
253
00:15:39,664 --> 00:15:43,640
put it in the, show notes, but it was
basically along the lines of like, your
254
00:15:43,790 --> 00:15:46,803
application is a database and that's bad.
255
00:15:47,117 --> 00:15:47,977
I gotta look it up.
256
00:15:48,051 --> 00:15:49,520
what the, exactly the title was.
257
00:15:49,820 --> 00:15:56,030
But it was basically, he's making the
point that every app that is sufficiently
258
00:15:56,030 --> 00:16:03,355
getting more complicated will basically
build its own version of a ad hoc database
259
00:16:03,652 --> 00:16:08,272
and if you're just using React useState
or something else enough, and then
260
00:16:08,272 --> 00:16:10,552
you try to add persistence, et cetera.
261
00:16:10,792 --> 00:16:15,502
What your app is basically becoming
is a poor implementation of a database
262
00:16:15,892 --> 00:16:21,542
where like all the things that a
database does a lot of like R&D great
263
00:16:21,542 --> 00:16:26,918
work to make it fast, to make it
correct, to make it like nicely, like
264
00:16:27,158 --> 00:16:29,198
transactionally correct, et cetera.
265
00:16:29,648 --> 00:16:35,975
All of those things you're now trying to
handle through, useState and useEffect
266
00:16:35,995 --> 00:16:39,775
and like when this thing changes,
also change that thing, et cetera.
267
00:16:40,195 --> 00:16:45,935
And, I just thought that framing was
so elegant and, his conclusion, which
268
00:16:45,935 --> 00:16:50,855
I agree with is like, hey, if let's try
to make the app about what the app tries
269
00:16:50,855 --> 00:16:55,835
to do and let's leverage a database
so we can focus on the actual like
270
00:16:55,835 --> 00:16:57,905
features, et cetera we want to implement.
271
00:16:58,445 --> 00:17:03,335
And I think that's another kinda
articulation of the elephant in the room
272
00:17:03,752 --> 00:17:09,275
that we're accidentally and without being
aware of it Building databases as that
273
00:17:09,275 --> 00:17:14,498
are sort of camouflaged as apps and, we
should embrace it more if we're starting
274
00:17:14,498 --> 00:17:16,628
to see those signs of those problems.
275
00:17:17,018 --> 00:17:22,438
So you've mentioned that you've gone down
this path, a lot out of curiosity and
276
00:17:22,438 --> 00:17:27,175
just because you've, pretty exhaustively
explored the more server centric
277
00:17:27,175 --> 00:17:31,745
paths, can you share more about like
what were your frustrations and pain
278
00:17:31,745 --> 00:17:35,825
points that you felt where you wanted
to go more, like embrace the client
279
00:17:35,825 --> 00:17:41,515
more, but still trying to do that with
the more server centric architecture.
280
00:17:41,725 --> 00:17:45,295
So you've mentioned optimistic
state, et cetera, maybe can motivate
281
00:17:45,295 --> 00:17:49,255
this through a concrete app you
wanted to build where you just felt
282
00:17:49,255 --> 00:17:51,265
that that pain and that impedance.
283
00:17:51,738 --> 00:17:56,688
Yeah, I mean I've played with all
sorts of side projects as we all have.
284
00:17:56,883 --> 00:17:59,853
and I was working on a
few different things.
285
00:18:00,363 --> 00:18:06,723
One was like a localized note taking
app, and I was also playing with local
286
00:18:06,753 --> 00:18:12,663
LLMs and other pieces to add vector
search into a local environment because
287
00:18:12,693 --> 00:18:17,553
I was running up on the limits of using
Notion and being very frustrated with
288
00:18:17,733 --> 00:18:21,633
loading spinners and offline warnings
when I was trying to use the app.
289
00:18:21,948 --> 00:18:26,238
I hear that's being changed and
rectified as they build out their
290
00:18:26,238 --> 00:18:28,818
own, like SQLite replication.
291
00:18:29,058 --> 00:18:29,958
I know that's in there.
292
00:18:29,958 --> 00:18:32,628
There's some fascinating videos
about that on the internet.
293
00:18:33,175 --> 00:18:39,115
but I still do see the value of just open
up Apple Notes, you type things and it's
294
00:18:39,115 --> 00:18:40,975
just kind of there in a SQLlite store.
295
00:18:40,975 --> 00:18:42,385
You can find it on your file system.
296
00:18:42,385 --> 00:18:45,265
All your Apple Notes are just in
a SQLlite store and it's great.
297
00:18:45,828 --> 00:18:48,888
so I thought, well, it seems
like that's the answer.
298
00:18:48,888 --> 00:18:51,528
If I were to go the traditional
server route, I would sit here
299
00:18:51,528 --> 00:18:54,858
waiting for all these updates to
persist, which just wouldn't work.
300
00:18:55,188 --> 00:18:59,118
Or I'd be sort of gluing together a
bunch of useState calls and figuring
301
00:18:59,118 --> 00:19:02,328
out how do I update this server and
what if someone else updates it?
302
00:19:02,328 --> 00:19:04,788
What if I'm on my phone, I type
a thing and then I go back to
303
00:19:04,788 --> 00:19:06,498
my computer and I try to see it?
304
00:19:06,768 --> 00:19:08,628
Is it gonna merge it together?
305
00:19:08,628 --> 00:19:09,648
Is it gonna break?
306
00:19:09,678 --> 00:19:11,328
I have no idea because.
307
00:19:12,363 --> 00:19:14,553
Client side stores don't
help you with those problems.
308
00:19:14,553 --> 00:19:16,203
They're not designed for those problems.
309
00:19:16,473 --> 00:19:21,213
They're designed for like ephemeral
state that can disappear at a moment's
310
00:19:21,213 --> 00:19:23,373
notice if it needs to for some reason.
311
00:19:23,847 --> 00:19:28,527
so that led me down exploring local-first
technologies and I found pretty quickly
312
00:19:28,527 --> 00:19:33,897
how capable SQLite is for these use
cases, including in the browser.
313
00:19:34,210 --> 00:19:38,050
you can load up SQLite with
like a wasm build as you're
314
00:19:38,050 --> 00:19:41,410
very aware and use it even with.
315
00:19:41,815 --> 00:19:45,595
Like very nice SQLite libraries.
316
00:19:45,655 --> 00:19:50,318
If you wanted to use Drizzle for example,
which is a common SQL querying library
317
00:19:50,318 --> 00:19:55,838
and JavaScript, it matches onto the
browser version of SQLite perfectly.
318
00:19:55,928 --> 00:19:59,978
So you can actually make
declarative, find many notes and
319
00:19:59,978 --> 00:20:01,808
it'll just do the little SQL query.
320
00:20:01,808 --> 00:20:04,178
It'll join it up with all the
authors of the post and it'll
321
00:20:04,178 --> 00:20:05,198
just give it back to you.
322
00:20:05,318 --> 00:20:07,958
Kind of like you're on the
server, but you're on the client.
323
00:20:08,108 --> 00:20:12,188
And you can use client side
ORMs or query builders, whatever
324
00:20:12,188 --> 00:20:14,048
your flavor of preference.
325
00:20:14,378 --> 00:20:16,448
So that was very empowering to see.
326
00:20:16,448 --> 00:20:20,348
Yeah, you can bring all of these
niceties you get from server side data
327
00:20:20,348 --> 00:20:22,748
querying and bring it into the client.
328
00:20:23,378 --> 00:20:26,258
And I tried to stretch it a
little bit further by asking,
329
00:20:26,618 --> 00:20:28,538
what about SQL extensions?
330
00:20:28,808 --> 00:20:31,658
Could I add a vector
search plugin, for example?
331
00:20:32,288 --> 00:20:33,158
The answer is yes.
332
00:20:33,158 --> 00:20:36,398
There actually is a vector search
plugin that I think is developed
333
00:20:36,398 --> 00:20:38,078
by someone on the Mozilla team.
334
00:20:38,168 --> 00:20:40,658
So it is pretty battle tested
in different languages.
335
00:20:41,258 --> 00:20:47,992
I think they're sponsored, yeah I've
had the chance to meet them in October
336
00:20:47,992 --> 00:20:50,498
when I was in LA super lovely person.
337
00:20:50,528 --> 00:20:54,285
And, they have some sponsorship
from the Mozilla team currently.
338
00:20:55,635 --> 00:20:56,295
Nice.
339
00:20:56,625 --> 00:21:01,355
Yeah, and I was playing with the,
rust flavor of that since, well now
340
00:21:01,355 --> 00:21:05,465
I'm working at Warp, and Warp is a
rust powered terminal, so naturally I
341
00:21:05,465 --> 00:21:08,315
need to get up on the Rust knowledge.
342
00:21:08,375 --> 00:21:13,085
And also if you build apps with Tori,
which is a native desktop application
343
00:21:13,085 --> 00:21:15,425
tool that uses Rust as well, so.
344
00:21:16,430 --> 00:21:19,850
Side tangent, but it is nice to use tools
that could work in JavaScript as well
345
00:21:19,850 --> 00:21:21,770
as rust in very efficient languages.
346
00:21:22,219 --> 00:21:23,107
so I reached for that.
347
00:21:23,317 --> 00:21:24,607
I put vector search in.
348
00:21:24,967 --> 00:21:29,467
I was also able to run an entire
LLM across the data in the
349
00:21:29,467 --> 00:21:34,657
browser and just load up like the
entire vector search setup thing.
350
00:21:35,233 --> 00:21:38,173
and I just said, all right,
I'm gonna backport all of my
351
00:21:38,203 --> 00:21:40,363
markdown files into this thing.
352
00:21:40,543 --> 00:21:42,553
I'm gonna vectorize all
of them in the browser.
353
00:21:42,793 --> 00:21:44,203
I'm gonna search them in the browser.
354
00:21:44,203 --> 00:21:47,983
And I was able to get it working
with like next key search.
355
00:21:48,073 --> 00:21:49,783
And it was absolutely mind blowing.
356
00:21:49,843 --> 00:21:53,683
Like this is like an actual
AI powered search tool.
357
00:21:53,953 --> 00:21:57,073
And you can get like with
every keystroke new results
358
00:21:57,198 --> 00:22:01,938
And is isn't that wild like that this
machine that we have like sitting
359
00:22:01,938 --> 00:22:07,218
on our laps or like this machine
here in my hands that is capable
360
00:22:07,218 --> 00:22:11,995
of all of those things and just the
way how we kind of build web apps.
361
00:22:11,995 --> 00:22:16,045
Over the last decade or so, we've
kind of forgotten or denied the
362
00:22:16,045 --> 00:22:18,355
capabilities of our client devices.
363
00:22:18,685 --> 00:22:22,975
And we only just like trust the server
and we've almost like, why did no
364
00:22:22,975 --> 00:22:24,835
one tell us that this is possible?
365
00:22:25,105 --> 00:22:28,682
And that's so magical when you see
that this is working and just the
366
00:22:28,682 --> 00:22:32,958
stuff that's currently, in the works
with like web, LLM, et cetera, where
367
00:22:32,958 --> 00:22:37,522
it can run like a full blown like
Llama model locally in your browser.
368
00:22:37,815 --> 00:22:43,125
running on web GPU is absolutely wild
to the capabilities that we have.
369
00:22:43,455 --> 00:22:47,395
But I think what's holding us
back is where does our data
370
00:22:47,395 --> 00:22:48,745
live and everything else.
371
00:22:48,745 --> 00:22:52,495
Kinda like, it's almost like a second
order effect from where the data lives.
372
00:22:52,945 --> 00:22:57,482
And this is what, your anecdote really
nicely highlights of like, you go with
373
00:22:57,482 --> 00:23:02,802
SQLite with your data and then you like
bring in another superpower of like SQLite
374
00:23:02,802 --> 00:23:05,582
Vec with the vector embedding, et cetera.
375
00:23:05,972 --> 00:23:08,822
And I think it all starts with
where the data is, how your
376
00:23:08,882 --> 00:23:10,382
application is being shaped.
377
00:23:10,905 --> 00:23:11,625
Yeah.
378
00:23:11,685 --> 00:23:13,515
And it is good to find that.
379
00:23:13,830 --> 00:23:15,690
Just common data layer.
380
00:23:15,740 --> 00:23:17,730
SQLite is the easy answer.
381
00:23:17,760 --> 00:23:22,380
PG light is a more robust exploration
of bringing Postgres to local devices.
382
00:23:22,380 --> 00:23:23,610
That's a bit earlier on.
383
00:23:24,110 --> 00:23:28,334
but I think we're entering a world
where software is just so easy to spin
384
00:23:28,334 --> 00:23:33,794
up that you will very quickly have
a web client, a mobile app client,
385
00:23:33,914 --> 00:23:37,994
a desktop client, and they're all
talking to the same sync server.
386
00:23:38,324 --> 00:23:40,894
And when you're in that world,
it's nice to just reach for SQLite.
387
00:23:40,964 --> 00:23:42,724
'cause I can run SQLlite on my iPhone.
388
00:23:42,914 --> 00:23:46,694
I can run SQLlite on my Android device,
I can run it in the browser and I
389
00:23:46,694 --> 00:23:48,674
could run it in a desktop application.
390
00:23:48,974 --> 00:23:54,164
So as long as you just have this concept
of clients write to SQL servers and
391
00:23:54,164 --> 00:23:58,334
those SQL servers have some way to
talk to each other, then you can build
392
00:23:58,334 --> 00:24:04,124
these multi-platform applications very
quickly, even across different languages.
393
00:24:04,214 --> 00:24:07,574
And just figure out what that
sync layer looks like, which
394
00:24:07,634 --> 00:24:09,374
we can probably talk about.
395
00:24:09,472 --> 00:24:12,839
that would've been my next question
since, I think what you started with
396
00:24:12,899 --> 00:24:17,319
was probably without the sync part
yet, where you can just locally in
397
00:24:17,319 --> 00:24:23,019
your browser web app, you successfully
ran SQLite using the wasm build.
398
00:24:23,109 --> 00:24:27,435
Then you brought in SQLite Vec, you
could, get the AI magic to work.
399
00:24:27,675 --> 00:24:30,795
You saw like how insanely
fast everything feels.
400
00:24:30,795 --> 00:24:33,435
You write something,
you reload the browser.
401
00:24:33,615 --> 00:24:35,775
Even if you're offline,
it's all still there.
402
00:24:35,965 --> 00:24:36,475
Great.
403
00:24:36,805 --> 00:24:41,005
But now you're thinking, okay,
it works on like local host 3000.
404
00:24:41,552 --> 00:24:47,965
how do I get it deployed and how do
I get it so that, if I accidentally
405
00:24:47,965 --> 00:24:52,525
open this in a cognitive tab and I
close it, poof, everything is gone.
406
00:24:52,734 --> 00:24:56,395
and how do I get it show up on
my, phone so this is kind of
407
00:24:56,395 --> 00:25:00,325
collaborating with yourself, but on
also collaborating with others, which
408
00:25:00,325 --> 00:25:02,695
we maybe punt on that for a moment.
409
00:25:03,055 --> 00:25:03,595
But yeah.
410
00:25:03,595 --> 00:25:09,565
How did you go from, it works locally
almost like if you use like just
411
00:25:09,595 --> 00:25:14,755
local storage to trying to share
the goodness across your devices.
412
00:25:15,224 --> 00:25:19,717
Well, I think we both have the privilege
of just ask Andrew on the Rocicorp team
413
00:25:19,717 --> 00:25:21,307
and he'll point you to some resources.
414
00:25:21,710 --> 00:25:25,144
if you don't have that,
I do recommend that.
415
00:25:25,174 --> 00:25:31,204
Well, the one approach that I used was
just reading through the Replicache docs.
416
00:25:31,469 --> 00:25:34,889
That explain how their sync
engine works on a very high level.
417
00:25:35,279 --> 00:25:41,969
And Replicache is a batteries included
library, well some batteries included
418
00:25:42,119 --> 00:25:48,509
library that sets up a simple key value
store for you where you can write and read
419
00:25:48,509 --> 00:25:54,599
keys on the client and you can author code
that will post those changes to a server
420
00:25:54,599 --> 00:25:58,949
that you own and pull changes back from
the server whenever changes are detected.
421
00:25:59,549 --> 00:26:04,619
And you can implement that on a SQLite
or anything else if you just have
422
00:26:04,969 --> 00:26:09,779
their simplified mental model, which
is very inspired by just how Git works.
423
00:26:09,839 --> 00:26:14,189
So in Git, if you had to change locally
and you wanted to push it up to the
424
00:26:14,189 --> 00:26:16,859
main line, you would run Git push.
425
00:26:17,309 --> 00:26:21,719
And the same kind of thing happens
with their sync engine service, where
426
00:26:21,719 --> 00:26:25,889
anytime you make a change locally
and you can decide what that means,
427
00:26:25,889 --> 00:26:30,059
maybe it's a debounce as you're
editing a document, maybe it's when
428
00:26:30,059 --> 00:26:33,479
you click on a status toggle in Linear
and you wanna change that status.
429
00:26:34,189 --> 00:26:40,219
Whenever the trigger is, you can use that
to call push, which would probably call
430
00:26:40,219 --> 00:26:43,219
an endpoint written on your own server.
431
00:26:43,459 --> 00:26:49,069
That's simply a push endpoint and that
can receive whatever event you ran
432
00:26:49,069 --> 00:26:53,719
on the client so that the server can
replay it, back it up into its own
433
00:26:53,719 --> 00:26:58,669
logs and tell other clients about it
whenever they try to pull those changes
434
00:26:58,669 --> 00:27:03,439
back down so you can run your own
little push whenever you make a change.
435
00:27:03,559 --> 00:27:07,399
And then other clients on an
interval or a web socket or some
436
00:27:07,399 --> 00:27:11,449
other connection can pull for data
whenever they want to get the most
437
00:27:11,449 --> 00:27:13,879
recent changes made by other people.
438
00:27:14,479 --> 00:27:18,649
Now the question would be, what am
I pushing and what am I pulling?
439
00:27:18,859 --> 00:27:20,059
Like what data?
440
00:27:20,674 --> 00:27:22,414
Needs to be sent across.
441
00:27:22,684 --> 00:27:25,264
And there are a couple different methods.
442
00:27:25,384 --> 00:27:30,244
I know Replicache uses a
data snapshot mechanism.
443
00:27:30,364 --> 00:27:35,464
I don't fully know the intricacies of
it, but I know that because they use
444
00:27:35,464 --> 00:27:39,874
like a key value storage, which is much
simpler than like a full-blown database.
445
00:27:39,949 --> 00:27:44,104
They can take a snapshot of what the
server looks like right now and then the
446
00:27:44,104 --> 00:27:47,974
client has its own copy of that server
snapshot and it can just run a little
447
00:27:47,974 --> 00:27:51,694
diff the same way you'd run a get diff
to see what code changes you have made.
448
00:27:52,024 --> 00:27:55,204
It can run that diff and you can
see, all right, I added this key.
449
00:27:55,204 --> 00:27:58,684
All right, updated this status
flag, and then tell the server,
450
00:27:58,684 --> 00:28:01,684
this is the diff, this is the
change that was made to the data.
451
00:28:02,044 --> 00:28:06,574
And the server can receive that request
and say, okay, this is the change
452
00:28:06,574 --> 00:28:08,584
that I need to make against my copy.
453
00:28:08,974 --> 00:28:13,024
And then I will tell other clients to
also apply that diff whenever they pull.
454
00:28:13,320 --> 00:28:18,990
it's a very Git inspired model, and it
works if you're able to diff data easily.
455
00:28:19,410 --> 00:28:23,670
If you are working with like a full
blown SQLlite table, you run table
456
00:28:23,670 --> 00:28:27,600
migrations, tables, change shapes, that's
a very hard thing to keep track of.
457
00:28:27,930 --> 00:28:33,484
So another option that I implemented
for the, learning resource I
458
00:28:33,484 --> 00:28:35,464
created called Simple Sync Engine.
459
00:28:35,554 --> 00:28:39,514
If you find that on my GitHub, probably
in the show notes, you can see that.
460
00:28:40,024 --> 00:28:43,744
But it was meant to be a very basic
implementation of this pattern
461
00:28:43,984 --> 00:28:46,474
that uses event sourcing instead.
462
00:28:46,594 --> 00:28:52,714
So rather than sending a diff of how
the data should change, instead sends
463
00:28:52,774 --> 00:28:57,034
an event that describes the change
that is made like a function call or
464
00:28:57,034 --> 00:29:01,114
an RPC, however you wanna think about
it, where you would tell the server
465
00:29:01,324 --> 00:29:04,414
I updated the status to this value.
466
00:29:05,179 --> 00:29:09,559
And in our implementation we create
these little like function helpers that
467
00:29:09,559 --> 00:29:11,449
can describe what those events are.
468
00:29:11,509 --> 00:29:13,999
So you might have like
an add status event.
469
00:29:13,999 --> 00:29:19,069
So like the type of the event is add
status and that accepts a few arguments.
470
00:29:19,129 --> 00:29:22,789
It accepts the status you wanna
change it to, and the ID of the
471
00:29:22,849 --> 00:29:25,189
record that has that status currently.
472
00:29:25,369 --> 00:29:30,259
So the server receives that event, it
sees, okay, the type was set status.
473
00:29:30,499 --> 00:29:31,669
I see two arguments here.
474
00:29:31,669 --> 00:29:32,869
The ID of the record.
475
00:29:33,149 --> 00:29:35,189
And the new status that should be applied.
476
00:29:35,489 --> 00:29:38,969
I'm gonna go ahead and run that
event as a database update.
477
00:29:39,239 --> 00:29:43,679
So it has that mapping understanding
of, I received this event, I know that
478
00:29:43,679 --> 00:29:48,299
maps to this SQL query, so I'm gonna go
ahead and make that change on my copy.
479
00:29:48,569 --> 00:29:52,949
And then whenever people poll, you
can send that event log out, or you
480
00:29:52,949 --> 00:29:57,029
can send whatever events the client
hasn't received up until that point.
481
00:29:57,419 --> 00:30:00,179
You can kind of think of those like
Git commits where you're pulling
482
00:30:00,179 --> 00:30:04,259
the latest commits on the branch and
the server's able to tell you, here
483
00:30:04,259 --> 00:30:05,909
are the latest events that were run.
484
00:30:06,149 --> 00:30:10,109
Go ahead and run those on your
replicas so both the server
485
00:30:10,109 --> 00:30:11,819
and client know what events.
486
00:30:12,189 --> 00:30:16,119
Actually mean, like this event
means I need to make this SQL query.
487
00:30:16,119 --> 00:30:17,439
And it's able to do that mapping.
488
00:30:17,939 --> 00:30:19,979
and you can have a bit of freedom there.
489
00:30:19,979 --> 00:30:24,539
If you had like a very specific kind
of data store on the server, like
490
00:30:24,539 --> 00:30:28,499
MongoDB, you could customize it to
say, whenever I receive this event, it
491
00:30:28,499 --> 00:30:33,089
means this MongoDB query and this call
to the Century error logging system,
492
00:30:33,089 --> 00:30:35,189
or whatever middleware you wanna do.
493
00:30:35,609 --> 00:30:40,469
As long as server and client agree on
what events exist and what changes they
494
00:30:40,469 --> 00:30:44,489
make in the data stores, then everyone
can be on the same page whenever
495
00:30:44,489 --> 00:30:46,229
they're syncing things up and down.
496
00:30:46,714 --> 00:30:49,264
you're very familiar with
event sourcing as well.
497
00:30:49,414 --> 00:30:53,404
I'm curious if there's things that
I've missed or important edge cases
498
00:30:53,404 --> 00:30:54,754
that we should probably talk about.
499
00:30:55,146 --> 00:31:00,684
I think you very elegantly, described how
simple the foundation of this can be and
500
00:31:00,684 --> 00:31:02,994
hence the name of like Simple Sync Engine.
501
00:31:03,350 --> 00:31:06,814
I think this has served as a great
learning resource for you and
502
00:31:06,814 --> 00:31:08,404
I'm sure for many, many others.
503
00:31:08,974 --> 00:31:13,634
And once you like, start pulling more on
that thread, you realize, oh shit like,
504
00:31:13,664 --> 00:31:15,404
okay, that thing I didn't think about.
505
00:31:15,644 --> 00:31:16,634
Oh, what about this?
506
00:31:16,844 --> 00:31:22,194
So you've mentioned already the reason
why you preferred, event sourcing over
507
00:31:22,194 --> 00:31:26,898
the snapshot approach because like with
SQLlite, What would you actually compare?
508
00:31:27,408 --> 00:31:31,914
This is where I would give a shout
out again, to Carl's work with Graft.
509
00:31:32,331 --> 00:31:33,801
this is what he's been working on.
510
00:31:33,801 --> 00:31:36,321
We should have him on the show
highlighting this as well.
511
00:31:36,591 --> 00:31:41,881
But, where he's built a new kinda
sync engine that, is all about that
512
00:31:41,881 --> 00:31:44,551
diffing of like blocks of storage.
513
00:31:45,164 --> 00:31:50,654
I think all focused around SQLite and
that gives, that does the heavy work
514
00:31:50,684 --> 00:31:54,944
of like, diffing and then sending
out the minimal amount of changes
515
00:31:55,154 --> 00:31:56,564
to make all of that efficient.
516
00:31:56,564 --> 00:32:02,566
Since there's this, famous saying
of like make it work, make it right
517
00:32:02,566 --> 00:32:04,486
or correct and then make it fast.
518
00:32:04,966 --> 00:32:09,976
And working on all of this has really
given me a deep appreciation for all
519
00:32:09,976 --> 00:32:14,806
of this since like, and I'm sure you
probably also went through those stages
520
00:32:15,046 --> 00:32:19,516
with the Simple Sync Engine, like with
making something work in the first place
521
00:32:19,546 --> 00:32:21,346
was already quite the accomplishment.
522
00:32:21,616 --> 00:32:25,576
But then you also realize, ah, okay, in
those cases this is not quite yet correct.
523
00:32:25,756 --> 00:32:30,583
And then you could go back and like try
to iterate and then you also realize,
524
00:32:30,613 --> 00:32:36,763
okay, so now it has worked for my little
to-do app, but if I, now, depending
525
00:32:36,763 --> 00:32:40,663
on the architecture, if I roll this
out and put in hack news and suddenly
526
00:32:40,663 --> 00:32:45,073
have like 5,000 people there on the
same time, this thing will break apart.
527
00:32:45,073 --> 00:32:48,973
Will A not be correct in some
ways you didn't anticipate and
528
00:32:48,973 --> 00:32:50,503
will also not be fast enough.
529
00:32:50,773 --> 00:32:52,513
So now making this fast.
530
00:32:52,768 --> 00:32:55,828
It is at the end of the day
is like really reinventing.
531
00:32:55,828 --> 00:33:00,478
It's like your app is becoming
a little database and you want
532
00:33:00,478 --> 00:33:04,828
to like move as much of that
database burden to the sync engine.
533
00:33:05,038 --> 00:33:09,358
This is why folks like Rocicorp,
ElectricSQL, et cetera, they're
534
00:33:09,358 --> 00:33:13,048
doing a fantastic job trying to
absorb as much of that complexity.
535
00:33:13,468 --> 00:33:18,238
But building something like this by
yourself really gives you an understanding
536
00:33:18,238 --> 00:33:21,508
and an appreciation for what is going on.
537
00:33:21,568 --> 00:33:25,431
I love the Git analogy that you've
used, but just a, a couple of
538
00:33:25,431 --> 00:33:30,714
points just similarly to how, your
sync engine works is actually very
539
00:33:30,714 --> 00:33:35,634
analogous to how the Livestore
architecture on a high level works.
540
00:33:36,054 --> 00:33:42,024
But I've had to, before I arrived at that,
I really wanted to think through a lot
541
00:33:42,024 --> 00:33:45,029
of like the more further down the road.
542
00:33:45,029 --> 00:33:50,094
Like, what if situations since,
one that I'm curious whether you've
543
00:33:50,094 --> 00:33:53,814
already run into, whether you
resolved in some way or left for the
544
00:33:53,814 --> 00:33:59,304
future, is how would you impose a
total order of your change events?
545
00:33:59,664 --> 00:34:04,239
So this is where When you have,
like, let's say this to-do app
546
00:34:04,239 --> 00:34:06,505
or like a mini Linear, app.
547
00:34:06,685 --> 00:34:12,062
Let's say you create an issue and then
you say you, complete the issue or
548
00:34:12,062 --> 00:34:14,152
you toggle the issue state, et cetera.
549
00:34:14,532 --> 00:34:19,745
It can mean something very different,
if one happens first and then the
550
00:34:19,745 --> 00:34:21,275
other, or the other way around.
551
00:34:21,905 --> 00:34:26,135
And for that, where you have your events
of like, hey, the issue was created,
552
00:34:26,435 --> 00:34:30,425
the issue status was changed, this,
the issue status was changed, that,
553
00:34:30,979 --> 00:34:35,584
the order really matters in which way
in which order everything happened.
554
00:34:36,004 --> 00:34:40,910
And, this might be not so bad if you're
the absolutely only person using your
555
00:34:40,910 --> 00:34:42,800
app and typically only on one device.
556
00:34:43,070 --> 00:34:46,580
But when you then do stuff between
multiple devices or multiple
557
00:34:46,580 --> 00:34:50,900
users, then it's no longer in
your single user's control.
558
00:34:50,960 --> 00:34:52,670
That stuff happens concurrently.
559
00:34:53,120 --> 00:34:57,994
And then it really matters that everyone
has the same order of the events.
560
00:34:58,024 --> 00:35:01,384
And this is where you need to
impose what's called a total order.
561
00:35:02,044 --> 00:35:06,794
And I'm very curious whether you've
already, hit that point, where you thought
562
00:35:06,794 --> 00:35:10,844
that through and whether you found a
mechanism to impose it since there's many,
563
00:35:10,844 --> 00:35:12,584
many downstream consequences of that.
564
00:35:13,040 --> 00:35:13,670
Right.
565
00:35:13,670 --> 00:35:16,190
And I definitely did hit it and.
566
00:35:16,520 --> 00:35:20,840
You will find in the resource, it's
not addressing that issue right now.
567
00:35:20,900 --> 00:35:25,730
It's in a very naive state of when change
is made, send, fetch, call to server.
568
00:35:26,240 --> 00:35:29,870
And there are a few problems with
that, even if you're the only client.
569
00:35:30,020 --> 00:35:35,150
Because first off, if you send one event
per request, it's very possible that you
570
00:35:35,150 --> 00:35:41,480
send a lot of very quick secession events
in like an order, and then they reach the
571
00:35:41,480 --> 00:35:46,940
server in a different order because maybe
you pushed the first change on low network
572
00:35:47,000 --> 00:35:50,870
latency, the next one on high latency,
or actually the reverse of that where
573
00:35:50,870 --> 00:35:54,890
the first change hits after the second
change because that's just the speed of
574
00:35:54,890 --> 00:35:56,390
the network and that's what happened.
575
00:35:56,851 --> 00:35:59,960
And also you need to think
about offline capabilities.
576
00:36:00,080 --> 00:36:04,730
If you push changes when they
happen, how do you queue them
577
00:36:04,730 --> 00:36:07,760
when you're not connected to the
internet and then run through that
578
00:36:07,760 --> 00:36:09,170
queue once you're back online?
579
00:36:09,170 --> 00:36:11,750
That's another consideration
you kind of have to think about.
580
00:36:12,080 --> 00:36:15,050
Could be solved with just like
an in-memory event log and
581
00:36:15,050 --> 00:36:16,310
just kind of work with that.
582
00:36:16,520 --> 00:36:19,130
But you still have the order issue.
583
00:36:19,520 --> 00:36:23,660
I'm familiar with atomic
clocks as a method to do this.
584
00:36:23,660 --> 00:36:28,270
There are even SQLite extensions
that'll sort of enforce that, having
585
00:36:28,270 --> 00:36:30,100
not implemented atomic clocks.
586
00:36:30,130 --> 00:36:34,630
Is it kind of this silver bullet
to that problem or are there more
587
00:36:34,630 --> 00:36:37,930
considerations to think about than
just reaching for something like that?
588
00:36:38,695 --> 00:36:39,115
Right.
589
00:36:39,115 --> 00:36:42,055
I suppose you're referring
to vector clocks or logical
590
00:36:42,055 --> 00:36:43,855
clocks on a more higher level?
591
00:36:43,855 --> 00:36:43,915
Yeah.
592
00:36:44,265 --> 00:36:47,685
since the atomic clocks, at least
my understanding is like that's
593
00:36:47,685 --> 00:36:51,679
actually what's, at least in some
super high-end hardware is like
594
00:36:51,679 --> 00:36:56,035
an atomic clock that is, like that
actually gives us like the wall clock.
595
00:36:56,065 --> 00:36:58,135
So Right, right now is like.
596
00:36:58,545 --> 00:37:03,585
Uh, 6:30 PM on my time, but
this clock might drift, and this
597
00:37:03,585 --> 00:37:04,995
is what makes it so difficult.
598
00:37:04,995 --> 00:37:09,645
So what you were referring to with logical
clocks, this is where it basically,
599
00:37:09,645 --> 00:37:14,115
instead of saying like, Hey, it's
6:30 with this time zone, which makes
600
00:37:14,115 --> 00:37:20,445
everything even more complicated, I'm
keeping track of my time is like 1, 2, 3.
601
00:37:20,685 --> 00:37:24,525
It like might just be a logical
counter, like much simpler
602
00:37:24,525 --> 00:37:26,085
actually than wall clock time.
603
00:37:26,423 --> 00:37:31,572
but this is easier to reason about
and there might be no weird issues of
604
00:37:31,572 --> 00:37:36,079
like, Daylight saving where certainly
like the, the clock is going backwards
605
00:37:36,349 --> 00:37:40,159
or someone tinkers with the time,
this is why you need logical clocks.
606
00:37:40,519 --> 00:37:44,829
And, there, at least the mechanism
that I've also landed on to
607
00:37:44,859 --> 00:37:46,959
implement, to impose a total order.
608
00:37:47,319 --> 00:37:50,379
But then it's also tricky,
how do you exchange that?
609
00:37:50,503 --> 00:37:54,883
how does your client know what like
three means in my client, et cetera?
610
00:37:54,883 --> 00:37:59,915
And the answer that I found to
this is to like that we all trust.
611
00:38:00,328 --> 00:38:02,674
A single, authority in the system.
612
00:38:02,914 --> 00:38:07,421
So this is where, and I think this is also
what you're going for, and with the Git
613
00:38:07,421 --> 00:38:12,971
analogy, what we are trusting as authority
in that system is GitHub or GitLab.
614
00:38:13,421 --> 00:38:17,048
And this is where we are basically,
we could theoretically, you could
615
00:38:17,048 --> 00:38:20,588
send me your IP address and I could
try to like pull directly from you.
616
00:38:20,818 --> 00:38:24,598
It would work, and that would also
work with the system that you've built.
617
00:38:25,018 --> 00:38:29,108
However, there might still be,
they're called network petitions,
618
00:38:29,408 --> 00:38:33,428
where like the two of us have like,
synced up, but some others haven't.
619
00:38:33,728 --> 00:38:39,745
So as long as we're all connected to
the same, like main upstream node, that
620
00:38:39,745 --> 00:38:41,935
is the easiest way to, to model this.
621
00:38:41,995 --> 00:38:46,525
An alternative would be to go full on
peer to peer, which makes everything
622
00:38:46,585 --> 00:38:48,685
a lot, lot, lot more complicated.
623
00:38:49,225 --> 00:38:53,011
And this is where like something, like
an extension of logical clocks called
624
00:38:53,011 --> 00:38:55,461
vector clocks, can come in handy.
625
00:38:55,685 --> 00:39:00,628
you've mentioned the, the book, designing
dataset intensive application by Martin
626
00:39:00,628 --> 00:39:02,458
Kleppman had him on the show before.
627
00:39:02,688 --> 00:39:06,968
he's actually working on the version two
of that book right now, but he's also done
628
00:39:06,968 --> 00:39:12,671
a fantastic free course about distributed
systems where he is walking through all of
629
00:39:12,671 --> 00:39:18,571
that, with a whiteboard, I actually think
so, I think does what, what the two of
630
00:39:18,571 --> 00:39:24,555
you have very much like you've both nailed
the, craft of like showing with simple
631
00:39:24,555 --> 00:39:27,231
strokes, some very complicated matters.
632
00:39:27,725 --> 00:39:30,935
so highly recommend to anyone
who wants to learn more there.
633
00:39:31,815 --> 00:39:33,705
Like, learn it from, from Martin.
634
00:39:33,735 --> 00:39:37,575
He's, like an absolute master
of explaining those difficult
635
00:39:37,575 --> 00:39:39,385
concepts in a simple way.
636
00:39:40,125 --> 00:39:45,521
But, yeah, a lot of things go kind
of downstream from that total order.
637
00:39:45,551 --> 00:39:51,898
So just to, go together on like one little
journey to understand like a downstream
638
00:39:51,898 --> 00:39:56,698
problem of this, let's say we have
implemented the queuing of those events.
639
00:39:56,908 --> 00:40:00,751
So let's say you're currently on
a plane ride and, you're like.
640
00:40:00,981 --> 00:40:03,601
Writing your blog post,
you're very happy with it.
641
00:40:03,841 --> 00:40:07,231
You have now like a thousand
of events of like change
642
00:40:07,231 --> 00:40:09,571
events that captures your work.
643
00:40:09,661 --> 00:40:11,641
Your SQLite database is up to date.
644
00:40:12,175 --> 00:40:16,525
but you didn't just create this new blog
post, but you maybe while you're still at
645
00:40:16,525 --> 00:40:21,181
the airport, like you created the initial
version with it with like TBD in the body.
646
00:40:21,721 --> 00:40:26,185
And your coworker thought like, oh,
actually I have a lot of thoughts on this.
647
00:40:26,485 --> 00:40:29,365
And they also started writing
down some notes in there.
648
00:40:29,935 --> 00:40:33,488
And now, the worlds have
like, kind of drifted apart.
649
00:40:33,848 --> 00:40:35,108
Your coworker.
650
00:40:35,288 --> 00:40:38,618
Has written down some important
things they don't want to lose,
651
00:40:38,768 --> 00:40:42,358
and you've written down some things
you are not aware of the other ones
652
00:40:42,408 --> 00:40:48,698
neither are they, and at some point
the semantic merge needs to happen.
653
00:40:48,938 --> 00:40:52,538
But how do you even make that happen
in this sync engine thing here?
654
00:40:52,838 --> 00:40:57,458
And this is where you need the total
order, where you basically, in the worst
655
00:40:57,458 --> 00:41:04,329
case, this is what decides, like who, gets
a say in this, who gets the last say, in
656
00:41:04,629 --> 00:41:07,029
which order those events have happened.
657
00:41:07,539 --> 00:41:12,159
The model that I've landed on, and
I think that's similar to what Git
658
00:41:12,159 --> 00:41:17,619
does with rebasing, is basically that
before you get to push your own stuff,
659
00:41:18,099 --> 00:41:22,899
you need to pull down the events
first, and then you need to reconcile
660
00:41:22,929 --> 00:41:25,779
your kind of stash local changes.
661
00:41:26,394 --> 00:41:32,374
On top of the work that whoever has
gotten the, who got lucky enough to push
662
00:41:32,374 --> 00:41:35,584
first without being told to pull first.
663
00:41:35,974 --> 00:41:39,124
So in that case, it might have
been your coworker because they've
664
00:41:39,274 --> 00:41:41,284
stayed online and kept pushing.
665
00:41:41,884 --> 00:41:45,917
And now it sort of like falls
on you to reconcile that.
666
00:41:46,067 --> 00:41:50,807
And I've implemented a, like an
actual rebase mechanism for this,
667
00:41:51,107 --> 00:41:56,057
where you now have this set of
new events that your coworker has
668
00:41:56,057 --> 00:42:01,401
produced and you still have your set
of events that, reflect your changes.
669
00:42:01,491 --> 00:42:03,441
And now you need to reconcile this.
670
00:42:03,501 --> 00:42:05,181
So that is purely on the.
671
00:42:05,471 --> 00:42:12,284
Event log level, but given that we
both, want to use SQLite now, we don't
672
00:42:12,284 --> 00:42:17,654
need to just think about going forward
with SQLite, but we also now need to
673
00:42:17,654 --> 00:42:19,664
think about like, Hey, how do we go?
674
00:42:19,784 --> 00:42:23,774
Like in Git you have like, you
have this stack of events, right?
675
00:42:24,014 --> 00:42:27,494
So you have like a commit, which has
a parent of another commit, which
676
00:42:27,494 --> 00:42:28,934
has a parent of another commit.
677
00:42:29,264 --> 00:42:36,219
It's very similar to how your events and
this event log look like, except it's now
678
00:42:36,249 --> 00:42:41,379
no longer just one event log, but you also
get this little branch from your coworker.
679
00:42:41,379 --> 00:42:44,239
So now you need to go to
the last common ancestor.
680
00:42:44,569 --> 00:42:46,586
And from there you need
to figure out like.
681
00:42:46,891 --> 00:42:48,691
How do I linearize this?
682
00:42:49,304 --> 00:42:53,294
I've opted for a model where everything
that was pushed once cannot be
683
00:42:53,294 --> 00:42:55,214
overwritten, so there's no force push.
684
00:42:55,484 --> 00:42:58,904
So you basically just get
to append stuff at the end.
685
00:42:59,444 --> 00:43:04,887
But, in order to get there, you need
to first roll back your own stuff, then
686
00:43:05,277 --> 00:43:07,497
play forward what you've gotten first.
687
00:43:08,021 --> 00:43:10,151
and then on top add those.
688
00:43:10,481 --> 00:43:15,301
And the rolling back with SQLite is
a, thing that I've like put a lot of
689
00:43:15,301 --> 00:43:21,314
time into where I've been using another
SQLite extension, called the SQLite
690
00:43:21,334 --> 00:43:27,577
Sessions extension, which allows you,
per SQLite write, to basically, record
691
00:43:27,877 --> 00:43:29,677
what has the thing actually done.
692
00:43:30,037 --> 00:43:32,677
So instead of storing, insert.
693
00:43:33,144 --> 00:43:34,764
Into issues, blah, blah, blah.
694
00:43:35,214 --> 00:43:40,614
when running that, you get a blob
of let's say 30 bytes, and that has
695
00:43:40,614 --> 00:43:45,891
recorded on SQLite level, what has
happened to the SQLite database.
696
00:43:46,311 --> 00:43:52,511
And I store that alongside of each
change event, that sits in the event log.
697
00:43:53,081 --> 00:43:57,481
And the very cool thing about this
is, I can use that to replay it
698
00:43:57,481 --> 00:44:01,201
on top of another database, but to
kind of catch it up more quickly.
699
00:44:01,501 --> 00:44:03,001
But I can also invert it.
700
00:44:03,421 --> 00:44:07,381
So now I have basically this
like, let's say 20 events.
701
00:44:07,681 --> 00:44:10,981
And for each, I've recorded what
has happened on SQLite level,
702
00:44:11,341 --> 00:44:12,811
and now I can basically say.
703
00:44:13,101 --> 00:44:17,901
When I need to roll back, I can revisit
each of those, invert each of those
704
00:44:17,901 --> 00:44:22,614
change sets, apply them again on the
SQLite database, and then I'll end up
705
00:44:22,944 --> 00:44:27,114
where I was before and that's how I've
implemented rollback on top of SQL Lite.
706
00:44:27,504 --> 00:44:32,331
So this is as mentioned when
you're going, down the, rabbit hole
707
00:44:32,331 --> 00:44:34,551
of like imposing a total order.
708
00:44:34,821 --> 00:44:37,881
There's a lot of downstream
things you need to do that makes
709
00:44:37,881 --> 00:44:39,501
this even more complicated.
710
00:44:39,891 --> 00:44:43,029
But, from what I can see,
you're, on the right track if
711
00:44:43,029 --> 00:44:44,289
you wanna pursue this further.
712
00:44:45,373 --> 00:44:45,583
Yeah.
713
00:44:45,703 --> 00:44:52,333
And I do have a rebasing mechanism
in place in mind that's more,
714
00:44:52,569 --> 00:44:53,739
just kind of a sledgehammer.
715
00:44:53,739 --> 00:44:56,259
I got two SQLite databases in mind.
716
00:44:56,566 --> 00:45:00,796
in the same way that on Git you have like
your local copy of the main line and your
717
00:45:00,796 --> 00:45:05,536
local copy of your work, there's always
this local copy of Main, that's just
718
00:45:05,596 --> 00:45:07,726
whatever events have come from the server.
719
00:45:07,846 --> 00:45:12,286
So this is the source of truth that the
server has told me about and that was
720
00:45:12,286 --> 00:45:13,696
something I forgot to mention earlier.
721
00:45:13,696 --> 00:45:16,906
Explaining all of this is the
server is the source of truth.
722
00:45:16,906 --> 00:45:21,166
It has that main line of the order
of all of the events, and that is
723
00:45:21,166 --> 00:45:22,966
what all the clients use to trust.
724
00:45:23,308 --> 00:45:27,058
But yeah, it has like that local
copy, and then when it pulls from
725
00:45:27,058 --> 00:45:29,248
the server, it'll update that copy.
726
00:45:29,638 --> 00:45:33,688
It'll look at all the events that
are kind of ahead in the client,
727
00:45:33,988 --> 00:45:39,028
and then it'll say, okay, I'm gonna
roll back my client copy of my
728
00:45:39,028 --> 00:45:41,518
branch to whatever the server is.
729
00:45:41,578 --> 00:45:43,768
And it's literally just a file right call.
730
00:45:43,768 --> 00:45:45,898
So it just overwrites.
731
00:45:46,218 --> 00:45:50,448
Your like client SQLlite file
with a copy of the server one.
732
00:45:50,898 --> 00:45:53,928
And then we look at the events that
the server didn't acknowledge yet
733
00:45:53,928 --> 00:45:58,338
and then we replay those on top as
a very basic way to pull and make
734
00:45:58,338 --> 00:46:02,238
sure, because it's very possible that
you made some changes locally that
735
00:46:02,238 --> 00:46:04,188
the server hasn't acknowledged yet.
736
00:46:04,218 --> 00:46:08,298
Like you've pushed them up still
in process and you pull down the
737
00:46:08,298 --> 00:46:11,748
latest changes and you don't see
all of that stuff that you pushed
738
00:46:11,748 --> 00:46:13,938
up yet because of network latency.
739
00:46:14,328 --> 00:46:18,618
So this sort of avoids that problem
where you pull down from the server
740
00:46:18,768 --> 00:46:21,948
and now you need to replay whatever
you did on the client that the
741
00:46:21,948 --> 00:46:23,538
server hasn't acknowledged yet.
742
00:46:23,538 --> 00:46:25,548
It hasn't received that network request.
743
00:46:25,908 --> 00:46:30,198
So that was a very basic need to
have some rebasing, but it does
744
00:46:30,198 --> 00:46:34,518
get a lot more complicated when you
have collaborators on a document.
745
00:46:34,938 --> 00:46:37,698
I've seen a few different
versions of this.
746
00:46:37,772 --> 00:46:40,784
CRDTs is the fun, like magic wand.
747
00:46:40,784 --> 00:46:41,834
It does everything.
748
00:46:42,258 --> 00:46:47,063
but there are also solutions from
Figma, for example, where they
749
00:46:47,063 --> 00:46:50,693
say everything in Figma is kind
of its own little data structure.
750
00:46:50,693 --> 00:46:53,813
Like you can put some text and
that's its own little data field.
751
00:46:54,053 --> 00:46:54,983
You have rectangles.
752
00:46:54,983 --> 00:46:56,243
Those are a data field.
753
00:46:56,603 --> 00:47:01,253
And whenever you update a rectangle,
like you update the pixel width of
754
00:47:01,253 --> 00:47:05,543
a rectangle, that's like an update
event on some SQL table that stores
755
00:47:05,543 --> 00:47:07,433
all the rectangles for this document.
756
00:47:07,823 --> 00:47:12,233
So whenever you make that update, it'll
update the pixel value of whatever
757
00:47:12,233 --> 00:47:17,183
that row entry is, and then it'll push
it up for other people to receive.
758
00:47:17,543 --> 00:47:20,453
And when you pull it down,
it's last right wins.
759
00:47:20,843 --> 00:47:24,623
In other words, whoever the last
person is in that order that the
760
00:47:24,623 --> 00:47:26,513
server decided on that total order.
761
00:47:26,693 --> 00:47:28,373
That's a new word I know about now.
762
00:47:28,433 --> 00:47:31,799
Didn't know it was called total order,
but yeah, that, once you pull it down,
763
00:47:31,799 --> 00:47:35,159
whatever the server said was the order
of events, that's gonna be the final
764
00:47:35,159 --> 00:47:37,169
state of that rectangle on your device.
765
00:47:38,059 --> 00:47:41,539
The only time it becomes a problem, and
you may have experienced this, if you're
766
00:47:41,539 --> 00:47:45,139
ever working on like a fig jam together
with a bunch of people, if you're all
767
00:47:45,139 --> 00:47:48,709
typing in the same text box, everyone's
just like overriding each other and a
768
00:47:48,709 --> 00:47:52,099
text box glitches out and changes to
whatever's on the other person's screen.
769
00:47:52,309 --> 00:47:55,309
You can't see people's cursors
because you're fighting to update
770
00:47:55,309 --> 00:47:59,779
the exact same entry in the database
and it can't reconcile those changes.
771
00:48:00,236 --> 00:48:04,946
so it only works up to, like
you're editing different things
772
00:48:04,946 --> 00:48:08,486
in the file and you're not really
stepping on each other too much.
773
00:48:08,816 --> 00:48:12,146
As soon as you're stepping on each other
trying to edit like the same text field,
774
00:48:12,596 --> 00:48:17,326
then you wanna reach for something
that's very, very fancy, like CRDTs.
775
00:48:17,531 --> 00:48:20,861
Which will try to merge elegantly
all of the changes that you're
776
00:48:20,861 --> 00:48:23,081
typing into the same database field.
777
00:48:23,531 --> 00:48:28,301
It's maybe over-prescribed because of how
powerful it is, but for those specific
778
00:48:28,301 --> 00:48:32,531
scenarios, it's really nice to reach for,
and we can talk about them if you want.
779
00:48:32,561 --> 00:48:36,551
I only have a high level understanding
of what CRDTs do, but it would be
780
00:48:36,551 --> 00:48:38,681
something to apply that kind of problem.
781
00:48:39,198 --> 00:48:45,444
my takeaway from where to apply, CRDTs
versus where I would apply event sourcing
782
00:48:45,774 --> 00:48:50,778
is, CR DTs great for in two scenarios.
783
00:48:51,108 --> 00:48:54,151
One, if you don't quite know
yet where you want to go.
784
00:48:54,706 --> 00:48:59,026
And where in the past you might've
reached for, let's say, Firebase to
785
00:48:59,026 --> 00:49:00,736
just like have a backend of service.
786
00:49:00,916 --> 00:49:04,156
You know, you might want to change
it later, but you just, for now,
787
00:49:04,156 --> 00:49:08,466
you just want to get going and,
you can, particularly if you
788
00:49:08,466 --> 00:49:12,156
don't have like a strict schema
across your entire application.
789
00:49:12,156 --> 00:49:17,196
So you just try to like, not go off
the rails too much, but at least the
790
00:49:17,196 --> 00:49:21,846
data is like, mostly, like across
the applications in a good spot.
791
00:49:22,326 --> 00:49:26,389
But as you roll this out in
production, and, we are shipping
792
00:49:26,449 --> 00:49:31,096
an iOS app as well, that someone
is, running an old version on.
793
00:49:31,786 --> 00:49:35,836
Now you don't quite know, oh, this
document, this data document that has
794
00:49:35,836 --> 00:49:39,826
been synced around here, this might
not yet have this field that the
795
00:49:39,826 --> 00:49:41,806
newer application version depends on.
796
00:49:42,196 --> 00:49:47,416
So now you have, like, this is where
time drifts in a more significant
797
00:49:47,416 --> 00:49:52,066
way and in the more traditional
application architecture approach
798
00:49:52,066 --> 00:49:54,976
you would, this way you don't trust
the client in the first place.
799
00:49:54,976 --> 00:49:58,876
Then you have like your API endpoint
and the APIs, versioned, et cetera, and
800
00:49:58,876 --> 00:50:00,876
everything is governed through the, API.
801
00:50:01,056 --> 00:50:03,906
But now you also need to
tame that problem somehow.
802
00:50:03,906 --> 00:50:07,736
So at this point you're already,
going a little bit beyond where I
803
00:50:07,736 --> 00:50:12,626
think CRDTs shine right now, which
brings me to my next kind of more
804
00:50:12,656 --> 00:50:18,953
evergreen scenario for CRDTs, which
are like very specific, tasks.
805
00:50:19,253 --> 00:50:22,403
And so text editing,
particularly rich text editing.
806
00:50:22,648 --> 00:50:28,391
Is such a scenario where I think CRDTs
are just like a very, very good, approach.
807
00:50:28,391 --> 00:50:32,691
There's also like, you can also use
ot, like operational transform, which
808
00:50:32,691 --> 00:50:37,808
is, somewhat related under the covers,
works a bit differently, but the way how
809
00:50:37,808 --> 00:50:39,688
you would use it is pretty similarly.
810
00:50:40,328 --> 00:50:45,624
And, related to rich text editing
is also when you have like complex
811
00:50:45,624 --> 00:50:49,434
list structures where you wanna
move things within the list.
812
00:50:49,434 --> 00:50:55,031
So if you want to go for the, Figma
scenario, let's say you change the
813
00:50:55,031 --> 00:51:01,261
order of like multiple rectangles, like
where do they sit in that layer order?
814
00:51:01,681 --> 00:51:04,111
how do you convey how
you wanna change that?
815
00:51:04,111 --> 00:51:08,734
You could always, have like maybe
an array of all the IDs that give
816
00:51:08,734 --> 00:51:13,064
you this perfect order, but if
this kind of happens concurrently,
817
00:51:13,064 --> 00:51:14,504
then you need to reconcile that.
818
00:51:14,774 --> 00:51:15,914
So that's not great.
819
00:51:16,184 --> 00:51:20,271
And this is where CRDTs are also
like a very, special purpose
820
00:51:20,301 --> 00:51:22,371
tool, which works super well.
821
00:51:23,076 --> 00:51:28,236
And so what I've landed on is use
event sourcing for everything except
822
00:51:28,236 --> 00:51:33,306
where I need those special purpose
tools, and this is where them reach
823
00:51:33,306 --> 00:51:35,676
for CRDTs or for something else.
824
00:51:35,856 --> 00:51:40,863
That's kind of the conclusion I, took away
if you like the event sourcing approach.
825
00:51:41,403 --> 00:51:46,576
But, I think ultimately it really
comes down to what is the application
826
00:51:46,576 --> 00:51:51,136
that you're building and what are,
like, what is the domain of what
827
00:51:51,136 --> 00:51:54,376
you're building and which sort
of trade-offs does this require?
828
00:51:54,646 --> 00:51:56,056
So I think in Figma.
829
00:51:56,116 --> 00:52:02,056
The real timeness is really important
and it is recognized that those different
830
00:52:02,056 --> 00:52:06,969
pieces that are floating around, they're
like pretty, independent from each other.
831
00:52:07,239 --> 00:52:10,389
So, and if they're independent,
then you don't need that total order
832
00:52:10,389 --> 00:52:14,993
between that, which makes everything
a lot easier in terms of scalability,
833
00:52:14,993 --> 00:52:18,143
in terms of correctness, and then
you don't need to rebase as much.
834
00:52:18,743 --> 00:52:21,713
distributed systems is the
ultimate case of it depends.
835
00:52:22,336 --> 00:52:27,823
and I think trying to build one like
you did, I think is a very good way
836
00:52:28,113 --> 00:52:30,093
to like build a better understanding.
837
00:52:30,153 --> 00:52:35,493
And also I think that opens your eyes
of like, ah, now I understand why Figma
838
00:52:35,493 --> 00:52:40,563
has this shortcoming or Notion if we are
trying to change the same line, change the
839
00:52:40,563 --> 00:52:43,583
same block as where last writers, applies.
840
00:52:43,973 --> 00:52:48,776
Whereas in Google Docs, for example, we
could easily change the, same word even.
841
00:52:49,076 --> 00:52:51,786
And it would reconcile
that in a, in a better way.
842
00:52:52,656 --> 00:52:57,636
But, maybe you have some advice for
people like yourself when you're
843
00:52:57,636 --> 00:52:59,616
just getting started on that journey.
844
00:53:00,006 --> 00:53:05,226
What would you tell people what they
should do maybe shouldn't yet do?
845
00:53:05,609 --> 00:53:07,139
today 2025?
846
00:53:07,139 --> 00:53:08,909
There's more technologies out there now.
847
00:53:09,149 --> 00:53:11,459
What would you recommend
to someone who's curious?
848
00:53:12,113 --> 00:53:13,883
Depends on the type of learner you are.
849
00:53:13,883 --> 00:53:16,013
Sometimes some are very.
850
00:53:16,774 --> 00:53:21,634
outcome driven, like I need to see an
app running in production for me to
851
00:53:21,754 --> 00:53:24,064
really get excited about this Tech.
852
00:53:24,484 --> 00:53:26,464
Other people are very
first principles driven.
853
00:53:26,464 --> 00:53:30,034
Like I want to like screw in
every nut and bolt myself to
854
00:53:30,034 --> 00:53:31,564
get excited about this thing.
855
00:53:32,068 --> 00:53:35,698
I tend to fall into the first camp
where I think it is very useful to just
856
00:53:35,758 --> 00:53:39,928
look at the docs for something like
Replicache and see how would you implement
857
00:53:39,928 --> 00:53:42,478
this kind of protocol step by step.
858
00:53:42,838 --> 00:53:44,788
Like how would you set
up the event sourcing?
859
00:53:45,358 --> 00:53:48,838
How would you put the SQLlite store
in the browser in the first place?
860
00:53:48,838 --> 00:53:50,608
Like what capabilities are there?
861
00:53:50,998 --> 00:53:53,218
And then try to think
through those edge cases.
862
00:53:53,578 --> 00:53:58,168
As you run into them trying to build
something, I use Linear as my sort
863
00:53:58,168 --> 00:54:01,468
of learning example, but you could
use pretty much anything you want.
864
00:54:02,098 --> 00:54:03,688
so that's definitely one approach.
865
00:54:03,688 --> 00:54:07,258
Now there's just so many resources
for how these things work under
866
00:54:07,258 --> 00:54:11,308
the hood that you can easily learn
about the intricacies yourself.
867
00:54:11,748 --> 00:54:17,601
Another, resource is the talks given
by who's the engineer at Linear.
868
00:54:18,621 --> 00:54:19,611
I think you've had him on the show.
869
00:54:19,611 --> 00:54:20,021
Tuomas?
870
00:54:20,696 --> 00:54:21,326
Yes.
871
00:54:21,386 --> 00:54:21,686
Yeah.
872
00:54:21,686 --> 00:54:27,896
He gave a few really helpful talks about
how the Linear sync engine works on a high
873
00:54:28,076 --> 00:54:30,836
level, and that one's more opinionated.
874
00:54:30,896 --> 00:54:36,416
It reaches for technologies like
MobX, which is a react specific state
875
00:54:36,446 --> 00:54:39,596
store, and also MongoDB for documents.
876
00:54:39,713 --> 00:54:41,713
but you still get a high level
of how they think about the
877
00:54:41,713 --> 00:54:43,183
problem, which is really nice.
878
00:54:43,826 --> 00:54:46,076
the other option, if you're
really results driven, you wanna
879
00:54:46,076 --> 00:54:47,726
see a local-first step running.
880
00:54:48,086 --> 00:54:52,346
You can reach for all sorts of
frameworks and libraries at this point.
881
00:54:52,573 --> 00:54:58,333
Zero is the one that I've played with most
recently, and it is Alpha Software you'll
882
00:54:58,333 --> 00:55:03,620
run into, it holds your hands, plugging in
every battery and setting up everything.
883
00:55:03,710 --> 00:55:06,530
But error codes could be very confusing.
884
00:55:06,936 --> 00:55:10,536
but luckily their Discord is very
welcoming and will answer any question
885
00:55:10,536 --> 00:55:13,836
that you have since their only goal
is for everyone to get excited about
886
00:55:13,836 --> 00:55:15,216
the tech and use it in production.
887
00:55:15,726 --> 00:55:20,496
So I think Zero is a really great starting
point as just, I wanna build an app.
888
00:55:20,586 --> 00:55:22,026
I'm gonna reach for a library.
889
00:55:22,416 --> 00:55:24,726
It will give you a query builder.
890
00:55:24,786 --> 00:55:28,326
So instead of writing raw SQL,
it'll help you write SQL queries
891
00:55:28,326 --> 00:55:29,856
with some JavaScript functions.
892
00:55:30,276 --> 00:55:34,536
And it also works you through very common
problems that you do hit at some point.
893
00:55:35,076 --> 00:55:38,826
And the big one is data migrations
and, well, not data migrations, schema
894
00:55:38,826 --> 00:55:44,706
migrations, because when you have a data
store on the client and you have a source
895
00:55:44,706 --> 00:55:49,266
of truth on the server everyone has
to agree on how that data is shaped if
896
00:55:49,266 --> 00:55:54,096
you're using a SQL model and not something
that's Firebasey as you were mentioning.
897
00:55:54,636 --> 00:55:57,876
So in those cases, you have to
know like the four or five step
898
00:55:57,876 --> 00:56:02,916
process of update the server schema
to add the new field, then update
899
00:56:02,916 --> 00:56:04,566
the client to add that new field.
900
00:56:04,836 --> 00:56:08,706
And then if you're trying to delete an
old field for some reason, you would
901
00:56:08,706 --> 00:56:13,896
need to execute those on client, then
server in the correct order, and then
902
00:56:13,896 --> 00:56:17,856
manage a database version so that if
a client tries to connect with really,
903
00:56:17,856 --> 00:56:22,986
really old application code, the server
can say, sorry, I only accept people who
904
00:56:22,986 --> 00:56:26,076
are on version five of this SQL schema.
905
00:56:26,256 --> 00:56:29,826
You're on version three, so I'm just
gonna hard refresh your webpage and
906
00:56:29,826 --> 00:56:31,356
get you up to the latest version.
907
00:56:31,993 --> 00:56:35,443
all of these challenges are really
interesting to think about and Zero
908
00:56:35,743 --> 00:56:40,123
helps you think through them out of the
box and presents docs on all of these
909
00:56:40,123 --> 00:56:41,833
problems before you run into them.
910
00:56:42,440 --> 00:56:45,740
but I happen to be the type that
wants to run into as many brick
911
00:56:45,740 --> 00:56:49,280
walls as possible without someone
telling me what to worry about.
912
00:56:49,430 --> 00:56:50,690
I just wanna worry about it.
913
00:56:51,050 --> 00:56:57,020
so I think the Simple Sync Engine
resource is great just because it
914
00:56:57,020 --> 00:57:01,250
doesn't do very much and there's a
lot left up to the reader to go off
915
00:57:01,250 --> 00:57:03,140
and try to run into those challenges.
916
00:57:03,440 --> 00:57:07,460
I'm sure splunking through like the
LiveStore implementation, I would
917
00:57:07,460 --> 00:57:12,620
find 50 ways that I could improve what
I'm doing to get to that next step of
918
00:57:12,650 --> 00:57:14,690
like resilience, schema, migrations.
919
00:57:14,690 --> 00:57:16,910
I literally didn't even
touch schema migrations.
920
00:57:17,312 --> 00:57:20,130
there's so much that you need to
think about that just crawling
921
00:57:20,130 --> 00:57:23,423
through open source libraries is
really, really helpful with, so
922
00:57:23,423 --> 00:57:25,223
that's my preferred learning approach.
923
00:57:25,223 --> 00:57:26,303
I just like going that way.
924
00:57:26,814 --> 00:57:27,894
I completely agree.
925
00:57:27,894 --> 00:57:31,794
And I also like, it's, it's sort
of a bit of convincing yourself,
926
00:57:31,824 --> 00:57:33,504
is this entire thing worth it?
927
00:57:33,954 --> 00:57:38,754
And what I always appreciate if
someone knows a little thing about
928
00:57:38,964 --> 00:57:41,064
me and then tells me, you know what?
929
00:57:41,304 --> 00:57:42,804
I don't think this is for you.
930
00:57:43,075 --> 00:57:46,555
I wouldn't hold anything back for
someone who wants to look into this.
931
00:57:47,260 --> 00:57:49,990
to say like, this might not
be what you're looking for.
932
00:57:49,990 --> 00:57:57,800
If someone is very happy with like
building web apps with Vite Astro NextJS,
933
00:57:57,820 --> 00:58:02,600
et cetera, and they're productive,
they're building this, e-commerce
934
00:58:02,600 --> 00:58:08,930
platform, or they're building a more
static website, I don't think there's
935
00:58:08,930 --> 00:58:14,524
anything really where local-first
would change their work situation.
936
00:58:14,914 --> 00:58:21,154
But if they're frustrated with like actual
apps that they use day to day, when you're
937
00:58:21,154 --> 00:58:25,054
frustrated like yourself, when you're
frustrated with Notion being too slow,
938
00:58:25,054 --> 00:58:30,034
et cetera, and you're building those more
productivity daily driver apps yourself.
939
00:58:30,304 --> 00:58:32,134
For me that was like a music app.
940
00:58:32,134 --> 00:58:34,834
I got frustrated with
Spotify and other music apps.
941
00:58:35,164 --> 00:58:40,517
I think this is the, right scenario where
like local-first has something to offer,
942
00:58:40,907 --> 00:58:47,177
but, and I think it has also the potential
to become a lot simpler and easier over
943
00:58:47,177 --> 00:58:51,227
has already become a lot simpler and
easier or the past couple of years, and
944
00:58:51,287 --> 00:58:53,627
it's gonna be even more so in the future.
945
00:58:53,897 --> 00:58:56,987
And there will be use cases
where it's actually simpler.
946
00:58:57,442 --> 00:59:02,152
To use local-first to build something,
then using Next for something.
947
00:59:02,675 --> 00:59:05,255
but that won't apply to all scenarios.
948
00:59:05,645 --> 00:59:07,475
And so it is not a silver bullet.
949
00:59:07,922 --> 00:59:12,152
the closest thing you'll get to a silver
bullet is the right architecture for
950
00:59:12,152 --> 00:59:17,612
the right application scenario, but
by default there is no silver bullet.
951
00:59:17,882 --> 00:59:19,272
Neither is local-first.
952
00:59:19,292 --> 00:59:22,742
And I think someone should
evaluate, Hey, is this even for me?
953
00:59:23,139 --> 00:59:24,729
that's, I think should
be the starting point.
954
00:59:25,282 --> 00:59:34,072
Yeah, and a meta comment just because
now I'm in the agent coding space, Warp
955
00:59:34,072 --> 00:59:38,392
is getting more capable by the day of
actually editing files and scaffolding new
956
00:59:38,392 --> 00:59:41,032
applications for you, from the terminal.
957
00:59:41,685 --> 00:59:47,205
I've found it's less valuable to know the
syntax of how all of these libraries work
958
00:59:47,235 --> 00:59:51,045
and a lot more valuable to just know high
level, what are they doing, what's the
959
00:59:51,045 --> 00:59:52,875
architecture and how would I debug it?
960
00:59:53,486 --> 00:59:56,892
because these agents are very
good at spitting out the syntax,
961
00:59:56,892 --> 00:59:58,422
if you draw a very clear picture.
962
00:59:59,007 --> 01:00:03,057
So if you go off and read designing
data intensive applications, and you
963
01:00:03,057 --> 01:00:06,987
start diagramming to yourself how all
of these systems are distributed, you
964
01:00:06,987 --> 01:00:12,387
could bring that diagram to Warp or
just the cloud website if you want, and
965
01:00:12,387 --> 01:00:13,857
say, I wanna build this kind of app.
966
01:00:13,857 --> 01:00:15,717
Here's how the architecture's gonna work.
967
01:00:15,777 --> 01:00:19,647
This is gonna talk to this,
and I know about this library.
968
01:00:19,647 --> 01:00:23,727
I know LiveStore uses event sourcing,
so I would like you to implement that
969
01:00:24,147 --> 01:00:28,767
and use React, but follow the handrails
because I understand the architecture.
970
01:00:29,097 --> 01:00:32,727
It'll give you a way better application
than if you were to just say, give
971
01:00:32,727 --> 01:00:35,067
me a local-first app with React.
972
01:00:35,127 --> 01:00:38,547
It would probably maybe not struggle
in the beginning, but definitely
973
01:00:38,547 --> 01:00:43,137
struggle as you try to figure out what
it is doing or debug whatever sort
974
01:00:43,137 --> 01:00:44,757
of system level issues you're having.
975
01:00:45,357 --> 01:00:46,497
I fully agree.
976
01:00:46,497 --> 01:00:50,817
And given that the both of us are not
just application developers but also
977
01:00:50,847 --> 01:00:55,707
tool creators, we spend a lot of time
thinking about like, how do I leverage
978
01:00:55,707 --> 01:01:00,597
the degree of freedom that I have here
in the API, the way how I design the API
979
01:01:00,837 --> 01:01:05,007
that is intuitive for someone that they
like, ideally that this becomes like a
980
01:01:05,007 --> 01:01:09,597
pit of success where they intuitively
use it the right way, but also if they
981
01:01:09,597 --> 01:01:11,667
use it the wrong way, how do they notice?
982
01:01:11,817 --> 01:01:17,187
Do they notice like as early on as
possible through type safety or only
983
01:01:17,187 --> 01:01:20,397
if they're already in production
and they felt like, wait, no,
984
01:01:20,397 --> 01:01:24,387
this, like, this was a path that
I've wrongly taken six months ago.
985
01:01:24,744 --> 01:01:30,424
so you want to design all of this in a way
that you like learn as early as possible
986
01:01:30,424 --> 01:01:31,864
whether you're in the right track or not.
987
01:01:31,864 --> 01:01:36,544
And I think you can't get better than
simplicity than going for simplicity.
988
01:01:36,544 --> 01:01:41,684
And this is why I love the path that
you've taken with the Simple Sync Engine.
989
01:01:42,134 --> 01:01:46,484
Through the push pull model because that's
already, that is deeply familiar for
990
01:01:46,484 --> 01:01:51,044
developers and that is how we're using
Git and that has really been proven.
991
01:01:51,074 --> 01:01:53,804
And there you can't really
get much simpler than that.
992
01:01:54,314 --> 01:01:57,224
And I think simple is great for everyone.
993
01:01:57,621 --> 01:02:00,981
and once we have a simple foundation,
we have a reliable foundation.
994
01:02:00,981 --> 01:02:03,591
We can build fast and
nice things on top of it.
995
01:02:03,921 --> 01:02:08,931
But particularly mentioning AI systems,
I make a lot of design trade offs
996
01:02:08,931 --> 01:02:12,861
now differently, where I care less
about how much effort it will be
997
01:02:12,861 --> 01:02:15,591
to write or to discover that thing.
998
01:02:15,591 --> 01:02:20,571
Since we have now LLMs, do TXT, et
cetera, I care a lot more about like,
999
01:02:20,571 --> 01:02:24,821
how does, how will you even spot
where like this doesn't seem right.
1000
01:02:24,821 --> 01:02:29,321
The robot has given me something
weird and just doesn't match my,
1001
01:02:29,321 --> 01:02:32,981
like, primitive understanding of
how this entire thing fits together.
1002
01:02:33,281 --> 01:02:37,091
And that should also help the
robot to like not go in the wrong
1003
01:02:37,151 --> 01:02:39,101
direction in, in the first place.
1004
01:02:39,461 --> 01:02:41,504
So, yeah, I love that.
1005
01:02:41,714 --> 01:02:47,071
Like, and going for a simple design
decision, the simple like overall
1006
01:02:47,101 --> 01:02:52,906
system architecture that's gonna help
you as a future programmer, observing
1007
01:02:52,966 --> 01:02:57,609
little robots, building things, a
lot more to know what's going on.
1008
01:02:57,639 --> 01:03:01,659
So maybe we use that as a last
segue to hear a little bit more
1009
01:03:01,659 --> 01:03:06,219
about what you're doing now at Warp
in regards to agents, et cetera.
1010
01:03:06,219 --> 01:03:08,856
I've been using Warp for, a little bit.
1011
01:03:09,306 --> 01:03:15,356
I still use it, for my standalone,
terminal, but most of my terminal
1012
01:03:15,356 --> 01:03:20,366
work is also happening within Cursor,
which is integrated in like the
1013
01:03:20,666 --> 01:03:22,676
agent thing and Cursor, et cetera.
1014
01:03:22,706 --> 01:03:27,176
Maybe can, yeah, help me a little bit
of like how I bring those two together
1015
01:03:27,386 --> 01:03:29,041
and use them for what they're best at.
1016
01:03:29,509 --> 01:03:35,719
Yeah, and it's an interesting world
of sort of agentic coding solutions.
1017
01:03:35,719 --> 01:03:38,269
It feels like there's a new
approach to it every other day.
1018
01:03:38,611 --> 01:03:42,563
before joining Warp, I was also
using Kline a lot, which is a VS Code
1019
01:03:42,563 --> 01:03:47,573
extension that's fully open source
that will, from my experience, give
1020
01:03:47,573 --> 01:03:49,643
you a more quality agent output.
1021
01:03:49,793 --> 01:03:54,443
Since it has these two phases,
you can flip on a plan switch and
1022
01:03:54,443 --> 01:03:59,693
it'll use a reasoning model to
take whatever you tell it and turn
1023
01:03:59,693 --> 01:04:01,853
it into like a step-by-step plan.
1024
01:04:02,003 --> 01:04:04,493
And you could walk through like, no,
that architecture doesn't make sense,
1025
01:04:04,493 --> 01:04:06,653
or Let me upload this six cal draw.
1026
01:04:06,653 --> 01:04:07,883
I actually want to work like this.
1027
01:04:07,883 --> 01:04:11,003
And you can go back and forth on
like a design doc and then you can
1028
01:04:11,003 --> 01:04:15,383
flip it to act mode and that engages
Claude to go build that for you.
1029
01:04:15,863 --> 01:04:19,613
And it's very hit or miss actually
doing like the code edits.
1030
01:04:19,643 --> 01:04:20,933
We're all struggling with that.
1031
01:04:21,563 --> 01:04:24,983
but it was like this really nice
mental model of, oh yeah, we're
1032
01:04:24,983 --> 01:04:26,363
gonna plan it out together.
1033
01:04:26,648 --> 01:04:29,798
We're gonna design jam, how this
thing's gonna work and then we're
1034
01:04:29,798 --> 01:04:33,908
gonna go build it and I can just let
it run and see how it ends up working.
1035
01:04:34,338 --> 01:04:39,588
and Warp is doing something similar
but not within the confines of VS code.
1036
01:04:39,888 --> 01:04:43,788
And also with the addition of a voice
button that I've been using a lot more
1037
01:04:43,788 --> 01:04:48,948
recently because you can talk faster than
you can type is generally what I found.
1038
01:04:48,978 --> 01:04:52,608
So I can just speak into my terminal,
here's how I want the app to
1039
01:04:52,608 --> 01:04:53,868
work, this, that, and the other.
1040
01:04:54,138 --> 01:04:58,008
And then it will, depending on how
complicated the question is, it
1041
01:04:58,008 --> 01:04:59,778
will reach for the planning step.
1042
01:05:00,018 --> 01:05:01,878
Otherwise it'll just give
you an answer right away.
1043
01:05:01,998 --> 01:05:04,488
So if it, see that's kind of
complicated, let me plan it out.
1044
01:05:04,788 --> 01:05:08,568
It'll give you that same kind of like
document, here's how it's going to work.
1045
01:05:08,928 --> 01:05:10,878
And then you can say, okay, do it.
1046
01:05:10,908 --> 01:05:15,408
And then it will go off and tell
Claude to make file edits and do other
1047
01:05:15,408 --> 01:05:19,068
things on your machine, which is much
further than Warp has gone in the past.
1048
01:05:19,614 --> 01:05:23,214
and I've really enjoyed using
this to build Swift apps recently.
1049
01:05:23,544 --> 01:05:27,084
Since I was just fascinated with
like, how could I build a really
1050
01:05:27,084 --> 01:05:31,764
slick desktop client like ChatGPT
there's this desktop shortcut to
1051
01:05:31,764 --> 01:05:33,264
like pull up a little chat bar.
1052
01:05:33,744 --> 01:05:36,174
Like I want something that integrated.
1053
01:05:36,234 --> 01:05:38,424
I don't wanna be confined
to Google Chrome anymore.
1054
01:05:38,424 --> 01:05:39,564
I wanna break out of it.
1055
01:05:40,021 --> 01:05:45,451
but if you open up XCode, you're just
met with this like decade old auto
1056
01:05:45,451 --> 01:05:49,441
complete that doesn't have anything that
you want in order to get stuff done.
1057
01:05:50,028 --> 01:05:51,618
but Warp is just a terminal.
1058
01:05:51,618 --> 01:05:54,378
So I'm like, okay, I'll just
open the Swift project in Warp
1059
01:05:54,378 --> 01:05:55,668
and say implement this feature.
1060
01:05:56,058 --> 01:06:00,318
And it doesn't it, it can literally
just enter any directory that you
1061
01:06:00,318 --> 01:06:02,088
have and just start doing things.
1062
01:06:02,508 --> 01:06:07,428
I've also used it to like migrate my open
source projects from a mono repo to a
1063
01:06:07,428 --> 01:06:12,348
set of micro repos on my system and says,
oh yeah, I'll just make a new directory.
1064
01:06:12,348 --> 01:06:16,538
I'll move all those files over and I'll
make the necessary file edits with Cloud.
1065
01:06:17,058 --> 01:06:19,158
Very like hit or miss quality.
1066
01:06:19,248 --> 01:06:20,268
We're dialing it in.
1067
01:06:20,628 --> 01:06:25,608
But this idea of you're not constrained
to the IDE anymore, you can kind
1068
01:06:25,608 --> 01:06:30,528
of just pull up your terminal and
ask it to modify anything from the
1069
01:06:30,528 --> 01:06:34,698
simplest request of, help me get
revert, whatever the heck I just did.
1070
01:06:34,968 --> 01:06:38,838
And it'll help you get to something
more complicated, like, why
1071
01:06:38,838 --> 01:06:40,488
isn't my Postgres server running?
1072
01:06:40,488 --> 01:06:42,618
And then it'll check your
Homebrew installation.
1073
01:06:43,158 --> 01:06:47,448
And then you can take it one step
further to, I actually want to fix this
1074
01:06:47,448 --> 01:06:49,728
error I see in my dev server right now.
1075
01:06:50,058 --> 01:06:53,118
Because you're running the dev server
in your terminal, it can say, all
1076
01:06:53,118 --> 01:06:57,198
right, pause the server, debug,
debug, debug, restart the server.
1077
01:06:57,618 --> 01:07:01,128
And then if something fails
again, I'll go back to debugging.
1078
01:07:01,338 --> 01:07:04,278
So it's like watching your
terminal session and figuring
1079
01:07:04,278 --> 01:07:05,868
out how to help you do something.
1080
01:07:06,288 --> 01:07:10,518
It feels like it's this natural next
step of let's go from you're in an
1081
01:07:10,518 --> 01:07:14,868
editor typing code quickly to like,
this is a general purpose tool on
1082
01:07:14,868 --> 01:07:18,918
your machine to edit all the software
that you're writing in any setting.
1083
01:07:19,588 --> 01:07:22,138
so I'm just very excited
about that kind of future.
1084
01:07:22,378 --> 01:07:25,078
And we've been moving very,
very quickly towards it.
1085
01:07:25,108 --> 01:07:30,448
Just in the past month, it's gone from
barely usable to, I'm actually using
1086
01:07:30,448 --> 01:07:34,408
this a lot for projects in a language
that I don't even know how to speak.
1087
01:07:34,468 --> 01:07:35,068
Swift.
1088
01:07:35,568 --> 01:07:38,988
it's been kind of crazy how far
you can get from zero to one
1089
01:07:39,108 --> 01:07:40,638
without a lot of field knowledge.
1090
01:07:41,313 --> 01:07:45,603
And intuitively this makes a lot of sense
since like Eternal is kind of like the
1091
01:07:46,083 --> 01:07:51,333
OG chat up in a way where like all the
way back to like IRC, et cetera, but
1092
01:07:51,333 --> 01:07:57,569
also now with, using ChatGPT a lot or,
or other LLM chat products, like yes,
1093
01:07:57,569 --> 01:08:01,986
you're, chatting with the thing, like
you write a command, the command happens
1094
01:08:01,986 --> 01:08:05,899
to be like plain English or another
language, and you get something back.
1095
01:08:06,169 --> 01:08:08,073
But the, kinda like back and forth.
1096
01:08:08,463 --> 01:08:14,708
And the interplay is very similar and it
makes so much more, so much sense that you
1097
01:08:14,708 --> 01:08:19,418
now bring that into the terminal as well,
where you get the best of both worlds.
1098
01:08:19,418 --> 01:08:22,628
You can like ride out
things in a fuzzy way.
1099
01:08:22,658 --> 01:08:27,278
The terminal helps you to like put
that into proper computer speak.
1100
01:08:27,641 --> 01:08:32,441
but then you also get the efficiency and
the correctness from what you can do in
1101
01:08:32,441 --> 01:08:38,411
a terminal and with all of like just a
top-notch craft that you get within Warp
1102
01:08:38,501 --> 01:08:41,021
as a terminal with like blocks, et cetera.
1103
01:08:41,261 --> 01:08:44,191
So yeah, highly recommend
everyone to, give it a try.
1104
01:08:44,654 --> 01:08:44,924
Yeah.
1105
01:08:44,924 --> 01:08:47,384
And it's free to reach
for all those things.
1106
01:08:47,444 --> 01:08:50,744
And anyone who is just bothered by
AI in their terminal, you can turn it
1107
01:08:50,744 --> 01:08:55,484
off and just use Warp as a really good
terminal, which is how I started using
1108
01:08:55,484 --> 01:09:01,044
it way back, probably like, 2021, I think
is when it said I created my account.
1109
01:09:01,466 --> 01:09:04,844
I just used it because I wanted
something that looked nice and
1110
01:09:04,904 --> 01:09:06,854
now it's going a lot deeper.
1111
01:09:06,944 --> 01:09:09,201
And yeah, the, chat
app analogy is perfect.
1112
01:09:09,201 --> 01:09:14,331
There's literally a toggle between typing
out commands and asking it a question,
1113
01:09:14,811 --> 01:09:19,791
and it'll even flip back and forth based
on like natural language, which is fancy.
1114
01:09:19,791 --> 01:09:22,251
I mean, I'll just hit the
keyboard shortcut, but why
1115
01:09:22,251 --> 01:09:23,691
not make it a little flashier?
1116
01:09:24,351 --> 01:09:25,071
That is awesome.
1117
01:09:25,071 --> 01:09:28,324
Well, I've already seen, a
bunch of your recent videos,
1118
01:09:28,366 --> 01:09:30,121
about content related to that.
1119
01:09:30,121 --> 01:09:32,041
I'm looking forward to many more of those.
1120
01:09:32,431 --> 01:09:37,821
Is there anything else that you wanna
share related to your local-first,
1121
01:09:38,193 --> 01:09:39,924
explorations or otherwise?
1122
01:09:40,573 --> 01:09:41,053
Yeah.
1123
01:09:41,259 --> 01:09:44,739
so my profile is bholmesdev everywhere.
1124
01:09:44,799 --> 01:09:50,439
So any learning resources I've put
out like videos on local-first and
1125
01:09:50,439 --> 01:09:54,909
conference talks, bholmesdev on
YouTube and Twitter and Bluesky.
1126
01:09:55,376 --> 01:09:59,036
And also on GitHub, so these Simple
Sync Engine project I mentioned,
1127
01:09:59,096 --> 01:10:00,506
that's on my personal GitHub.
1128
01:10:00,536 --> 01:10:02,276
Also under bholmesdev.
1129
01:10:02,486 --> 01:10:05,606
You should see it as one of the star
repos if you look up the profile.
1130
01:10:06,086 --> 01:10:06,416
That's it.
1131
01:10:06,416 --> 01:10:06,806
Perfect.
1132
01:10:07,676 --> 01:10:11,503
I've also put it in the show notes, so
everything, you'll find it there as well.
1133
01:10:11,503 --> 01:10:17,203
But, I'm really, really excited you have
put in the effort to create this project,
1134
01:10:17,439 --> 01:10:21,946
because I think there's no better way
to learn something than trying to do it.
1135
01:10:22,276 --> 01:10:25,096
And you've done that and you've
allowed other people to follow
1136
01:10:25,096 --> 01:10:29,206
your footsteps, and I think that's
a fantastic learning resource.
1137
01:10:29,206 --> 01:10:34,426
So thank you so much for doing that
and for, yeah, helping others learn
1138
01:10:34,456 --> 01:10:37,899
and, sharing what you've learned
and for coming on the show today.
1139
01:10:37,929 --> 01:10:38,349
Thank you.
1140
01:10:38,856 --> 01:10:39,636
Yeah, thanks so much.
1141
01:10:39,696 --> 01:10:41,976
This was like a really
far reaching conversation.
1142
01:10:42,186 --> 01:10:42,906
I hope it turns out good.
1143
01:10:43,637 --> 01:10:46,217
Thank you for listening to
the localfirst.fm podcast.
1144
01:10:46,397 --> 01:10:49,487
If you've enjoyed this episode and
haven't done so already, please
1145
01:10:49,487 --> 01:10:50,777
subscribe and leave a review.
1146
01:10:51,167 --> 01:10:53,687
Please also share this episode
with your friends and colleagues.
1147
01:10:54,077 --> 01:10:57,077
Spreading the word about the
podcast is a great way to support
1148
01:10:57,077 --> 01:10:58,787
it and to help me keep it going.
1149
01:10:59,447 --> 01:11:02,867
A special thanks again to Jazz
for supporting this podcast.
1150
01:11:03,167 --> 01:11:04,127
I'll see you next time.