0:17:04 Absolutely.
0:17:04 So, you know, the first principle
I talked about is thinking of an
0:17:08 application as a database query.
0:17:10 Right?
0:17:10 And, actually, to throw a
little more detail in there,
0:17:13 it's not one database query.
0:17:15 It's actually a graph of database
queries with dependencies.
0:17:17 Now that starts to sound kind of
fancy, but I think we all understand
0:17:21 this model from using spreadsheets.
0:17:23 The the metaphor, I think, to
think about is can You make coding
0:17:27 a rich, complex application as
simple as using a spreadsheet.
0:17:32 Spreadsheets dependency tracking
of formulas and automatic
0:17:36 Dependency propagation is the
bread and butter of spreadsheets.
0:17:38 It's one of the key features
that makes them useful.
0:17:41 Right?
0:17:41 When you're using a spreadsheet
and you update a cell, The contract
0:17:44 that Excel gives you is typically
instantly or at least very quickly,
0:17:48 unless you're in a huge spreadsheet,
everything will be up to date.
0:17:51 You're never worried about staleness.
0:17:53 You're never thinking about caching.
0:17:55 That complexity of dependency propagation
is pushed down into the system.
0:17:59 Right?
0:18:00 And I would note, I think this
is a lesson that we've definitely
0:18:03 learned in UI development.
0:18:05 You could definitely say that reactive
UI frameworks like, React JS give
0:18:10 you something like this guarantee.
0:18:12 Right?
0:18:12 They you declare the UI you want and
state and DOM are synced automatically.
0:18:18 The problem that I see is that often
in our web stacks, this reactivity
0:18:22 is partial to one part of the stack.
0:18:25 So in in a common web application,
your React UI, what it's
0:18:29 faithfully representing is not
all the state in your system.
0:18:33 It's some view you currently have in
this tab of some larger state that is,
0:18:38 you know, maybe primarily on a server.
0:18:41 And that introduces a lot of complexity
because all of a sudden, you have
0:18:44 your nice declarative spreadsheety
React thing, but then you have At
0:18:48 some point in your stack, you're
gonna start hitting data fetching.
0:18:51 You're gonna start hitting sending
rights back up to the server.
0:18:55 And, that's just an enormous source
of mental model overhead, I think.
0:19:00 On the flip side there have been some
interesting research projects which come
0:19:03 out from the other side, And people,
for example, have literally tried to
0:19:06 make UIs in things like Google Sheets.
0:19:09 There's a really neat project by Ted
Benson and collaborators called Quiltstep.
0:19:13 Literally, they use Google Sheets to power
your entire back end and all your your,
0:19:18 you know, UI templating and essentially.
0:19:20 And the problem there is normal
spreadsheets aren't really powerful
0:19:23 enough to do that, and they're not
really architected the right way.
0:19:27 one way to think about what we're
trying to do in Riffle is can you take
0:19:30 the beautifully simple model of just
declarative dependency propagation, and
0:19:35 apply that to your entire data stack.
0:19:38 So your mental model as developer should
be, I just have all the data locally.
0:19:42 I'm just writing a spreadsheet
that turns it into a UI, and the
0:19:47 system will take care of the rest.
0:19:48 Full stack reactivity.
0:19:50 That's sort of principle number one.
0:19:52 That makes that makes total sense.
0:19:54 And I think you've Touched on also, like,
the the third principle, which I remember
0:19:58 now is, like, the the unifying the UI
states and sort of the the other app data.
0:20:03 And if you really think about it, where do
you even draw the line between something
0:20:07 being UI state, something being app data?
0:20:10 There is sort of like a arbitrary line.
0:20:12 Maybe I think de facto in most today's
web apps is the app data is, like,
0:20:17 what you fetch over something like
React Query as your as your website
0:20:22 boots up or you have it pre hydrated.
0:20:25 And the app state It's more of like
the in memory stuff that if you
0:20:29 reload the pages, poof, it's gone.
0:20:32 But, ideally, most of the things should
really be treated in a very similar
0:20:36 way when I'm thinking of a native
Mac app, for example, like Finder.
0:20:41 If I close a window and I come back,
The same items are still selected.
0:20:46 I'm still, like, roughly in the the
same position with the the folders that
0:20:51 I've selected and and the hierarchy.
0:20:53 And most web apps, I would actually
suffer from that they they have, like
0:20:58 basically, they're suffering from
dementia when you and insert something
0:21:02 in a form, and maybe you really need to
look up every value and maybe the form
0:21:08 expires, you reload, everything is gone.
0:21:10 That's I think everyone has, like,
those terrible user experiences.
0:21:14 And so treating everything as the
same data, I think is is a kind
0:21:19 of a bold, but in my experience,
like, the the right step.
0:21:23 And then if you can, Therefore, as a
conclusion, kind of, like, have all
0:21:28 the data that you need to work with.
0:21:30 Have that locally.
0:21:31 It simplifies everything to such
a degree that's really liberating.
0:21:36 So that that's sort of the the third the
principle, and I think that's that's a
0:21:40 really bold one that is, I think the most
provocative compared to the traditional
0:21:45 way of building web apps today.
0:21:48 Yeah.
0:21:48 Exactly.
0:21:48 You know, just like you said I think it
can be a disrespectful user experience
0:21:52 to lose people's valuable input.
0:21:55 Even something like Scroll position
or, like you said, intermediate
0:21:59 form state can be thing something
that I put a lot of work into and
0:22:03 I don't wanna necessarily lose.
0:22:04 Right?
0:22:05 Now I think the deeper reason we
end up with software like this
0:22:10 isn't that people don't care.
0:22:11 It's really that we've made it hard to do.
0:22:14 So, again, it's this technical
defaults ruling, the user experience
0:22:18 situation that we talked about where,
You know, imagine you're a product
0:22:22 manager and you get a request to
please persist to this user input or
0:22:26 share this user input across users.
0:22:28 And the engineer says, oh, man.
0:22:30 That was previously React components
data, and now I need to, like, rearchitect
0:22:34 where that data is stored and plummet
through a bunch of places and Add a
0:22:38 database column for it, blah blah blah.
0:22:40 It's all this work.
0:22:42 And the goal of having a
unified system for your state.
0:22:44 It's not that all states
should be treated the same.
0:22:47 Definitely, some state, you know, should
be user local, device local, and so on.
0:22:51 There, Yeah.
0:22:52 Some state you want to reset
on new sessions by default.
0:22:56 So you can think of that as ephemeral.
0:22:58 But If you store it all in one system
and manage it all in one system,
0:23:02 the goal is that it's becomes much
easier to move between, these states.
0:23:06 Right?
0:23:07 one example I love is that someone
who's working on a collaborative app
0:23:10 told me the state of whether a context
menu is open in a spatial canvas app.
0:23:17 They actually realized they needed
it to be shared because when I open a
0:23:21 context menu, my cursor is moving around.
0:23:22 If you can't see the context
you I'm using, you don't
0:23:25 really know what I'm doing.
0:23:27 And so, I think that's a perfect
example of blurring these boundaries
0:23:30 where something that Seems like clearly
quote, unquote, local component state
0:23:35 in a traditional framing might actually
need to be collaboratively synced
0:23:40 or, you know, maybe even persisted.
0:23:42 So that's what I see as the goal of having
this unified state management system.
0:23:47 And in the Riffle work that's
basically the the maximalist
0:23:50 view we took is no react state.
0:23:52 Right?
0:23:53 Every single bit of state in the system
flows through this one database, and your
0:23:58 UI is just derived from that one place.
0:24:01 And You can always reason about
essentially, the pixels on the screen
0:24:05 are a function of the database.
0:24:07 That's it.
0:24:07 Nothing else.
0:24:08 And we found a lot of neat qualities
that that design led to along with
0:24:13 a bunch of interesting challenges.
0:24:15 And and I have to say, I was I was not
on board with that when we got started.
0:24:20 I was so attached to like, everything
is in React, and you have, like, things
0:24:26 inside of React components, etcetera.
0:24:28 And I remember Nicholas, he was, like,
on the on the opposite side there.
0:24:33 And so, like, no.
0:24:34 No.
0:24:34 No.
0:24:34 Like, React should do as little
as possible and, like, have your
0:24:37 state outside, and that didn't
click quite back then for me.
0:24:42 But step by step, I came around
to it, and I'm now also, like, on
0:24:46 a maximalist side of, like, most
state should live outside of React.
0:24:50 And for example, for Overtone,
there's a very simple case where
0:24:54 you for example, for the app
for the playback of your player.
0:24:58 Like, you want your playback
to happen to happen.
0:25:01 You wanna hear something.
0:25:02 Maybe you captured the the time code
of the the current playback progress.
0:25:07 And even if the app is not open,
You wanna still hear things.
0:25:12 If you reload the app you might
just still want to recover at the
0:25:16 the correct playback position.
0:25:18 So all of that state is kinda headless.
0:25:20 So the state still lives on and,
therefore wouldn't quite make sense to
0:25:25 have that be part of React, you say.
0:25:27 Sure.
0:25:27 You could say you have, like, a headless
React component, but that's already
0:25:32 feels a bit backwards at that point.
0:25:34 So Once I've started embracing that,
it was hugely liberating, and now I can
0:25:40 start to reason about my state outside
of the context of React components in
0:25:46 that outside of reactive view state.
0:25:49 So That took me a bit to get
around to it, but I'm fully on board
0:25:53 with that principle now as well.
0:25:55 you know, I'll I'll mention, I
don't think we're the we're not
0:25:57 the only ones who've of this right.
0:25:58 I think um, the idea of having a
reactive state graph separate from
0:26:02 your UI tree is something that other
web dev libraries have explored.
0:26:07 MobX, Recoil, arguably, even something
like Redux, I think shows some of the
0:26:11 power of having this sort of centralized,
outside of your UI tree state management.
0:26:16 So, there's definitely, I think
some broad recognition that this
0:26:21 can be a powerful pattern for for
some of the reasons you mentioned.
0:26:25 You know, but I'll also mention,
like, there are some challenges.
0:26:27 Right?
0:26:27 And, I mean, You've
encountered some of these.
0:26:29 I'd be curious to hear your take.
0:26:30 Some of them that I think immediately
come to mind for people are, one,
0:26:35 Performance is always an easy one.
0:26:37 Like, typically, we expect button
hovers and, you know, selection
0:26:42 changes to happen at 60 120 Hertz.
0:26:45 And we don't expect things like
loading thousands of rows of
0:26:50 data from our core data store to
necessarily happen at the same speed.
0:26:54 And so that mismatch can be an an
an interesting thing to Manage.
0:27:00 Our motto Nicholas's motto to some
extent is just make everything fast.
0:27:03 If everything goes at 120 FPS,
then don't have a problem.
0:27:06 Right?
0:27:07 Easier said than done, but I
think a fun goal to shoot for.
0:27:11 So far, most mostly what
we've been talking about is
0:27:13 conceptually a simplification.
0:27:16 Getting on board with that
different way of thinking about
0:27:19 your your app on a conceptual basis.
0:27:21 And I think for me, it took a little
bit of time to ease into that,
0:27:26 and then, be confronted with the
new challenges that are raising.
0:27:29 We we'll we'll go into the
challenges in a second since
0:27:32 We've encountered quite a few.
0:27:34 But I think we didn't quite touch too
much on the the second principle you've
0:27:38 motivated before, which is, uh, the
synchronous reactivity, and, like,
0:27:43 that should be as fast as possible.
0:27:45 We'll we'll go into the performance
challenges there in a bit.
0:27:48 But when you've said synchronous
reactivity as opposed to
0:27:54 asynchronous reactivity, what what
are the why is this a principle.
0:27:58 Like, what is so important?
0:27:59 Can can you motivate the the
problem and the status quo and
0:28:02 how Riffle is challenging that?
0:28:04 . Yeah.
0:28:05 Absolutely.
0:28:05 This is a really
important principle to me.
0:28:08 I think when we Build web
apps that have a server.
0:28:11 We assume a lot of
asynchrony at many points.
0:28:15 So, obviously, going on the network
to a server is a point of asynchrony.
0:28:19 Even often loading data from
you know, like a local disk.
0:28:23 Right?
0:28:23 We think of as an asynchronous operation.
0:28:25 And because of that, UI development
typically has a lot of reasoning
0:28:30 about asynchrony, which is a really
hard thing to do as a programmer.
0:28:34 For example, you might have a UI
where There are loading states.
0:28:38 And everywhere throughout your app,
you need to think about, am I still
0:28:42 loading, or do I have the data?
0:28:43 And maybe you have a UI that's composed
of different sections, and some of them
0:28:47 might be loaded already and others aren't.
0:28:49 You might have intermediate
states to reason about.
0:28:51 Like, After I select something
in a sidebar, there's some time
0:28:55 where the main other pane of the
app hasn't reacted to that yet.
0:28:59 So It might still be showing the old
thing that was there before I selected
0:29:02 the new thing, or it might be showing a
loading state while I'm loading the data.
0:29:06 And my experience at least personally
has been that We kind of accept
0:29:10 this as just the way it is in UI
development, but it's terrible.
0:29:13 It's it's a lot of
overhead to reason about.
0:29:16 There are many, many more states your
system can be in when you have asynchrony.
0:29:21 Right?
0:29:21 And the the vision for synchronous
reactivity is let's make it again
0:29:25 feel more like a spreadsheet.
0:29:26 So the way I want my UI to feel is
that when I select something, on the
0:29:31 next tick, on the next frame, every
pixel in the UI has fully updated
0:29:35 to reflect that new user input.
0:29:38 And that's obviously good for users
because they didn't have to wait
0:29:42 But it's also good for developers
because you don't have to reason
0:29:43 about the intermediate states.
0:29:46 You can sort of think transactionally
where The state of the world changes,
0:29:50 and, transactionally, we update a bunch
of derived views and queries of that state
0:29:56 which end up in the pixels of the UI.
0:29:58 And there was never any
intermediate state exposed.
0:30:01 And it's It's sort of a subtle point to
get across, but my experience has been
0:30:06 that when you work in a system like
this it just Cuts a lot of complexity.
0:30:11 Yeah.
0:30:11 It's not just a simplification in
terms of architecture that we collapse
0:30:16 the stack from back end and API,
etcetera, and all into the client.
0:30:21 But that's, like, one giant
dimension of complexity.
0:30:26 But there is a second, much less
talked about vector of complexity,
0:30:31 which is like, how is how are
things related to each other?
0:30:34 Can they be synchronously
related or can or do they have
0:30:38 to be asynchronously related?
0:30:41 And this is where I think we're getting
into the area of distributed systems.
0:30:45 I'm sure we'll talk a lot more in future
episodes about broader distributed
0:30:50 systems where they're really needed,
where you have You're you have a device.
0:30:54 You're in the US.
0:30:55 I'm in Europe, and so we are distributed.
0:30:57 However, if we are starting to
treat a local React app that's
0:31:01 running in single thread And we're
coordinating the various React use
0:31:06 states things with React useEffect.
0:31:08 We've kinda accidentally created a
distributed system that's It's really hard
0:31:13 to tame and where it is, there can be many
unintended bugs and just starts becoming
0:31:19 a lot more complex to to think about.
0:31:22 So this is the other part where I
think it really it con where Riffle,
0:31:27 again, liberates so much and simplifies
and frees us of the the accidental
0:31:33 distributed systems problem that
we have in most modern web apps.
0:31:37 I, you know, I, think to 2 principles
that I think about one is don't
0:31:43 make something a distributed system
if it really doesn't have to be.
0:31:47 And 2 is if it has to be,
someone smarter than me should
0:31:51 do the distributed systems part.
0:31:53 Right.
0:31:53 Really, we should be providing
abstractions that allow us to not think
0:31:58 about the hard parts most of the time.
0:32:01 Even, you know, I'm not a CRDT expert,
but I know quite a bit about CRDTs and
0:32:05 have done some work on developing CRDTs.
0:32:07 But and so I, you know, I Probably
know more about CRTs than a lot of
0:32:11 app devs, but I don't wanna think
about distributed systems when
0:32:15 I'm building some product feature.
0:32:17 Right?
0:32:17 And I think That is clearly one of the
core challenges in the local-first space
0:32:21 that I think a number of projects and
companies are trying to address, which
0:32:25 is what are the Good abstractions you
can expose to app developers that allow
0:32:29 them to reason about tricky distributed
systems problems in a in a straightforward
0:32:36 way without needing to turn on your sort
of distributed systems brain as much.
0:32:42 Yeah.
0:32:42 Exactly.
0:32:42 And I I think the Being confronted with
distributed systems problems there is
0:32:48 much more common than most app devs think.
0:32:52 So most of the time when in React, you use
useEffect that that hook to, yeah, somehow
0:32:59 tame your local state, Then you already
have a distributed systems problem.
0:33:04 Things like React suspense and and so on.
0:33:07 That that's kind of like a a solution
to address partially the the distributed
0:33:12 systems nature that you have in React.
0:33:14 So, I remember having when React suspense,
and all of those primitives came out.
0:33:19 I was kinda wondering, how does
that fit into our Riffle world here?
0:33:24 would Riffle at some point support
react suspense, and then at
0:33:28 some point, it clicked for me.
0:33:30 No.
0:33:30 That's, like, addressing a problem
that we've already ruled out
0:33:34 before, and we don't even have to
think about that complexity at all.
0:33:39 And yeah, this is, like, entirely
different approach to addressing
0:33:44 the problems that React, Suspense,
etcetera, is addressing in a, I would
0:33:49 say, in a much simpler overall picture.
0:33:52 Yeah.
0:33:52 I think maybe to avoid overclaiming
here, I'll say there are some trade offs.
0:33:57 Right?
0:33:57 I think Often, these approaches that
involve concurrency are they're optimizing
0:34:02 for either the reality of something
like a slow network request or for
0:34:05 sort of worst case performance making
sure that you keep some things running
0:34:09 fast even if other things are slow.
0:34:11 And I think there's a different approach
you can think about, which is, In a
0:34:15 nutshell, just make everything fast.
0:34:17 And as long as everything's
fast, it'll be simple.
0:34:19 If things get slow, you might end
up with a worse experience than you
0:34:22 might have in a system that, you know
that is designed around concurrency.
0:34:26 For example, maybe in a in a Riffle
style approach, if you're trying to
0:34:31 synchronously paint frames and something
is slow, you might just end up with a ton
0:34:35 of dropped frames and sort of a frozen UI.
0:34:38 Whereas in a system designed to account
for that with better concurrency support,
0:34:42 you might have, you know, a loading
spinner and some things remain responsive.
0:34:46 And so I think Part of the
challenge is figuring out how to
0:34:49 how to balance those 2 approaches.
0:34:52 And as we've seen in our work, I think
sometimes you do need to account for
0:34:56 the reality of sometimes you have to
make a network request because, you
0:34:59 know, you're talking to an external
service or something, and you do
0:35:02 need ways to model concurrency.
0:35:03 It's just that I think We don't
need to, I guess, throw the
0:35:07 baby out with the bathwater.
0:35:08 Like, not everything needs to be
concurrent, and some things can be fast.
0:35:12 I think I don't have much game dev
experience, but I my understanding is
0:35:15 that a lot of video games, essentially,
you know, they paint frames, and
0:35:19 they make sure every frame is fast.
0:35:20 And they don't drop frames because
they make sure the frames are fast.
0:35:23 Right?