1 00:00:00,000 --> 00:00:03,659 Well, can I just say that, you know, from the point of view of exhaustiveness, 2 00:00:04,079 --> 00:00:08,670 there are many, many unsolved problems in the local-first space. 3 00:00:09,109 --> 00:00:13,289 And I have a horrible feeling that it's easy to think that 4 00:00:13,309 --> 00:00:14,799 synchronization is the problem. 5 00:00:14,859 --> 00:00:17,309 And then once you've solved synchronization, you're good. 6 00:00:17,905 --> 00:00:19,594 Well, I don't think that's true. 7 00:00:20,014 --> 00:00:23,154 I think we all got hung up on, oh, once we crack CRDTs, we'll be good. 8 00:00:23,584 --> 00:00:24,944 no, no, no, no, that's the easy part. 9 00:00:25,174 --> 00:00:28,304 It's not state management, it's the state of the state management in a way. 10 00:00:28,304 --> 00:00:30,784 It's like, should I be synchronizing this data? 11 00:00:31,045 --> 00:00:35,094 Because this person isn't logged in, or is logged in, or is online, or is 12 00:00:35,094 --> 00:00:39,034 offline, or has shared this document, or hasn't shared this document, and 13 00:00:39,174 --> 00:00:41,734 that is actually unsolved, I think. 14 00:00:42,301 --> 00:00:44,381 Welcome to the localfirst.fm podcast. 15 00:00:44,661 --> 00:00:47,711 I'm your host, Johannes Schickling, and I'm a web developer, a 16 00:00:47,711 --> 00:00:50,801 startup founder, and love the craft of software engineering. 17 00:00:51,221 --> 00:00:55,111 For the past few years, I've been on a journey to build a modern, high quality 18 00:00:55,111 --> 00:00:57,131 music app using web technologies. 19 00:00:57,571 --> 00:01:01,401 And in doing so, I've been falling down the rabbit hole of local-first software. 20 00:01:01,921 --> 00:01:04,841 This podcast is your invitation to join me on that journey. 21 00:01:05,121 --> 00:01:09,801 In this episode, I'm speaking to James Pearce, the author of TinyBase, a reactive 22 00:01:09,801 --> 00:01:12,121 datastore library for local-first apps. 23 00:01:12,491 --> 00:01:16,231 In this conversation, we explore how TinyBase works, including its 24 00:01:16,231 --> 00:01:20,021 custom query system, the various persistence and syncing integrations, 25 00:01:20,271 --> 00:01:22,391 as well as James plans for the future. 26 00:01:22,724 --> 00:01:25,904 Before getting started, also a big thank you to Rosicorp and 27 00:01:25,914 --> 00:01:28,014 Expo for supporting this podcast. 28 00:01:28,334 --> 00:01:30,284 And now my interview with James. 29 00:01:31,168 --> 00:01:32,268 Hey, welcome James. 30 00:01:32,648 --> 00:01:34,818 Nice to meet you and nice to have you on the show. 31 00:01:34,828 --> 00:01:35,428 How are you doing? 32 00:01:35,616 --> 00:01:36,396 I'm doing great. 33 00:01:36,506 --> 00:01:37,706 Thank you so much for having me. 34 00:01:37,806 --> 00:01:40,746 Huge privilege to have a chance to come along and chat 35 00:01:40,746 --> 00:01:41,976 about all things local-first. 36 00:01:42,406 --> 00:01:43,546 yeah, very excited to be here. 37 00:01:43,756 --> 00:01:47,406 It's safe to say that you're the first guest so far in the podcast, 38 00:01:47,476 --> 00:01:52,916 who's calling in from a boat, which I think is very on brand for what 39 00:01:52,916 --> 00:01:55,286 local-first software should enable. 40 00:01:55,696 --> 00:01:59,886 and I think Maybe there's a sort of romantic pitch for why you're doing 41 00:01:59,886 --> 00:02:02,336 local-first, or, or maybe not so much. 42 00:02:02,346 --> 00:02:07,486 I think we'll get to that in a moment, but the first time, I've took notice of 43 00:02:07,486 --> 00:02:12,256 you is like when you've been at Facebook back then taking care of overseeing a 44 00:02:12,256 --> 00:02:13,826 whole bunch of open source projects. 45 00:02:13,836 --> 00:02:18,276 So would you mind taking a step back, briefly introducing yourself and sharing 46 00:02:18,276 --> 00:02:20,276 more about, your time at Facebook? 47 00:02:20,513 --> 00:02:20,943 Sure. 48 00:02:21,033 --> 00:02:21,553 Certainly. 49 00:02:21,573 --> 00:02:22,643 So, I'm James. 50 00:02:22,643 --> 00:02:27,170 I did have a career before Facebook slash Meta, fairly long time ago. 51 00:02:27,170 --> 00:02:28,350 I'm from the UK originally. 52 00:02:28,376 --> 00:02:30,706 I've been in tech nearly all of my professional life. 53 00:02:31,406 --> 00:02:38,986 Moved to California in about, 2010, and started work at Facebook and worked on a 54 00:02:38,996 --> 00:02:40,896 variety of things during my time there. 55 00:02:40,896 --> 00:02:44,476 I was at the company for just over 10 years, but, you know, one of the 56 00:02:44,476 --> 00:02:46,156 highlights for sure was working on. 57 00:02:46,566 --> 00:02:51,193 The open source program, as I'm sure most of the listeners are aware Meta 58 00:02:51,333 --> 00:02:55,923 does have quite a large investment in open source whether it's on the client 59 00:02:55,993 --> 00:03:02,573 JavaScript technology side or on DevTools or on data infrastructure and of course 60 00:03:02,573 --> 00:03:07,323 now on AI, ML and LLM technologies. 61 00:03:07,433 --> 00:03:10,873 And so yeah, it was a, an awesome period of time for me. 62 00:03:10,873 --> 00:03:14,473 It was between about 2014 and 2017. 63 00:03:14,763 --> 00:03:18,173 So I helped out with a bunch of the launches of things like React 64 00:03:18,213 --> 00:03:24,263 and PyTorch and HHVM and various build tools and a whole bunch of things 65 00:03:24,263 --> 00:03:26,063 that people are probably aware of. 66 00:03:26,523 --> 00:03:31,763 And yeah, it was a super exciting time, just in terms of helping to develop how 67 00:03:31,763 --> 00:03:36,413 the company thought about open source and what open source could do for the company. 68 00:03:36,748 --> 00:03:38,501 Wasn't the only thing I did whilst I was at Facebook. 69 00:03:38,551 --> 00:03:42,311 Also worked on Portal, which was the smart speaker device that the company had. 70 00:03:43,331 --> 00:03:46,441 , sadly now discontinued, but, it was, that was pretty awesome working on, 71 00:03:46,811 --> 00:03:48,171 on a hardware project for a bit. 72 00:03:48,621 --> 00:03:52,781 And also spent some time in the data infrastructure team, working on some of 73 00:03:52,781 --> 00:03:57,101 the large data warehouse technologies, that also Meta is fairly well known for. 74 00:03:57,101 --> 00:04:01,190 So, yeah, one of the great things, maybe underappreciated, features of 75 00:04:01,190 --> 00:04:04,430 being an employee at Facebook is you, you do get to, move around a lot. 76 00:04:04,430 --> 00:04:08,103 There are lots of mobility opportunities, and I took the greatest advantage of 77 00:04:08,108 --> 00:04:11,216 that, that I could, and worked on a wide, wide range of different things. 78 00:04:11,276 --> 00:04:16,496 but yeah, enjoyed, every minute of it and then in about 2022. 79 00:04:16,646 --> 00:04:20,210 So yeah, just coming up on two years ago, figured it was a, a good opportunity 80 00:04:20,210 --> 00:04:21,500 to try something completely different. 81 00:04:21,910 --> 00:04:26,740 And so, Moved on, moved on board Scout, which is the boat that you see me on now. 82 00:04:27,110 --> 00:04:31,140 And, sold up, my house and life in Silicon Valley. 83 00:04:31,140 --> 00:04:35,090 And I've been sailing on the ocean waves ever since writing open source 84 00:04:35,090 --> 00:04:36,486 software, so that's where we are. 85 00:04:36,730 --> 00:04:38,290 That sounds quite incredible. 86 00:04:38,330 --> 00:04:40,953 And I mean, Facebook is certainly. 87 00:04:41,123 --> 00:04:46,183 One of the companies that comes to mind is like how to have a positive sum 88 00:04:46,193 --> 00:04:49,143 vision on how to do open source at scale. 89 00:04:49,473 --> 00:04:53,753 , I can't really think of another company that has had such an, such a 90 00:04:53,753 --> 00:04:56,923 positive impact on our web ecosystem. 91 00:04:56,973 --> 00:05:00,558 and in particular With React, with GraphQL. 92 00:05:00,588 --> 00:05:05,218 I'm not as deep on the AI side, but I mean, the recent releases, 93 00:05:05,228 --> 00:05:09,491 et cetera, around Llama, et cetera, this is all, I think a great example 94 00:05:09,541 --> 00:05:15,041 of how a large company can have a very positive impact on the, the 95 00:05:15,041 --> 00:05:17,551 ecosystem beyond its own benefits. 96 00:05:17,561 --> 00:05:21,871 So it's great to hear that you must've had like a very positive impact on that. 97 00:05:22,101 --> 00:05:22,371 Yeah. 98 00:05:22,371 --> 00:05:23,041 It's a lot of this. 99 00:05:23,536 --> 00:05:24,526 took time to develop. 100 00:05:24,526 --> 00:05:29,546 I mean you could argue we stumbled into some of these benefits inadvertently. 101 00:05:29,846 --> 00:05:32,376 I think early on it just seemed like the right thing to do. 102 00:05:32,386 --> 00:05:36,186 The company had been built on other people's open source when, you know, 103 00:05:36,186 --> 00:05:37,496 Mark Zuckerberg started the site. 104 00:05:38,051 --> 00:05:40,711 PHP, running on Apache with MySQL. 105 00:05:41,061 --> 00:05:44,331 And so there was always this sense of standing on the shoulders of other giants. 106 00:05:44,681 --> 00:05:49,268 And so there was a, an almost cultural obligation to share back things like 107 00:05:49,318 --> 00:05:53,048 Memcached and, you know, some of the early things that the company was working on. 108 00:05:53,438 --> 00:05:57,608 but then it began to become apparent that it was more than just that obligation. 109 00:05:57,618 --> 00:06:01,738 It was also, starting to help lead the industry because as Facebook 110 00:06:01,828 --> 00:06:06,348 grew, it was experiencing problems that other apps or other sites 111 00:06:06,378 --> 00:06:07,988 weren't, just because of its scale. 112 00:06:07,988 --> 00:06:11,608 And so it was like the advanced guard of finding out what happens when you 113 00:06:11,608 --> 00:06:13,258 build really complicated web apps. 114 00:06:13,558 --> 00:06:16,198 and so that was what gave birth to React, you know, at the 115 00:06:16,198 --> 00:06:17,548 time that React was launched. 116 00:06:18,153 --> 00:06:22,123 You know, most web apps were still MVC that was kind of the prevailing 117 00:06:22,123 --> 00:06:27,023 pattern and it was still, still super easy to have inconsistencies between 118 00:06:27,023 --> 00:06:29,093 state across a big complicated app. 119 00:06:29,493 --> 00:06:32,743 And so, you know, React's innovation was, you know, how do we make sure that 120 00:06:32,753 --> 00:06:36,783 the, the user interface is always a pure function of data that's gone into it. 121 00:06:37,053 --> 00:06:40,893 And, you know, at the time it seemed heretical, but pretty quickly other 122 00:06:40,893 --> 00:06:42,103 people started to hit the same. 123 00:06:42,563 --> 00:06:45,703 scaling issues with, with MVC apps and, and, and realized 124 00:06:45,703 --> 00:06:46,873 that React was maybe a way out. 125 00:06:46,913 --> 00:06:50,553 And that's the pattern I think at Facebook over and over again, whether it's data 126 00:06:50,553 --> 00:06:55,641 infrastructure or or even ML, you know, the, the company is experiencing problems 127 00:06:55,651 --> 00:06:57,221 that others will face, but haven't yet. 128 00:06:57,271 --> 00:07:02,611 And so it has a chance to maybe share back some of its solutions from the future, so 129 00:07:02,611 --> 00:07:06,001 to speak, so that others then benefit from those as they experience those challenges. 130 00:07:06,521 --> 00:07:09,131 Um, so that was, that was one of the big drivers, I think. 131 00:07:09,381 --> 00:07:10,951 Obviously, it helps with recruiting, right? 132 00:07:10,951 --> 00:07:13,341 Because you're demonstrating the kinds of problems that you're solving 133 00:07:13,651 --> 00:07:16,611 and that gets people excited to come and help solve those problems. 134 00:07:16,911 --> 00:07:20,871 and it also kind of opens the windows onto the kind of code that people are 135 00:07:20,871 --> 00:07:22,391 building at a company like Facebook. 136 00:07:22,721 --> 00:07:26,051 And obviously, there are lots of world class engineers there, and there's no 137 00:07:26,081 --> 00:07:29,241 better way of learning that than seeing the kind of code that they're writing. 138 00:07:29,601 --> 00:07:34,326 , and so it kind of gives you that little bit of visibility into the, the caliber of 139 00:07:34,326 --> 00:07:35,946 the, the engineers that are working there. 140 00:07:35,946 --> 00:07:39,206 And yeah, so there, there were benefits coming back to the company 141 00:07:39,206 --> 00:07:42,940 and just in terms of engineering brand and recruiting, that over time became, 142 00:07:43,080 --> 00:07:45,570 that became so obvious that it was like, okay, now we need to just keep 143 00:07:45,570 --> 00:07:46,830 investing in all of these things. 144 00:07:47,230 --> 00:07:50,950 Um, and it's now more than just a thing that we're doing for fun. 145 00:07:50,950 --> 00:07:53,960 It's now something that's actually bringing real value to the business. 146 00:07:54,280 --> 00:07:54,850 Exactly. 147 00:07:54,860 --> 00:08:01,230 And I think there's no coincidence that now some of the most prolific, open 148 00:08:01,230 --> 00:08:05,790 source and tool builders, actually used to work at Facebook, like thinking of 149 00:08:05,790 --> 00:08:10,640 the people who invented GraphQL, now building other technologies or Matt 150 00:08:10,640 --> 00:08:14,870 Wonlaw, who was just on the show, was also at Facebook for, for a while. 151 00:08:15,050 --> 00:08:17,410 So I think really like this deep culture. 152 00:08:17,655 --> 00:08:21,075 around great developer experience and very principled thinking, 153 00:08:21,085 --> 00:08:22,685 like marrying those two together. 154 00:08:22,985 --> 00:08:27,145 I think this is what, what makes tools really successful and sticky. 155 00:08:27,505 --> 00:08:32,248 And I think this is what the internal, Facebook engineering culture has 156 00:08:32,258 --> 00:08:33,788 sort of like brought to light. 157 00:08:34,015 --> 00:08:36,565 I will say it's not as easy as it sounds. 158 00:08:36,615 --> 00:08:38,805 there are lots of things you have to do behind the scenes and as 159 00:08:38,815 --> 00:08:40,115 someone that was responsible. 160 00:08:40,445 --> 00:08:44,485 for the the overall program rather than individual projects, you know, I 161 00:08:44,485 --> 00:08:49,715 needed to think about how we I guess sell this concept internally, right? 162 00:08:49,715 --> 00:08:53,305 Because if you're an engineering manager and you're running a team and they've 163 00:08:53,305 --> 00:08:57,065 decided they want to spend 20 percent of their time supporting their project on 164 00:08:57,075 --> 00:09:00,635 github You're like wait, but now i'm down 20 percent in terms of like my internal 165 00:09:00,655 --> 00:09:05,115 goals So a lot of it was a lot of the challenges were internally Making sure 166 00:09:05,115 --> 00:09:09,465 that everybody was on board with the, the macro global benefits, even if at a 167 00:09:09,465 --> 00:09:11,985 local level, it seemed like it was a cost. 168 00:09:12,158 --> 00:09:14,398 but we got around that in a variety of different ways. 169 00:09:14,428 --> 00:09:16,978 , one of the ways that, you know, perhaps isn't obvious, but I will 170 00:09:16,988 --> 00:09:20,378 state it because I think it was, it was perhaps even innovative at the 171 00:09:20,378 --> 00:09:25,226 time, is make sure you're using exactly the same code from GitHub internally 172 00:09:25,448 --> 00:09:28,158 and I see lots of companies that, you know, are doing open source, but 173 00:09:28,158 --> 00:09:31,388 it's a fork that they've thrown over the wall from a couple of years ago. 174 00:09:31,598 --> 00:09:34,078 And now they've diverged and they're doing something completely different internally. 175 00:09:34,541 --> 00:09:38,231 and that never works because of course that always gets forgotten about relative 176 00:09:38,231 --> 00:09:39,651 to the thing that's being used internally. 177 00:09:39,991 --> 00:09:44,511 But I think it's pretty awesome that you go to GitHub and you look at slash 178 00:09:44,551 --> 00:09:46,671 react, and then you go to facebook. 179 00:09:46,871 --> 00:09:48,101 com and you view source. 180 00:09:48,451 --> 00:09:52,468 And you will see exactly the same code, because the, the internal version of 181 00:09:52,478 --> 00:09:56,168 React and the external version are being synchronized commit by commit. 182 00:09:56,501 --> 00:09:58,471 and so that means that you never get out of sync. 183 00:09:58,761 --> 00:10:01,111 It means that the engineers who are working internally get to 184 00:10:01,111 --> 00:10:04,221 see their value immediately going to the community and vice versa. 185 00:10:04,221 --> 00:10:07,631 The community gets to see their value, appearing on the world's largest website. 186 00:10:07,681 --> 00:10:11,735 So, those things are hard, like, infrastructurally to get working, but 187 00:10:11,745 --> 00:10:15,345 those are the kinds of things that, that then help the whole open source machine 188 00:10:15,375 --> 00:10:17,585 work inside a large company like that. 189 00:10:18,225 --> 00:10:24,735 So in 2022, you mentioned you sold your house, you, like, made a drastic change to 190 00:10:24,735 --> 00:10:26,801 your life, and you moved onto your boat. 191 00:10:27,141 --> 00:10:29,698 how Did that lead you to local-first? 192 00:10:29,718 --> 00:10:35,178 It seems to coincide a bit timing wise to, to also, start like working 193 00:10:35,208 --> 00:10:37,468 on TinyBase as your primary project. 194 00:10:37,678 --> 00:10:38,788 Can you share more about that? 195 00:10:39,038 --> 00:10:42,098 Yeah, I guess with the benefit of hindsight, you can rework the 196 00:10:42,098 --> 00:10:45,058 origin story a little bit and say, Oh yeah, it was all intentional. 197 00:10:45,298 --> 00:10:46,438 it wasn't, of course. 198 00:10:46,488 --> 00:10:46,938 I had. 199 00:10:47,068 --> 00:10:52,238 Some good milestones in my life you know, my, my kids were leaving home and 200 00:10:52,238 --> 00:10:57,528 had reached my 10 year anniversary at Facebook and the boat that we had been 201 00:10:57,538 --> 00:10:59,628 dreaming about, you know, was, was ready. 202 00:10:59,898 --> 00:11:02,998 and so it seemed like it was a good idea to do all these, these things at the same 203 00:11:02,998 --> 00:11:05,498 time in terms of my, my life choices. 204 00:11:05,928 --> 00:11:10,615 but as regards local-first and TinyBase, which is the project that I've been 205 00:11:10,615 --> 00:11:15,105 working on that I'm sure we'll, we'll talk about, that was somewhat Coincidental, 206 00:11:15,130 --> 00:11:18,150 that was something that I had been, you know, thinking about for a long time. 207 00:11:18,550 --> 00:11:23,310 but I think, yeah, then once you move on a boat, the, a number of the 208 00:11:23,310 --> 00:11:27,210 benefits of, of building local-first apps suddenly become acutely clear. 209 00:11:27,500 --> 00:11:30,290 It's like, wait, Oh, sometimes I really just don't have a cloud. 210 00:11:30,340 --> 00:11:33,660 And sometimes even if I do, my network is pretty bad. 211 00:11:34,150 --> 00:11:38,112 And it really throws into perspective the difference between the apps that 212 00:11:38,112 --> 00:11:44,245 I native apps, right, that do have much better local support and the web 213 00:11:44,285 --> 00:11:46,115 apps, which just now fail to load. 214 00:11:46,295 --> 00:11:50,555 And it really threw into stark relief the difference between them at the 215 00:11:50,555 --> 00:11:55,315 same time, as I said, I was working on TinyBase and maybe I can reverse the local 216 00:11:55,825 --> 00:11:59,960 local-first principle into into the origin story of TinyBase because it does seem 217 00:11:59,960 --> 00:12:01,450 to help support those kinds of things. 218 00:12:01,860 --> 00:12:05,420 and then like almost within a month, I then read the Ink and Switch article 219 00:12:05,420 --> 00:12:07,270 and I saw the essay about Rffle. 220 00:12:07,320 --> 00:12:12,540 I'm like, okay, all my, all these things are converging and , and 221 00:12:12,540 --> 00:12:13,680 it all suddenly made sense. 222 00:12:13,690 --> 00:12:16,980 So there was a lot of serendipity involved in kind of weaving 223 00:12:17,160 --> 00:12:18,670 these narratives together. 224 00:12:18,836 --> 00:12:22,958 But yeah, certainly life on a boat does, does make you think about 225 00:12:23,178 --> 00:12:26,676 local-first a lot more than living in some, you know, high density, 226 00:12:26,756 --> 00:12:28,896 high network connectivity lifestyle. 227 00:12:28,896 --> 00:12:33,316 So, yeah, no, it definitely helped inform my thinking about this and has encouraged 228 00:12:33,336 --> 00:12:36,576 me to, to keep working on, on TinyBase and other, other projects like it. 229 00:12:36,716 --> 00:12:38,916 So you mentioned keep working on TinyBase. 230 00:12:38,936 --> 00:12:43,581 So something must've led you to start working on TinyBase to begin 231 00:12:43,581 --> 00:12:48,241 with, and later you oriented more around local-first, what was the 232 00:12:48,361 --> 00:12:50,501 impetus to start working on TinyBase? 233 00:12:50,821 --> 00:12:54,801 So, well, maybe I should just like rewind a little bit in case 234 00:12:54,801 --> 00:12:55,911 it's not blindingly obvious. 235 00:12:56,071 --> 00:13:01,171 I love building stuff and all of my life, I guess, I've been, you 236 00:13:01,171 --> 00:13:02,371 know, surrounded by computers. 237 00:13:02,371 --> 00:13:06,621 I was programming when I was a kid and, you know, grew up always trying to 238 00:13:06,621 --> 00:13:12,961 create things with code and I guess the lifestyle of a manager or a director at 239 00:13:12,991 --> 00:13:17,271 Meta is that you tend to get a little further and further away from the code 240 00:13:17,271 --> 00:13:22,731 base, which I found sad because even though I love managing teams and people 241 00:13:22,731 --> 00:13:27,461 and helping grow teams, Organizations and so forth, like some part of me, 242 00:13:27,741 --> 00:13:31,071 like a primeval part of me still needs to be building something of my own. 243 00:13:31,521 --> 00:13:36,031 And so, that, that was really the origin of just doing something, anything. 244 00:13:36,031 --> 00:13:39,251 I just needed to be able to keep building even if it was 245 00:13:39,251 --> 00:13:40,801 just a small project for myself. 246 00:13:41,601 --> 00:13:44,821 And then obviously once I left the company and I started to find I had 247 00:13:44,821 --> 00:13:49,201 more time, I was able to start investing a little bit more effort into it. 248 00:13:49,636 --> 00:13:55,206 And yeah, the, the original kind of story for, for building TinyBase was that I 249 00:13:55,216 --> 00:13:59,376 had an idea in my mind of an app that would be really cool, but I knew that 250 00:13:59,426 --> 00:14:03,186 in order to build that app, I was going to need to have something like TinyBase. 251 00:14:03,266 --> 00:14:06,006 So, you know, without going into too much detail, you know, when we were 252 00:14:06,006 --> 00:14:11,046 at when I was at Facebook, I, I, I needed a way to manage the state of 253 00:14:11,096 --> 00:14:12,736 hundreds of open source projects, right? 254 00:14:12,736 --> 00:14:18,511 And so we built some internal tools that allowed us to, Pull from GitHub, you 255 00:14:18,511 --> 00:14:22,381 know, the state of all of the repos, how many pull requests, issues, and so forth. 256 00:14:22,451 --> 00:14:26,451 And um, That tooling really made it easy for us to look at this 257 00:14:26,451 --> 00:14:29,301 huge portfolio and figure out what was going well and what wasn't. 258 00:14:29,881 --> 00:14:33,641 But I felt like the world itself could benefit from something like 259 00:14:33,651 --> 00:14:36,891 that So I thought maybe maybe there's a there's an app out there somewhere 260 00:14:36,891 --> 00:14:40,661 which would be , a GitHub at scale kind of interface that would let you 261 00:14:40,661 --> 00:14:42,091 manage your open source portfolio. 262 00:14:42,861 --> 00:14:45,611 And so anyway, I thought, well, if I needed an app like that, and I'd need 263 00:14:45,621 --> 00:14:49,161 to put it in a browser, and I'd need to have some kind of way of storing 264 00:14:49,171 --> 00:14:53,941 the tables of repos and issues and pull requests, you know, how would I do that? 265 00:14:53,951 --> 00:14:57,291 Like, what is a good tabular data store that runs in the browser? 266 00:14:57,291 --> 00:15:02,276 And there isn't, well, back then anyway, 2021 2022, there wasn't 267 00:15:02,276 --> 00:15:04,406 really an obvious way to do that. 268 00:15:04,406 --> 00:15:07,826 And so I thought, well, rather than building the app, I'll start off by 269 00:15:07,826 --> 00:15:12,916 building a state management library that allows me to store tabular data that in 270 00:15:13,096 --> 00:15:16,546 the future will store this data that I need in this app that I haven't built yet. 271 00:15:16,696 --> 00:15:20,276 And yeah, that, that, that's where TinyBase originally came from. 272 00:15:20,276 --> 00:15:24,706 I knew it needed sort of database semantics, but didn't actually 273 00:15:24,706 --> 00:15:25,856 need to be a database, right? 274 00:15:25,856 --> 00:15:28,386 It could still be considered an in memory data store. 275 00:15:28,386 --> 00:15:31,446 and I'm sure everyone's familiar with the idea of a, you know, 276 00:15:31,576 --> 00:15:33,056 kind of a store in a React app. 277 00:15:33,686 --> 00:15:36,296 But I wanted it to kind of look and feel a little bit like a database. 278 00:15:36,296 --> 00:15:38,946 I wanted it to feel like it had tables and rows and cells. 279 00:15:39,446 --> 00:15:42,206 I wanted to do queries against it, and I wanted to be able to do aggregates 280 00:15:42,286 --> 00:15:45,206 of the data so I could count up the number of stars or commits in a 281 00:15:45,206 --> 00:15:46,506 repo and all those kinds of things. 282 00:15:47,023 --> 00:15:51,243 so yeah, that was the original, idea of TinyBase, you know, something 283 00:15:51,243 --> 00:15:55,973 that wasn't a database, but sort of had database conceptual semantics. 284 00:15:56,256 --> 00:15:57,636 and that's where it popped up. 285 00:15:57,966 --> 00:16:01,286 And then of course I stumbled into the Rffle And you 286 00:16:01,286 --> 00:16:02,846 know, Ink and Switch material. 287 00:16:03,186 --> 00:16:06,076 And I obviously spotted that you were doing something similar with, 288 00:16:06,456 --> 00:16:09,156 with a SQLite approach, which is obviously literally a database. 289 00:16:09,416 --> 00:16:11,806 So you literally get the database semantics. 290 00:16:12,026 --> 00:16:14,516 but yeah, by that point I'd kind of committed myself to 291 00:16:14,556 --> 00:16:16,536 doing an in memory approach. 292 00:16:17,416 --> 00:16:20,036 So I thought, well, let's, let's try both. 293 00:16:20,436 --> 00:16:23,146 , and I, and so I kept pushing, pushing forward with TinyBase. 294 00:16:23,736 --> 00:16:25,676 Now the story has obviously evolved a lot since then. 295 00:16:25,676 --> 00:16:29,516 TinyBase is now a lot better about integrating with real databases. 296 00:16:29,516 --> 00:16:30,656 We can come on to talk about that. 297 00:16:30,966 --> 00:16:33,306 Um, but yeah, that was, that was the origin story. 298 00:16:33,306 --> 00:16:34,976 It was just something I needed for myself. 299 00:16:35,146 --> 00:16:37,386 I thought the world needed, and I went and built it. 300 00:16:37,456 --> 00:16:40,786 And then once I found I had more time, I guess I kept building. 301 00:16:41,026 --> 00:16:41,776 that is amazing. 302 00:16:41,816 --> 00:16:46,556 I think there's a lot of similarities in terms of your motivations, et cetera. 303 00:16:46,796 --> 00:16:50,946 Maybe a few different technical decisions along the way, but for me, 304 00:16:51,090 --> 00:16:56,280 starting to work on Overtone would have not been feasible without having 305 00:16:56,340 --> 00:17:01,425 a very substantial State, local state management foundation to build on top of. 306 00:17:01,695 --> 00:17:05,365 And this is how I then like got to know Jeffrey, et cetera, and 307 00:17:05,365 --> 00:17:06,915 started getting involved with Rffle. 308 00:17:07,265 --> 00:17:12,715 So it seems like there is a clear parallel to you wanting to build your own app 309 00:17:12,785 --> 00:17:17,540 and realizing, okay, that's going to be terrible working on that app without 310 00:17:17,550 --> 00:17:19,280 those primitives that you have in mind. 311 00:17:19,480 --> 00:17:22,970 So obviously you got to start building your, your own state 312 00:17:22,970 --> 00:17:24,320 management system first. 313 00:17:24,730 --> 00:17:26,450 so, and kudos to you. 314 00:17:26,500 --> 00:17:31,680 I think TinyBase is one of the most polished and, sophisticated solutions 315 00:17:31,690 --> 00:17:37,650 that is already out there where I think a lot of technologies are, maybe have 316 00:17:37,650 --> 00:17:39,720 really great ideas, but are still. 317 00:17:40,030 --> 00:17:43,820 pretty early in terms of being approachable and having the 318 00:17:43,820 --> 00:17:45,460 supporting resources around it. 319 00:17:45,780 --> 00:17:48,560 So, for example, LiveStore is not yet open source. 320 00:17:48,570 --> 00:17:52,480 It's available to GitHub sponsors, but it doesn't have documentation yet. 321 00:17:52,480 --> 00:17:53,640 It doesn't have guides yet. 322 00:17:53,650 --> 00:17:56,966 It doesn't have, like, all of those, supporting material around it. 323 00:17:57,026 --> 00:18:00,696 Whereas TinyBase has a gorgeous website. 324 00:18:00,991 --> 00:18:04,801 Really polished docs, lots of examples, lots of guides. 325 00:18:05,071 --> 00:18:09,541 you keep, releasing regularly, like new versions, integrations 326 00:18:09,541 --> 00:18:11,171 with different other technologies. 327 00:18:11,481 --> 00:18:14,291 So really, really exciting how far you've come. 328 00:18:14,571 --> 00:18:18,461 I'm now looking forward to trying to understand better the different details. 329 00:18:18,641 --> 00:18:19,031 Right. 330 00:18:19,091 --> 00:18:22,001 Well, just firstly, a bit, a little bit of a meta point on 331 00:18:22,001 --> 00:18:23,431 the, on the kind of the polish. 332 00:18:23,531 --> 00:18:28,451 so a side kind of motivation for this was to see what it was like 333 00:18:28,451 --> 00:18:32,921 to build software where I didn't have to make any major compromises. 334 00:18:33,356 --> 00:18:36,886 and I'm sure you and, and, and the people listening or watching, you 335 00:18:36,886 --> 00:18:40,966 know, have, have written software where you're under a deadline or there's 336 00:18:40,966 --> 00:18:44,906 some set of constraints and you just start cutting corners, you know, ah, 337 00:18:44,906 --> 00:18:47,546 you know, this bit of code is really crappy or, well, we didn't have time 338 00:18:47,546 --> 00:18:50,496 to finish the test coverage or, no, just, we've got to ship it, right? 339 00:18:50,726 --> 00:18:52,616 Cause that's, and actually that's fine. 340 00:18:52,616 --> 00:18:52,846 Right. 341 00:18:52,846 --> 00:18:54,846 I mean that, that, that's life. 342 00:18:56,046 --> 00:18:57,266 I always feel like some. 343 00:18:57,711 --> 00:19:02,031 Sort of perfectionist part of my brain is always like, ah, like next time, 344 00:19:02,081 --> 00:19:03,811 next time, I'm going to make it perfect. 345 00:19:03,831 --> 00:19:06,841 Next time, you know, the architecture is going to be perfect. 346 00:19:06,861 --> 00:19:08,951 Next time the test coverage is going to be a hundred percent. 347 00:19:09,521 --> 00:19:13,211 And so given that I now had this project that only I was working on, 348 00:19:13,501 --> 00:19:15,101 I was really only my only customer. 349 00:19:15,561 --> 00:19:16,911 I had a bunch of spare time. 350 00:19:17,521 --> 00:19:17,961 I figured. 351 00:19:18,616 --> 00:19:20,976 , if I'm ever going to have a chance to build, quote, what I thought was 352 00:19:21,016 --> 00:19:23,566 perfect software, and of course that's subjective, it's just my view that it's 353 00:19:23,606 --> 00:19:25,546 perfect then this was going to be it. 354 00:19:25,606 --> 00:19:30,236 So, can I build a piece of software that has, you know, 100 percent test coverage, 355 00:19:30,476 --> 00:19:36,671 is refactored, like, so iteratively that eventually it can't be refactored anymore. 356 00:19:36,671 --> 00:19:39,491 , it's got all the documentation you could ever need. 357 00:19:39,491 --> 00:19:43,521 It's got the best website that me and my crappy web design can manage. 358 00:19:43,781 --> 00:19:47,021 Um, and I, you know, I don't have a deadline, right? 359 00:19:47,081 --> 00:19:48,571 I don't have somebody breathing down my neck. 360 00:19:48,571 --> 00:19:51,331 I don't have some investors I got to keep happy. 361 00:19:51,331 --> 00:19:53,981 It's like, no, it's like, I'm going to polish this thing. 362 00:19:54,041 --> 00:19:59,301 It's, and it's a beautiful, feeling of kind of craftsmanship, which I have 363 00:19:59,301 --> 00:20:01,031 rarely had before in my professional life. 364 00:20:01,071 --> 00:20:03,031 You know, often you feel like you're a tradesman. 365 00:20:03,321 --> 00:20:06,101 You're just like knocking stuff out for a customer or for a deadline. 366 00:20:06,481 --> 00:20:09,501 But suddenly I could feel like, sounds pretentious, but 367 00:20:09,511 --> 00:20:10,411 almost like an artist, right? 368 00:20:10,861 --> 00:20:14,281 You know, artisanal software that's like perfectly polished 369 00:20:14,301 --> 00:20:16,181 to my, to my specification. 370 00:20:16,471 --> 00:20:18,611 And that was an interesting journey in its own right. 371 00:20:18,661 --> 00:20:22,556 By the way, it's also a curse because you could just end up polishing and 372 00:20:22,556 --> 00:20:25,503 polishing and polishing and then forgetting to actually share it with 373 00:20:25,503 --> 00:20:27,913 you, share it with the rest of the world. 374 00:20:27,913 --> 00:20:30,591 So, you know, there are downsides to this as well. 375 00:20:30,591 --> 00:20:35,611 But no, I always wanted to invest in test coverage, and documentation. 376 00:20:35,861 --> 00:20:39,821 And by the way, and this is an interesting side story we may not have time for, 377 00:20:39,821 --> 00:20:44,431 but like, as a result, you build lots of tools to help you do that too, right? 378 00:20:44,431 --> 00:20:48,231 So I've now got tools that help me generate the documentation, tools 379 00:20:48,241 --> 00:20:50,151 that help me generate test cases. 380 00:20:50,571 --> 00:20:53,951 The documentation, as you identified, has lots of examples and sample code. 381 00:20:54,236 --> 00:20:57,236 Each piece of sample code is also a unit test, right? 382 00:20:57,236 --> 00:20:59,406 It'll actually take the code out of the documentation and run 383 00:20:59,406 --> 00:21:02,736 it which is obviously stuff that I had to go and build myself. 384 00:21:02,996 --> 00:21:06,556 but that was all me, you know, thinking, well, I don't want to, I 385 00:21:06,556 --> 00:21:09,256 don't want a piece of code in the documentation that doesn't actually run. 386 00:21:09,626 --> 00:21:11,896 Like, you know, what happens if I refactor the code and I 387 00:21:11,896 --> 00:21:12,856 forget that it was in the docs? 388 00:21:13,360 --> 00:21:15,200 I know exactly what you're talking about. 389 00:21:15,420 --> 00:21:17,320 There's a little thing that comes to mind. 390 00:21:17,320 --> 00:21:21,550 I'm not sure where I've seen it, but I think it was along the lines of 391 00:21:21,570 --> 00:21:27,200 like a yuck yuck tree, like the, the concept of like yuck shaving and 392 00:21:27,200 --> 00:21:31,350 like a tree of dependencies or like how you would try to do one thing. 393 00:21:31,710 --> 00:21:36,120 And to do that, you had to like diverge into a different side 394 00:21:36,120 --> 00:21:40,370 project and then your side project has a side project and you see like 395 00:21:40,370 --> 00:21:42,750 a dependency tree of Yuck Shaving. 396 00:21:43,100 --> 00:21:45,220 So I know exactly what you're talking about. 397 00:21:45,370 --> 00:21:45,940 That's right. 398 00:21:46,300 --> 00:21:47,800 That's right. 399 00:21:47,800 --> 00:21:50,390 And each one of those tools for the tools for the tools, each one 400 00:21:50,390 --> 00:21:51,710 of them has to be perfect as well. 401 00:21:51,710 --> 00:21:51,920 Right? 402 00:21:51,920 --> 00:21:53,780 So, anyway, it's probably a fractal thing. 403 00:21:54,200 --> 00:21:57,920 , but yeah, that, that, that itself has been a really interesting journey trying 404 00:21:57,920 --> 00:22:02,750 to build the, the software that I really always yearned to be able to, 405 00:22:03,200 --> 00:22:07,070 to build even as it scales, which is, you know, tricky to be honest. 406 00:22:07,970 --> 00:22:11,180 And obviously once people start using it too, because then now you've got 407 00:22:11,230 --> 00:22:15,540 opinions coming from people outside saying, Oh, well, you know, you 408 00:22:15,540 --> 00:22:18,130 might think it's perfect that you do this, but we don't like it like that. 409 00:22:18,490 --> 00:22:20,590 You're like, ah, now I've got this dilemma. 410 00:22:20,690 --> 00:22:24,120 Like, am I going to break my perfect thing to do the thing 411 00:22:24,120 --> 00:22:25,240 that actually a customer wants? 412 00:22:25,540 --> 00:22:28,010 Well, of course, you know, because if no one's using it, 413 00:22:28,010 --> 00:22:29,670 then then what was the point? 414 00:22:29,680 --> 00:22:33,130 So there is a a whole set of interesting trade offs that 415 00:22:33,140 --> 00:22:34,970 you then start, start making. 416 00:22:34,970 --> 00:22:38,220 But no, just again, true to its name, I would say the most interesting 417 00:22:38,220 --> 00:22:43,020 part of that is how did, how do you make it as small as possible? 418 00:22:43,246 --> 00:22:46,836 and I, maybe that's not so important these days in the world of, faster internet 419 00:22:46,836 --> 00:22:48,856 connections, et cetera, and good bundlers. 420 00:22:48,906 --> 00:22:53,786 But like, I wanted to see, like, could I make this like as almost, you know, 421 00:22:53,796 --> 00:22:55,256 infinitely as small as I could make it? 422 00:22:55,283 --> 00:22:57,473 and so I spent a lot of time thinking about minification, 423 00:22:57,473 --> 00:22:58,903 a lot of time thinking about. 424 00:22:59,533 --> 00:23:03,093 you know, how to refactor the code in a way that, you know, made it compress 425 00:23:03,093 --> 00:23:05,723 really small and, you know, it's still, it's still pretty small for what it does. 426 00:23:05,893 --> 00:23:06,673 so I'm pretty pleased with it. 427 00:23:06,876 --> 00:23:07,696 That is awesome. 428 00:23:07,766 --> 00:23:11,796 I highly encourage everyone who's watching this to also check out 429 00:23:11,796 --> 00:23:16,356 your beautiful website and the documentation, the examples, et cetera. 430 00:23:16,406 --> 00:23:18,646 I think you've laid it out very clearly. 431 00:23:18,976 --> 00:23:23,666 but given this is a podcast where most people listen to, would you 432 00:23:23,676 --> 00:23:29,386 mind in words, trying to describe what TinyBase is, like, for example, 433 00:23:29,386 --> 00:23:31,236 in the context of a React app. 434 00:23:31,516 --> 00:23:31,926 Okay. 435 00:23:32,226 --> 00:23:34,376 Well, firstly, TinyBase. 436 00:23:34,406 --> 00:23:38,376 org will do a better job of explaining it than I will do now, 437 00:23:38,436 --> 00:23:40,136 so definitely go, go check that out. 438 00:23:40,166 --> 00:23:41,576 , it's very nice that you say it's beautiful. 439 00:23:41,886 --> 00:23:45,276 I'm no graphic designer and I actually think the website 440 00:23:45,276 --> 00:23:46,286 could do with a bit of love. 441 00:23:46,286 --> 00:23:49,406 So if anybody would like to come along and make it look a little nicer, 442 00:23:49,756 --> 00:23:51,216 they are very welcome to file some. 443 00:23:51,796 --> 00:23:54,696 CSS pull requests on me, I would be very happy to take those. 444 00:23:54,876 --> 00:23:59,466 But, that all said yeah, look, the basic idea or the strap line 445 00:23:59,716 --> 00:24:05,146 is that TinyBase is a reactive data store for local-first apps. 446 00:24:05,376 --> 00:24:08,656 And I'm very careful to say data store, not database. 447 00:24:08,696 --> 00:24:11,286 I don't want anyone thinking that it literally is a database. 448 00:24:11,606 --> 00:24:15,566 it's a data store, but it just has characteristics that are perhaps 449 00:24:15,566 --> 00:24:19,136 familiar to people who have thought about relational databases in the past. 450 00:24:19,202 --> 00:24:24,131 I also say that it's for local-first apps because I'm riding on the back 451 00:24:24,131 --> 00:24:27,661 of the cool local-first movement that's happening right now. 452 00:24:27,881 --> 00:24:31,381 I guess there's no obligation to only use it on local-first apps. 453 00:24:31,411 --> 00:24:35,841 I think, you know, even a permanently online app that wants some tabular 454 00:24:35,871 --> 00:24:41,121 data store in the browser would still be able to use TinyBase. 455 00:24:41,121 --> 00:24:42,891 But yeah, that's the principle. 456 00:24:43,051 --> 00:24:44,051 It's in memory. 457 00:24:44,721 --> 00:24:49,986 So the data is either loaded up from some local you know, to the browser, 458 00:24:50,236 --> 00:24:55,366 some local storage, or session storage, or IndexedDB, and then 459 00:24:55,366 --> 00:24:59,246 it's loaded into memory, and then you build your app on top of it in 460 00:24:59,246 --> 00:25:04,656 a way that is fully reactive to the changes that happen to that data store. 461 00:25:04,706 --> 00:25:11,966 So, in a way, you have delegated all of the data provisioning, loading, 462 00:25:11,976 --> 00:25:16,476 synchronization, whatever, to TinyBase, and you just build your app as a pure 463 00:25:16,476 --> 00:25:20,596 function of the TinyBase contents, and I will take care of getting all that 464 00:25:20,596 --> 00:25:22,056 data updated in the background for you. 465 00:25:22,206 --> 00:25:27,406 Whether I'm pulling it from the web, or I'm synchronizing it with CRDTs, or I'm 466 00:25:27,406 --> 00:25:32,776 pulling it out of a local store, or I'm firing it over PartyKit WebSockets to your 467 00:25:32,826 --> 00:25:35,186 friends you know, I'll do all of that. 468 00:25:35,316 --> 00:25:41,156 All you have to do is build a UI that hooks into tables or rows or cells or 469 00:25:41,156 --> 00:25:43,686 key value pairs in, in the TinyBase data. 470 00:25:43,956 --> 00:25:47,666 and your app will always be reacting to, to those changes. 471 00:25:48,366 --> 00:25:49,706 So that's the basic principle. 472 00:25:49,926 --> 00:25:51,586 It doesn't have to use React, incidentally. 473 00:25:51,616 --> 00:25:54,686 Like, there's nothing to stop you just, you know, listening to changes 474 00:25:54,746 --> 00:25:57,616 in the data directly, and then just, I don't know, document dot 475 00:25:57,616 --> 00:25:58,966 writing out stuff if you want to. 476 00:25:59,386 --> 00:26:02,156 But React does work well because, obviously, that also has a 477 00:26:02,156 --> 00:26:03,326 sort of reactive principle. 478 00:26:03,676 --> 00:26:08,856 And so you can use hooks to attach your components to any of the 479 00:26:09,731 --> 00:26:11,911 elements of the, the, the store. 480 00:26:11,931 --> 00:26:13,811 And by the way, you can do that at any granularity. 481 00:26:13,841 --> 00:26:17,031 So you could have a component that was just watching one cell of one row of one 482 00:26:17,041 --> 00:26:21,241 table, or you could have a component that was watching a whole set of tables, or 483 00:26:21,271 --> 00:26:24,191 one set of key value pairs, what have you. 484 00:26:24,481 --> 00:26:25,631 Lots of different ways to listen. 485 00:26:26,021 --> 00:26:31,636 And you know, TinyBase will make the, or do the job of, you know, alerting you 486 00:26:31,636 --> 00:26:35,046 when just that thing that you've looked, you're looking at has, has changed. 487 00:26:35,466 --> 00:26:38,776 And the same then goes for queries against the database, by the way. 488 00:26:38,776 --> 00:26:42,126 So if you want to do something more interesting than just watching raw data, 489 00:26:42,126 --> 00:26:46,956 you also want to look at aggregates or you know, relationships between 490 00:26:46,956 --> 00:26:48,061 data, or in fact, just queries. 491 00:26:48,611 --> 00:26:50,011 construct arbitrary queries. 492 00:26:50,271 --> 00:26:53,581 You can then attach your app to listen to the results of those. 493 00:26:53,871 --> 00:26:56,811 And again, I'll take care of all the reactivity behind the scenes 494 00:26:57,051 --> 00:27:01,231 and your app will update when, when when those results do. 495 00:27:01,361 --> 00:27:05,081 So I guess when I explain it like that, it sounds like TinyBase is doing quite a lot. 496 00:27:05,471 --> 00:27:09,191 and that's probably why, you know, we're two years in and it's taken me 497 00:27:09,311 --> 00:27:10,581 quite a long time to get to this point. 498 00:27:10,881 --> 00:27:12,621 But yeah, that's the basic principle. 499 00:27:12,981 --> 00:27:16,868 And I would say, I think this is kind of an interesting approach 500 00:27:17,118 --> 00:27:22,213 to apps, whereby You know, how the data gets into the memory of the app 501 00:27:22,313 --> 00:27:24,563 is kind of, like, delegated away. 502 00:27:24,873 --> 00:27:29,493 And you, as an app builder, now don't need to worry about things like 503 00:27:29,503 --> 00:27:33,623 going on or offline, or things like synchronizing, or things like storing. 504 00:27:33,683 --> 00:27:37,523 Like, you should expect your data storage layer to kind of handle 505 00:27:37,523 --> 00:27:42,883 that for you, and you are then just painting your app out of the palette 506 00:27:42,893 --> 00:27:45,553 of data that is provided to you. 507 00:27:46,143 --> 00:27:49,613 because As I'm sure you know, and I'm sure many of the listeners know, 508 00:27:50,313 --> 00:27:54,313 the acts of synchronization and storage and dealing with all these 509 00:27:54,313 --> 00:27:56,073 network conditions is just horrible. 510 00:27:56,433 --> 00:28:00,623 And rather a small number of people deal with those problems than have 511 00:28:00,683 --> 00:28:03,943 everybody trying to build a local-first app having to re solve those problems 512 00:28:03,973 --> 00:28:06,163 is probably the long way to go. 513 00:28:06,613 --> 00:28:09,483 and so I, I, I guess that's what I'm hoping happens. 514 00:28:09,523 --> 00:28:11,923 And it's not just TinyBase, of course, there are lots of other people in this 515 00:28:11,923 --> 00:28:14,873 space, yourself included, but, you know, many other vendors at this point 516 00:28:14,873 --> 00:28:16,393 who are trying to crack these problems. 517 00:28:16,733 --> 00:28:19,823 and I think it's because it's hard and we know that we have to get 518 00:28:19,873 --> 00:28:25,053 this layer of infrastructure going before the, oh the local-first 519 00:28:26,013 --> 00:28:27,903 ecosystem, you know, really takes off. 520 00:28:28,523 --> 00:28:32,003 I suspect that at the moment we may have more vendors building 521 00:28:32,003 --> 00:28:34,123 solutions than we actually have apps. 522 00:28:34,203 --> 00:28:34,613 That's. 523 00:28:35,143 --> 00:28:38,923 Bit of an offhand assessment there, but, you know, there are a lot of 524 00:28:38,923 --> 00:28:40,163 people trying to solve these problems. 525 00:28:40,503 --> 00:28:42,323 And I think it's because it's genuinely hard. 526 00:28:42,593 --> 00:28:45,003 And what I really hope is that we can make it easy enough 527 00:28:45,123 --> 00:28:49,073 collectively that that people can build these apps much more simply. 528 00:28:49,233 --> 00:28:52,063 And you get the benefits of local-first without having to have a degree in 529 00:28:52,063 --> 00:28:53,953 computer science for synchronization. 530 00:28:54,523 --> 00:28:55,473 That's what we're all trying to get. 531 00:28:56,307 --> 00:28:56,677 Totally. 532 00:28:56,697 --> 00:29:01,357 And, and I think we're getting really close to, to that where I think right 533 00:29:01,397 --> 00:29:07,187 now the de facto, solution for state management on the client might rather look 534 00:29:07,187 --> 00:29:12,283 something like MobX or some people might still like use Redux or Redux toolkit, 535 00:29:12,283 --> 00:29:14,823 et cetera, or, or like React query. 536 00:29:15,143 --> 00:29:20,913 and I think it's hard enough to build an app that works in the right way. 537 00:29:20,923 --> 00:29:25,463 And most developers don't, dare yet to think about persistence 538 00:29:25,463 --> 00:29:28,997 and working offline, trying to make it work, just like that. 539 00:29:29,367 --> 00:29:35,477 And I think a technology like TinyBase can really, raise the, the floor quite a 540 00:29:35,477 --> 00:29:39,237 lot in terms of what a state management solution can give you out of the box. 541 00:29:39,497 --> 00:29:44,917 It gives you all of the things something like MobX does, but also on top of it 542 00:29:44,947 --> 00:29:49,337 gives you persistence, gives you offline functionality, gives you syncing. 543 00:29:49,747 --> 00:29:52,827 So I think we just, yeah, raised the floor. 544 00:29:53,147 --> 00:29:57,187 So in terms of the different responsibilities, I'm curious to hear 545 00:29:57,187 --> 00:30:01,582 a little bit more, and I think what stands out about TinyBase here is is 546 00:30:01,592 --> 00:30:05,552 that you really went above and beyond to make things flexible and pluggable. 547 00:30:05,872 --> 00:30:11,682 So for the persistence path, I think you do allow a whole different range of 548 00:30:11,712 --> 00:30:15,872 different options to persist, whether it's IndexedDB or, or other options 549 00:30:15,882 --> 00:30:17,522 as well as for, for the syncing. 550 00:30:17,792 --> 00:30:20,942 So would you mind sharing a little bit more about the details and 551 00:30:21,182 --> 00:30:22,472 the thoughts you have around that? 552 00:30:22,968 --> 00:30:23,758 Yeah, certainly. 553 00:30:23,758 --> 00:30:29,933 So in the very first instance, I was just jSON serializing the content 554 00:30:29,933 --> 00:30:34,553 of the store and writing it to local storage, or to session storage, or to 555 00:30:34,553 --> 00:30:38,083 file, because you can obviously run TinyBase on a node server as well. 556 00:30:38,483 --> 00:30:45,078 And, that's great, and in 2022 that seemed like the Basically, you know, 557 00:30:45,418 --> 00:30:49,768 when you're thinking about small stores anyway that seems like a fine array of 558 00:30:49,768 --> 00:30:53,538 ways to do it, but Obviously people start to push the boundaries of what you can 559 00:30:53,548 --> 00:30:56,688 store in those media and just serialize JSON isn't necessarily the way to go 560 00:30:56,908 --> 00:31:00,778 So I think IndexedDB was probably the next one that I tackled was not as easy. 561 00:31:00,778 --> 00:31:05,758 , just Just as a small side note, IndexedDB is not the most 562 00:31:05,758 --> 00:31:08,018 enjoyable API to work with. 563 00:31:08,428 --> 00:31:12,488 Most notably, it lacks any form of reactivity whatsoever that I know of. 564 00:31:12,548 --> 00:31:14,748 I think there were some proposals that never went anywhere. 565 00:31:15,408 --> 00:31:16,588 But there's no observability. 566 00:31:17,038 --> 00:31:22,028 So it's kind of Crappy to have to poll for changes, but , yeah, 567 00:31:22,028 --> 00:31:24,128 I do now have that as well. 568 00:31:24,128 --> 00:31:29,258 So you can either save your in memory store to IndexedDB or load 569 00:31:29,258 --> 00:31:33,558 it or automatically save to it when changes happen or automatically load 570 00:31:33,558 --> 00:31:34,908 from it with a polling mechanism. 571 00:31:35,628 --> 00:31:40,238 so that, that, that, that's there as well and and then around the same 572 00:31:40,268 --> 00:31:43,878 time, in fact, it was Matt Wonlow that I know it was on your show a 573 00:31:43,878 --> 00:31:50,198 little while back he was working on his CR SQLite solution and I started to see 574 00:31:50,198 --> 00:31:53,108 that SQLite was starting to emerge. 575 00:31:53,468 --> 00:31:56,688 as as a browser opportunity. 576 00:31:57,088 --> 00:32:00,878 And so I figured, well, you know, there'll be people who want to store it in the 577 00:32:00,878 --> 00:32:03,368 browser on, on a SQLite database as well. 578 00:32:03,698 --> 00:32:06,198 So I went down the path of supporting that. 579 00:32:06,208 --> 00:32:10,168 So the tables that were in your memory store in TinyBase can 580 00:32:10,168 --> 00:32:14,438 now be literally written out to tables in a SQLite instance. 581 00:32:14,678 --> 00:32:18,698 Or read in, and again, reactivity is not perfect. 582 00:32:19,418 --> 00:32:23,768 , some APIs are better than others, but you know, do what I can to, to 583 00:32:23,768 --> 00:32:25,778 keep that as synchronized as possible. 584 00:32:26,598 --> 00:32:31,308 And that then opened the door to support things like PowerSync 585 00:32:31,878 --> 00:32:36,858 and, uh Turso and ElectricSQL. 586 00:32:37,523 --> 00:32:37,763 , I also did. 587 00:32:38,090 --> 00:32:41,660 PartyKit along the way, which is not a SQLite store, but it's a 588 00:32:42,220 --> 00:32:46,690 kind of a WebSocket based way of storing data up in a durable object, 589 00:32:46,760 --> 00:32:48,540 and so it supported that also. 590 00:32:49,070 --> 00:32:52,610 So yeah, there was a period, probably over the last year or so, that the 591 00:32:52,610 --> 00:32:56,620 version 4 series of TinyBase, if you like, where each major release was me adding 592 00:32:56,780 --> 00:33:02,160 support for one of these new storage technologies or synchronization platforms. 593 00:33:02,530 --> 00:33:08,560 The other two that are very worthy of note are AutoMerge and YJS, so those 594 00:33:08,600 --> 00:33:13,605 are more classic CRDT technologies, but you can store TinyBase into a 595 00:33:13,605 --> 00:33:17,805 Yjs document, or load it from a Yjs document, or into an automerge document. 596 00:33:18,185 --> 00:33:20,345 And then, of course, you've got the benefit of those two 597 00:33:20,345 --> 00:33:23,865 platforms synchronizing in whatever way they want to. 598 00:33:23,885 --> 00:33:28,145 So, yeah, I guess you could say I, am providing a lot of options. 599 00:33:28,205 --> 00:33:28,995 Maybe too many. 600 00:33:29,105 --> 00:33:31,568 I, Suspect that when you go to the website and you see all these 601 00:33:31,568 --> 00:33:34,628 baffling options, you're like, Oh yeah, but which do I actually choose? 602 00:33:34,968 --> 00:33:38,508 so what I'm hoping to do actually fairly soon is write a guide for, you know, 603 00:33:38,518 --> 00:33:39,948 this is the kind of app I'm building. 604 00:33:39,948 --> 00:33:42,968 This is the kind of platform I should use, or this is how I should synchronize. 605 00:33:43,238 --> 00:33:46,668 Do I do the synchronization myself or do I rely on some third party to do it? 606 00:33:47,318 --> 00:33:51,558 And at the same time, obviously we've seen companies like PowerSync and 607 00:33:51,558 --> 00:33:54,698 ElectricSQL, you know coming forward with their, their solutions here. 608 00:33:54,698 --> 00:33:58,128 And so, yeah, if people are using TinyBase and they want to Provide 609 00:33:58,128 --> 00:34:01,473 a a gateway to those systems, then that's that option as well. 610 00:34:01,693 --> 00:34:04,433 So yeah, I have tried to be as flexible as possible. 611 00:34:04,692 --> 00:34:07,392 One of the privileges, I guess, of working on this as a hobby is that, 612 00:34:07,522 --> 00:34:12,122 you know, I can be open to working with as many of these partners as possible, 613 00:34:12,122 --> 00:34:15,242 whether they're commercial startups or just open source projects, right? 614 00:34:15,242 --> 00:34:17,352 I'm not in competition with anybody. 615 00:34:18,132 --> 00:34:21,402 And so if I'm providing a way for developers to onboard onto these 616 00:34:21,402 --> 00:34:22,832 different things, then that's great. 617 00:34:22,922 --> 00:34:26,152 I definitely have a view that the, the tide is rising up. 618 00:34:26,372 --> 00:34:30,482 I'm saying that on a boat but the local tide is rising, local-first tide is 619 00:34:30,482 --> 00:34:34,822 rising, and I'm happy to see all boats go with it even in the long term. 620 00:34:35,462 --> 00:34:40,247 , even if in the long term TinyBase becomes not needed because, you 621 00:34:40,247 --> 00:34:43,517 know, all of these other platforms have, have matured to the point where 622 00:34:43,517 --> 00:34:44,897 they're offering what I already do. 623 00:34:44,897 --> 00:34:49,497 But I think in the meantime, I'm hopefully providing a nice, disambiguation 624 00:34:49,497 --> 00:34:53,797 layer or you know, something like that to help people decide what their 625 00:34:53,797 --> 00:34:57,052 approach is going to be without having to rewrite their app dramatically. 626 00:34:57,072 --> 00:35:00,102 So yeah, today you could store your TinyBase data to local 627 00:35:00,102 --> 00:35:03,312 storage and tomorrow you could then sync it to ElectricSQL. 628 00:35:03,912 --> 00:35:04,602 Why not, right? 629 00:35:04,985 --> 00:35:11,885 So in terms of the data that is persisted, let's say to IndexedDB or OPFS, SQLite, 630 00:35:11,885 --> 00:35:18,075 or synced to one of the syncing providers, how do you bring the data into memory? 631 00:35:18,135 --> 00:35:22,085 And do you typically, let's say I have a, let's say I'm using 632 00:35:22,085 --> 00:35:26,945 Turso and have my SQLite database somewhere, Is TinyBase automatically, 633 00:35:27,175 --> 00:35:32,195 hydrating all of that data from the database into the in memory version? 634 00:35:32,245 --> 00:35:36,835 Or is there some sort of scoped version where maybe the database is two 635 00:35:36,835 --> 00:35:41,945 gigabytes and you only can constrain yourself to 200 megabytes in memory? 636 00:35:42,222 --> 00:35:43,282 how are you handling that? 637 00:35:43,702 --> 00:35:44,192 Right. 638 00:35:44,292 --> 00:35:48,673 So for all of the database based persisters, I call them, there's a 639 00:35:48,733 --> 00:35:52,293 configuration where you say which tables you want and, you know, 640 00:35:52,293 --> 00:35:55,283 whether you want to just load from those tables or whether you also 641 00:35:55,283 --> 00:35:56,723 want to be able to save back to them. 642 00:35:57,023 --> 00:36:00,673 the worst thing I could think of would be somebody with some large production 643 00:36:00,683 --> 00:36:03,783 database and they connect TinyBase to it and the next thing you know, it's 644 00:36:03,783 --> 00:36:08,073 tried to load the entire database into memory or it's tried to write No, back 645 00:36:08,088 --> 00:36:10,488 to the whole database, and then, you know, that's the end of the world. 646 00:36:10,488 --> 00:36:14,738 So no, it's all very configurable on a table by table basis. 647 00:36:15,048 --> 00:36:18,378 And the one thing I have not done, but I'm pretty sure I need to 648 00:36:18,378 --> 00:36:22,698 do, is also provide some kind of pagination or filtering on that view. 649 00:36:23,008 --> 00:36:27,693 because if you've got a database that has, let's say you, you, you shard 650 00:36:27,693 --> 00:36:31,663 it on user or something, and you only want to be able to see just the data 651 00:36:31,663 --> 00:36:34,723 from that user, which, you know, it's gonna be a pretty common use case, 652 00:36:34,933 --> 00:36:38,543 then make sure that persistence is not for the whole table, but just 653 00:36:38,543 --> 00:36:41,553 for the relevant rows, of that table. 654 00:36:41,783 --> 00:36:43,223 I think it's going to be harder than I think it is. 655 00:36:43,947 --> 00:36:46,177 I suspect there are lots of gotchas to doing that. 656 00:36:46,497 --> 00:36:51,337 , and so for now, there's my, my solution or my suggestion will be that people 657 00:36:51,337 --> 00:36:56,077 doing this should probably have a per user database, which is kind of an 658 00:36:56,077 --> 00:36:58,977 interesting approach that I know some people have started taking anyway, 659 00:36:59,337 --> 00:37:01,707 for, for edge based, databases. 660 00:37:02,097 --> 00:37:06,167 Um, so that, you know, you're, you're in no danger of, accidentally 661 00:37:06,167 --> 00:37:09,377 loading data from a different user into someone's browser. 662 00:37:09,937 --> 00:37:12,197 But no, that's definitely something I need to tackle. 663 00:37:12,227 --> 00:37:16,117 So it's scoped, to answer your question, it's scoped to individual tables as 664 00:37:16,117 --> 00:37:17,987 to whether it's load or save or both. 665 00:37:18,717 --> 00:37:21,107 But I think over time we need to add more pagination. 666 00:37:21,387 --> 00:37:23,837 So that you are just looking at, you know, the top 10 records, 667 00:37:23,837 --> 00:37:25,067 100 records, or what have you. 668 00:37:25,327 --> 00:37:29,237 The reactivity gets really tricky at that point because I'm sure you 669 00:37:29,287 --> 00:37:32,647 know, you know, SQLite's reactivity out of the box is pretty weak. 670 00:37:32,907 --> 00:37:34,787 At best, you're going to know that a table changed. 671 00:37:35,077 --> 00:37:36,707 And what are you going to do? 672 00:37:36,907 --> 00:37:39,307 Query the whole table to find out which row it was that changed. 673 00:37:40,250 --> 00:37:45,580 And so knowing that it was row 47 out of 2 billion that was the one that changed. 674 00:37:45,765 --> 00:37:49,595 is not currently something that the database platforms provide by default. 675 00:37:49,735 --> 00:37:54,125 Um, So, that's one thing where I am really having to work around 676 00:37:54,125 --> 00:37:55,665 those limitations right now. 677 00:37:56,045 --> 00:37:59,448 , and I'm hoping, if I can't make it clearer to you, I'm hoping that people 678 00:37:59,448 --> 00:38:03,908 working on SQLite are going to crack this and provide, you know technologies 679 00:38:03,918 --> 00:38:06,658 that make it easier to synchronize at a much more granular level, or get 680 00:38:06,658 --> 00:38:10,108 reactivity at a much more granular level, so that TinyBase isn't having to poll 681 00:38:10,118 --> 00:38:12,652 everything in a way that doesn't scale. 682 00:38:13,248 --> 00:38:21,758 Yeah, I mean, I, I think, Ideally for SQLite, the most principled and 683 00:38:21,758 --> 00:38:27,178 foundational approach would be to rebuild SQLite from the ground up to 684 00:38:27,178 --> 00:38:29,418 be incremental and to be reactive. 685 00:38:29,798 --> 00:38:35,138 And I think actually a former coworker of yours, who is um, Julian, who 686 00:38:35,168 --> 00:38:40,448 has also created, like HipHopVM at, at Facebook and, and HackLang. 687 00:38:40,943 --> 00:38:45,463 he's actually trying to, to do that with a new startup called, Skip that is a 688 00:38:45,463 --> 00:38:50,923 very ambitious, or even audacious goal to build the whole new database from scratch 689 00:38:50,923 --> 00:38:52,338 to be reactive, from the ground up. 690 00:38:53,003 --> 00:38:57,620 Which I think is very, very interesting, but short of that, I think you, you're 691 00:38:57,620 --> 00:39:04,080 left by imposing, reactivity, primitives on top of SQLite and the best you can 692 00:39:04,090 --> 00:39:09,353 do there is to do the minimal amount of work, you need at a given situation. 693 00:39:09,538 --> 00:39:16,278 So instead of pulling the entire table that, you know, okay, row 47 has changed. 694 00:39:16,458 --> 00:39:20,248 And that requires quite a bit of like a reactivity system on top of it. 695 00:39:20,628 --> 00:39:25,642 And so luckily with, a signal like system, that is a, it's a pretty 696 00:39:25,642 --> 00:39:29,622 good, primitive to put on top of it, to implement that reactivity. 697 00:39:30,072 --> 00:39:33,905 But, yeah, it's not as efficient as you, if you'd build it 698 00:39:33,905 --> 00:39:34,985 from the, from the ground up. 699 00:39:35,325 --> 00:39:40,175 That being said, SQLite is so fast that you get a lot of performance benefits out 700 00:39:40,175 --> 00:39:42,155 of the box and you can get away with it. 701 00:39:42,588 --> 00:39:43,618 Yeah, that's true. 702 00:39:43,668 --> 00:39:44,918 you know, certainly on the client. 703 00:39:44,968 --> 00:39:48,898 Um, yeah, I, I, without, you know, sharing too much, internal stuff, 704 00:39:49,258 --> 00:39:51,278 there was a big push, at Facebook. 705 00:39:51,718 --> 00:39:56,238 I'm thinking six, seven years ago, you know, to think about reactivity for 706 00:39:56,238 --> 00:40:00,678 the entire Facebook stack, because this concept of, you know, taking the ideas 707 00:40:00,678 --> 00:40:02,188 of React that were out on facebook. 708 00:40:02,218 --> 00:40:06,438 com and bringing them all the way back through the web servers, which are 709 00:40:06,468 --> 00:40:10,128 HHVM, all the way through to the data stores that were being used at the 710 00:40:10,128 --> 00:40:14,048 company, you know, like it's a utopian vision, but like, if you can make 711 00:40:14,048 --> 00:40:15,788 that push based all the way through. 712 00:40:16,078 --> 00:40:17,008 That's spectacular. 713 00:40:17,488 --> 00:40:22,068 and you know, Julian and I were probably in many of the same, uh 714 00:40:22,098 --> 00:40:25,098 discussion groups and, you know, internal Facebook groups where a lot of 715 00:40:25,128 --> 00:40:26,948 these ideas were being bounced around. 716 00:40:27,378 --> 00:40:31,928 And, and so yeah, when he left and went off and did, Skip and you know, 717 00:40:31,958 --> 00:40:36,788 I guess maybe some of this informed my thinking about TinyBase too, you know, 718 00:40:36,978 --> 00:40:40,248 that dream is still very much alive. 719 00:40:40,298 --> 00:40:44,078 and yeah, it's coming from, ironically, it's like it's coming from React. 720 00:40:44,128 --> 00:40:45,598 That idea is coming from React. 721 00:40:45,638 --> 00:40:48,968 It's just like, well, okay, can we bring that idea a little bit further back, 722 00:40:48,968 --> 00:40:51,148 and then a little bit further back, and then a little bit further back? 723 00:40:51,148 --> 00:40:53,048 Oh, like, and then we end up on the database. 724 00:40:53,298 --> 00:40:57,678 Like, why can't we get the database to be, you know, telling us about these changes? 725 00:40:57,738 --> 00:41:00,608 And, yeah, I guess that would be the vision. 726 00:41:00,648 --> 00:41:04,238 Unfortunately, I haven't checked in recently, but I think SkipDB wasn't 727 00:41:04,783 --> 00:41:08,663 Wasn't at a level where I could start working with it, when I last looked. 728 00:41:08,763 --> 00:41:09,593 Um, so yeah. 729 00:41:10,137 --> 00:41:13,867 I think it's not yet fully production ready, but as far as I know, 730 00:41:13,867 --> 00:41:15,207 they're just still working on it. 731 00:41:15,677 --> 00:41:20,993 So in terms of the query layer for TinyBase, you have, not 732 00:41:21,213 --> 00:41:25,587 implemented SQL as a query language on top of your own data store. 733 00:41:25,587 --> 00:41:25,786 Um, so yeah. 734 00:41:25,937 --> 00:41:29,667 But you've provided, given that JavaScript or TypeScript is the 735 00:41:29,667 --> 00:41:34,217 primary way how you interact with it, you've just embedded a DSL, as a 736 00:41:34,217 --> 00:41:36,537 query language directly into TinyBase. 737 00:41:36,887 --> 00:41:40,683 So would you mind briefly describing that and, how that describes a user 738 00:41:40,683 --> 00:41:42,173 experience or the developer experience? 739 00:41:42,600 --> 00:41:43,770 Right, right. 740 00:41:43,890 --> 00:41:47,320 So the, the journey here for me was that first I knew there were going 741 00:41:47,320 --> 00:41:51,800 to be some very simple query like primitives that I wanted to have. 742 00:41:51,810 --> 00:41:57,220 So one is, like a metric, which is some kind of, you know, number 743 00:41:57,220 --> 00:41:59,060 derived from the content in a table. 744 00:41:59,260 --> 00:42:02,500 And I knew that that was going to be the first thing I wanted to have if I was 745 00:42:02,500 --> 00:42:04,840 building this hypothetical GitHub app. 746 00:42:04,840 --> 00:42:07,820 I was going to want to have a number in the top that said number of repos, 747 00:42:07,820 --> 00:42:10,180 and I was going to want to have a number that said number of commits. 748 00:42:10,510 --> 00:42:12,570 And so I actually baked in. 749 00:42:12,875 --> 00:42:19,145 a kind of a metrics oriented DSL where you can define a metric which is, you know, 750 00:42:19,155 --> 00:42:24,915 basic aggregates, counts, averages, sums, those sorts of things, which by default 751 00:42:24,915 --> 00:42:29,715 is normally just counting the rows in a table, but that is then a reactive entity. 752 00:42:29,715 --> 00:42:33,388 So again, you can say, I just want this span in the top right hand 753 00:42:33,388 --> 00:42:36,675 corner of the window to just always show whatever that metric was, 754 00:42:36,985 --> 00:42:38,085 and then you can forget about it. 755 00:42:38,215 --> 00:42:40,665 And if that table is updated, You know that number's going to change, 756 00:42:40,695 --> 00:42:41,595 TinyBase will take care of it. 757 00:42:42,015 --> 00:42:44,285 another thing that I knew I was going to need to do was 758 00:42:44,285 --> 00:42:45,495 relationships between tables. 759 00:42:45,505 --> 00:42:51,265 So I built a very specific thing for just one local table, one remote foreign 760 00:42:51,305 --> 00:42:53,325 table, and then like keys between them. 761 00:42:53,535 --> 00:42:57,475 So the value of one column in one table is used as the identifier for another. 762 00:42:57,795 --> 00:43:01,475 And so, yeah, you want to see the issues just for this repo, or you want to see 763 00:43:01,772 --> 00:43:04,162 the repos just for this user, then you'll be able to do those kinds of things. 764 00:43:04,332 --> 00:43:07,582 So those are, were actually baked in pretty early to TinyBase, just because 765 00:43:07,582 --> 00:43:13,102 they seemed Very important, but I, I guess I knew eventually people were 766 00:43:13,132 --> 00:43:19,702 going to want to push that on and start doing things like arbitrary queries. 767 00:43:20,162 --> 00:43:26,112 And I toyed with the idea of, you know, supporting SQL in some form. 768 00:43:26,777 --> 00:43:28,297 And I should say, I love SQL. 769 00:43:28,297 --> 00:43:30,947 I have been writing SQL most of my professional career, which 770 00:43:30,947 --> 00:43:32,927 you know, goes back a fair way. 771 00:43:33,317 --> 00:43:37,397 And my most recent role at Facebook was, you know, the data analytics 772 00:43:37,577 --> 00:43:40,347 team in the data infrastructure org. 773 00:43:40,347 --> 00:43:43,217 So you know, we were working with SQL all the time. 774 00:43:43,437 --> 00:43:47,867 Perhaps despite that, or because of that, I kind of got nervous about doing 775 00:43:47,867 --> 00:43:53,367 an arbitrary SQL parser, evaluator, executor, and then being able to make 776 00:43:53,367 --> 00:43:58,398 the results reactive, which was like, that's the, that's That's the non deniable 777 00:43:58,418 --> 00:44:00,608 requirement, like, it has to be, reactive. 778 00:44:01,268 --> 00:44:06,458 So, I didn't really have a lot of choice, but, oh, and by the way, you 779 00:44:06,458 --> 00:44:09,858 know, a full SQL parser with all the dialects you'd want is, like, it's 780 00:44:09,858 --> 00:44:12,338 gonna quadruple the size of TinyBase. 781 00:44:12,338 --> 00:44:13,288 It's just, it's code based. 782 00:44:13,288 --> 00:44:15,298 So, no longer tiny. 783 00:44:15,348 --> 00:44:15,858 Yes. 784 00:44:15,888 --> 00:44:19,298 So, I wanted to come at it from a slightly different direction. 785 00:44:19,298 --> 00:44:22,868 I know I'm not the only person to have thought of doing more like a DSL approach 786 00:44:22,868 --> 00:44:26,778 to this, but I, I wanted to see whether I could capture as much of the, the 787 00:44:26,778 --> 00:44:31,348 valuable parts of SQL without turning it into a place where people could build 788 00:44:31,808 --> 00:44:33,828 stupid cartesian joins and kill their app. 789 00:44:34,158 --> 00:44:36,978 so I wanted to make it kind of a little bit on rails. 790 00:44:37,118 --> 00:44:41,528 And yeah, it was the idea of TinyQL was born, which is kind of a stupid name. 791 00:44:41,528 --> 00:44:45,305 But it's, as you said, a DSL that allows you to, you know, 792 00:44:45,315 --> 00:44:48,358 select, where, group, limit. 793 00:44:48,658 --> 00:44:50,098 , actually, no, I don't have a limit in there. 794 00:44:50,098 --> 00:44:53,028 I limit a later part in the pipeline. 795 00:44:53,028 --> 00:44:55,778 But, yeah, basically allows you to join tables together. 796 00:44:56,103 --> 00:45:01,623 and then where them, there's havings and groups and those kinds of things too, 797 00:45:02,193 --> 00:45:07,113 which like really is what 98 percent of people want to do with SQL anyway. 798 00:45:07,223 --> 00:45:11,393 And haven't really come across too many queries that I can't express 799 00:45:11,483 --> 00:45:14,263 with these five or six quote keywords. 800 00:45:14,623 --> 00:45:17,453 Yeah, the cool thing about SQL is you can do anything. 801 00:45:17,463 --> 00:45:19,293 The awful thing about SQL is you can do anything. 802 00:45:19,673 --> 00:45:24,103 And, I have worked with enough people who thought they knew what SQL did and 803 00:45:24,113 --> 00:45:27,143 didn't quite know what it did to, you know, see the trouble you can get into. 804 00:45:27,143 --> 00:45:30,793 So I really, yeah, wanted to make it a little bit more constrained than that. 805 00:45:31,483 --> 00:45:33,273 And it's, it's been, it was a real journey. 806 00:45:33,293 --> 00:45:35,743 That was a very tricky, for me, anyway. 807 00:45:35,743 --> 00:45:38,663 , that was a very tricky thing to build, especially making sure 808 00:45:38,663 --> 00:45:39,853 that all the results are reactive. 809 00:45:39,883 --> 00:45:42,603 Because, by the way, you know, any of those tables could change. 810 00:45:43,198 --> 00:45:44,808 Any of the rows in those tables could change. 811 00:45:44,978 --> 00:45:47,928 In fact, you don't want to react to the changes of the results if 812 00:45:47,928 --> 00:45:49,398 some of the irrelevant rows change. 813 00:45:49,398 --> 00:45:51,861 You only want to know if it was the relevant rows that changed. 814 00:45:51,861 --> 00:45:54,828 So you have to set up listeners on all the things that were relevant and 815 00:45:54,948 --> 00:45:56,508 not the things that were irrelevant. 816 00:45:56,918 --> 00:45:59,468 so that ended up being a little harder than I thought, but yeah, 817 00:45:59,468 --> 00:46:00,628 pretty proud with how it turned out. 818 00:46:00,978 --> 00:46:04,633 And as a result, and I would urge people to go check this out, You can 819 00:46:04,633 --> 00:46:09,623 build kind of interesting analytics apps, right, with this, in the browser. 820 00:46:09,753 --> 00:46:11,773 So if you go to the TinyBase. 821 00:46:11,863 --> 00:46:14,733 org demos, you'll see there's a demo. 822 00:46:14,773 --> 00:46:17,453 I think it's called car analytics or something like that. 823 00:46:17,453 --> 00:46:20,453 And it takes a data set of car data, different models, different 824 00:46:20,463 --> 00:46:23,513 makes, miles per gallon, years. 825 00:46:23,563 --> 00:46:27,503 So there's a bunch of numerical measures and there's a bunch of dimensions. 826 00:46:27,752 --> 00:46:29,902 And in the browser, it loads all the data in pretty quick. 827 00:46:30,202 --> 00:46:32,392 It's not that much data, but you know, loads it in pretty quickly. 828 00:46:32,392 --> 00:46:35,545 And then you can do groups, sorts, filters, across all 829 00:46:35,545 --> 00:46:37,045 of this data in the browser. 830 00:46:37,045 --> 00:46:38,915 And it's like, it's completely instant. 831 00:46:39,015 --> 00:46:40,825 It's like, great, love it. 832 00:46:41,145 --> 00:46:44,815 and look, this is not going to be great for querying the Facebook data 833 00:46:44,815 --> 00:46:49,005 warehouse, however many hundreds of petabytes that is these days, but 834 00:46:49,005 --> 00:46:53,525 it is going to be fine if you've got 10 or 100, 000 rows in your browser. 835 00:46:53,545 --> 00:46:55,135 And Don't be scared, people. 836 00:46:55,305 --> 00:46:58,115 Like, getting a hundred thousand rows of data into a browser is not hard. 837 00:46:58,575 --> 00:47:00,225 The browser is perfectly capable of doing that. 838 00:47:00,405 --> 00:47:04,435 You know, most even phones are very happy to run that kind 839 00:47:04,435 --> 00:47:05,945 of sized data in, in memory. 840 00:47:06,285 --> 00:47:09,815 so yeah, for a lot of even analytics use cases, you can do, you could, you 841 00:47:09,815 --> 00:47:13,345 could build interactive dashboards with this kind of technology and 842 00:47:13,345 --> 00:47:17,245 have a sort of constrained set of queries all running off TinyBase. 843 00:47:17,245 --> 00:47:19,655 And it's super feasible, and it's been. 844 00:47:20,310 --> 00:47:21,810 It's been fun to build that. 845 00:47:22,070 --> 00:47:24,160 Whether or not people are using it in the wild, I don't know. 846 00:47:24,275 --> 00:47:27,775 I should say, I don't put any instrumentation into this product. 847 00:47:27,775 --> 00:47:31,115 So who knows what people are using it for, or how they're using it. 848 00:47:31,405 --> 00:47:34,035 But I hope that those people who use it do, do find the value of being 849 00:47:34,035 --> 00:47:37,075 able to run queries, without, shooting themselves in the foot with SQL. 850 00:47:37,615 --> 00:47:41,505 Did you follow any sort of prior art in regards to how you built 851 00:47:41,505 --> 00:47:43,395 that reactive query system? 852 00:47:43,395 --> 00:47:48,865 So, as I was working on a similar part as, the work on Rffle, there, 853 00:47:48,915 --> 00:47:53,415 there was, some prior art in regards to Adapton and mini Adapton. 854 00:47:53,825 --> 00:47:57,915 And so this was, some inspiration, that we followed for the reactivity 855 00:47:57,935 --> 00:48:04,155 system and we've like over time learned that, it's very similar to how now like 856 00:48:04,155 --> 00:48:06,365 signals is all the rage these days. 857 00:48:06,455 --> 00:48:09,025 It's like very similar in terms of the ideas. 858 00:48:09,055 --> 00:48:11,215 So did you follow some, some similar paths? 859 00:48:12,236 --> 00:48:12,716 No. 860 00:48:13,276 --> 00:48:14,016 And I wish I had. 861 00:48:14,416 --> 00:48:16,016 I think it's one of my weaknesses. 862 00:48:16,066 --> 00:48:18,096 One of my weaknesses is that I look at a problem. 863 00:48:18,186 --> 00:48:19,756 You know, it's like the famous Hacker News thing. 864 00:48:19,766 --> 00:48:21,726 It's like, I could have built that in a weekend. 865 00:48:22,056 --> 00:48:23,876 I, I look at a reactive query system. 866 00:48:23,876 --> 00:48:25,296 I think, yeah, I'm sure I could build that. 867 00:48:25,636 --> 00:48:30,146 And then like two months later, I was like, I wish I'd read a bit more. 868 00:48:30,236 --> 00:48:35,343 I am not good at prior art and reading scientific papers or even 869 00:48:35,343 --> 00:48:37,263 just, you know, looking at other projects to see how they're done. 870 00:48:37,263 --> 00:48:39,763 I think, you know, I know what the result should be and I'm 871 00:48:39,763 --> 00:48:40,783 going to try to build it myself. 872 00:48:40,783 --> 00:48:41,703 And then I get into trouble. 873 00:48:42,056 --> 00:48:43,466 Fortunately, I worked my way out of trouble. 874 00:48:44,236 --> 00:48:48,116 If it makes you feel any better, even with knowledge of the prior art, 875 00:48:48,346 --> 00:48:52,936 it still took easily more than two months just to get anything working. 876 00:48:53,336 --> 00:48:53,846 So, 877 00:48:55,111 --> 00:48:59,021 Okay, that's just, that's just my, my weakness that fortunately I 878 00:48:59,026 --> 00:49:00,651 was able to work around this time. 879 00:49:00,651 --> 00:49:06,041 But yeah, I think the other thing is that I obviously knew in intense detail 880 00:49:06,401 --> 00:49:11,141 how the reactivity of the underlying tables, rows, and cells were right. 881 00:49:11,411 --> 00:49:16,591 And so I knew how I was gonna have to cascade the listener trees into all 882 00:49:16,591 --> 00:49:18,391 the relevant parts of the database. 883 00:49:18,801 --> 00:49:23,251 And, That was, yeah, that was just something I had to figure out for myself. 884 00:49:23,981 --> 00:49:27,511 The other thing I alluded to earlier is that TinyBase is 885 00:49:27,511 --> 00:49:29,221 just draped with test cases. 886 00:49:29,231 --> 00:49:32,371 Like, I am exhaustive, overly exhaustive. 887 00:49:32,371 --> 00:49:35,621 I'm completely OCD when it comes to test cases. 888 00:49:35,901 --> 00:49:37,421 It's not quite TDD. 889 00:49:37,721 --> 00:49:41,071 I don't always do the tests first, but, you know, I do eventually try 890 00:49:41,071 --> 00:49:42,481 to make it look like I did TDD. 891 00:49:42,521 --> 00:49:46,981 So I built every possible query I can think of, and I build all these demo 892 00:49:46,981 --> 00:49:50,901 apps, and I build all these sample, you know, fragments of code, and 893 00:49:50,911 --> 00:49:54,801 they all get tested exhaustively, and so I figure out, well, once, once, 894 00:49:55,261 --> 00:49:56,871 once those work, I'm probably there. 895 00:49:57,238 --> 00:49:59,258 So that, that was my benchmark, and I got there. 896 00:49:59,558 --> 00:50:02,108 It's probably, I'm sure it's still possible to break it, 897 00:50:02,158 --> 00:50:05,568 but, you know, I think, I think I'm about where it needs to be. 898 00:50:06,356 --> 00:50:10,586 So one of my favorite problems to, to think about, and it's a 899 00:50:10,586 --> 00:50:14,836 love hate relationship in terms of technical problems, which are schema 900 00:50:14,886 --> 00:50:18,736 migrations and schema evolution, which is something I thought a lot 901 00:50:18,736 --> 00:50:20,566 about, through the work on Prisma. 902 00:50:20,616 --> 00:50:26,816 And I tried for the work on Overtone, ideally having to deal with as little 903 00:50:27,016 --> 00:50:30,481 as possible, or at least have a very principled foundation for that. 904 00:50:30,781 --> 00:50:34,371 And I feel this is a bit of a underdeveloped area in some 905 00:50:34,371 --> 00:50:36,911 of the available open source technologies right now, or 906 00:50:36,911 --> 00:50:38,511 local-first technologies right now. 907 00:50:38,921 --> 00:50:44,661 So I'm curious how you have been thinking about schema migrations for TinyBase. 908 00:50:45,051 --> 00:50:47,271 and if you've already built some foundation there. 909 00:50:47,755 --> 00:50:48,025 Okay. 910 00:50:48,025 --> 00:50:50,825 Well, before we talk about schema migrations, we should talk about schemas. 911 00:50:51,395 --> 00:50:51,875 So. 912 00:50:52,390 --> 00:50:57,570 even having a schema was kind of a late thought for me, TinyBase outta the 913 00:50:57,570 --> 00:51:00,600 box by default, when, and especially when I first built, it, didn't have 914 00:51:00,600 --> 00:51:02,310 the concept of a schema at all. 915 00:51:03,120 --> 00:51:07,430 And, you know, any row could have any set of cells. 916 00:51:07,430 --> 00:51:10,095 I mean, a, a a row of cells was basically just a map. 917 00:51:10,655 --> 00:51:14,675 So it was possible for every row of a table to have completely different 918 00:51:14,675 --> 00:51:16,396 cells in it, which is a bit. 919 00:51:16,880 --> 00:51:21,290 Scary, and contrarian especially for someone like me who'd 920 00:51:21,290 --> 00:51:23,670 just come from working on the world's largest data warehouse. 921 00:51:24,030 --> 00:51:25,940 the idea of doing this was, was a little daunting. 922 00:51:26,380 --> 00:51:31,886 But,, I felt like, you know, the word tiny meant I was allowed to do things like that 923 00:51:32,096 --> 00:51:37,576 and take away some constraints and just let people put arbitrary data into this 924 00:51:38,236 --> 00:51:39,906 basically a map of map of maps, right? 925 00:51:39,906 --> 00:51:41,396 Tables of rows of cells. 926 00:51:41,446 --> 00:51:46,366 Now that obviously is fun for little demo apps, but at some point you 927 00:51:46,366 --> 00:51:47,986 need to start tidying things up. 928 00:51:48,026 --> 00:51:50,296 It's all very good when it's just one person building it too, right? 929 00:51:50,296 --> 00:51:53,096 Because I can remember all the fields that are supposed to be in this table. 930 00:51:53,476 --> 00:51:55,926 but when I've got colleagues who maybe want to know what the schema is, 931 00:51:55,946 --> 00:51:56,866 then should do something different. 932 00:51:57,616 --> 00:51:59,406 So I did, yeah, we do have schema support. 933 00:51:59,696 --> 00:52:02,146 I did add schema support, but it's optional, right? 934 00:52:02,146 --> 00:52:06,006 You, you, you have to explicitly add the schema onto your store. 935 00:52:06,386 --> 00:52:09,846 but even then the schema is, I'm afraid, sorry, very basic. 936 00:52:10,196 --> 00:52:13,526 So you can say whether a cell is a, you know, string, a boolean or a number. 937 00:52:13,861 --> 00:52:16,551 And you can say whether it was required or not, and what the default is. 938 00:52:17,231 --> 00:52:17,621 That's it. 939 00:52:18,141 --> 00:52:20,531 So that seems a little daunting. 940 00:52:20,931 --> 00:52:24,761 I should say, by the way, that TinyBase only supports those data types anyway. 941 00:52:24,791 --> 00:52:27,111 So you can't put anything other than a string, a number, and 942 00:52:27,121 --> 00:52:29,471 a Boolean into a field anyway. 943 00:52:29,471 --> 00:52:35,041 So I don't have support for arrays, or objects, or even null. 944 00:52:35,541 --> 00:52:41,131 so that kind of makes you realize this is a relatively simple thing to model anyway. 945 00:52:41,911 --> 00:52:46,396 But yeah, that, that, that's, that's where we are right now with schemas. 946 00:52:46,446 --> 00:52:51,296 And once you've added a schema, it will try to massage the data 947 00:52:51,376 --> 00:52:53,886 into that schema, and it'll drop anything that doesn't match. 948 00:52:54,236 --> 00:52:55,976 that's pretty basic, I will admit. 949 00:52:55,976 --> 00:53:00,316 But there are two, I think, two main things I need to do from here with this, 950 00:53:00,346 --> 00:53:01,516 because clearly that's not sufficient. 951 00:53:01,826 --> 00:53:03,656 It's great for fun, simple apps. 952 00:53:03,956 --> 00:53:07,516 It's actually, I will say, it does allow you to do more than you might think. 953 00:53:07,776 --> 00:53:12,456 But anyway, people do want to do arrays, and That lets you do many to many joins, 954 00:53:12,456 --> 00:53:14,786 and they do want to do objects, so fine. 955 00:53:14,796 --> 00:53:15,876 We should probably get to that at some point. 956 00:53:16,396 --> 00:53:20,843 So, that's, yeah, that's my big push, I think, is making time based support 957 00:53:20,883 --> 00:53:22,613 richer data types inside a cell. 958 00:53:22,926 --> 00:53:24,246 I think arrays will be the first. 959 00:53:24,606 --> 00:53:27,896 I'll probably stick to having arrays of one, type, like it's going to 960 00:53:27,896 --> 00:53:29,916 be all numbers or all strings. 961 00:53:29,926 --> 00:53:31,176 I don't think I'm going to have mixed arrays. 962 00:53:31,176 --> 00:53:32,276 That could get too tricky. 963 00:53:32,276 --> 00:53:33,786 I still want to have some kind of validation. 964 00:53:34,263 --> 00:53:35,143 so we'll do that. 965 00:53:35,393 --> 00:53:39,113 , that knocks off a many to many requirement that a bunch of people have had on GitHub 966 00:53:39,113 --> 00:53:41,303 that I can't currently do very easily. 967 00:53:41,890 --> 00:53:43,590 And so we'll, we'll do that. 968 00:53:43,620 --> 00:53:47,630 Secondly, I would love to do more with the schemas themselves. 969 00:53:48,000 --> 00:53:52,830 So the, schema dialect that I put in right now is just, like I said, 970 00:53:53,190 --> 00:53:57,675 a fairly simple description of what the rows are, but There's Zod in the 971 00:53:57,675 --> 00:54:01,225 world, and there's Effect, and a bunch of other ways that people like to 972 00:54:01,225 --> 00:54:05,401 express schemas, and if they've already built their schema for some other part 973 00:54:05,401 --> 00:54:08,371 of the app, or they've built it for some, you know, some other part of the 974 00:54:08,371 --> 00:54:12,921 system, maybe up on the server, like, how can they then turn that into the 975 00:54:13,631 --> 00:54:15,991 schema that resides in the app itself? 976 00:54:16,251 --> 00:54:22,821 So I have no doubt that I will be building Zod to TinyBase Converter, and Effect 977 00:54:22,821 --> 00:54:27,341 to TinyBase Converter or some, some, something to map, map those schemas in. 978 00:54:27,711 --> 00:54:30,931 Still doesn't solve your problem of migration, and I will admit 979 00:54:30,961 --> 00:54:34,031 that I am a bit daunted by that. 980 00:54:34,251 --> 00:54:36,991 In general, I am daunted by the whole space of anything that 981 00:54:36,991 --> 00:54:41,255 requires build time code mods and, you know, running generated code. 982 00:54:42,655 --> 00:54:46,375 Like, I just hate that because immediately you've got a team of 10 people and all 983 00:54:46,380 --> 00:54:47,665 of their artifacts get out of sync. 984 00:54:47,670 --> 00:54:48,925 I've seen that at Facebook, right? 985 00:54:49,315 --> 00:54:51,325 Whether it's GraphQL or what have you. 986 00:54:51,595 --> 00:54:55,285 So I, always gonna be trying to look for solutions that don't require you 987 00:54:55,285 --> 00:55:00,255 to run a build step, to generate some magic code that you then have to remember 988 00:55:00,255 --> 00:55:04,605 to link in, how to actually avoid that step and still do schema migration. 989 00:55:05,435 --> 00:55:06,795 is not quite in my head yet. 990 00:55:07,065 --> 00:55:09,535 So I share your sentiment. 991 00:55:09,535 --> 00:55:11,905 These things are underdeveloped, but I don't have an answer for you. 992 00:55:12,573 --> 00:55:12,933 Right. 993 00:55:12,983 --> 00:55:18,233 I can share my thoughts on it, which is, kind of like an architectural one, that 994 00:55:18,233 --> 00:55:23,223 has quite a lot of implications, but those implications are appealing to me, I would 995 00:55:23,223 --> 00:55:28,793 separate the read path from the write path and have the write path be event driven. 996 00:55:29,403 --> 00:55:32,983 And that allows me to do sort of event sourcing. 997 00:55:33,023 --> 00:55:37,373 So similar to how Redux works, but, persisted and distributed 998 00:55:37,653 --> 00:55:39,143 Redux, if you, if you want. 999 00:55:39,543 --> 00:55:43,943 And, then like the way, how you interpret your Redux events. 1000 00:55:44,388 --> 00:55:47,168 you can interpret them however you want, and you can very 1001 00:55:47,168 --> 00:55:48,768 easily change that over time. 1002 00:55:49,168 --> 00:55:53,178 So you basically just go through the entire event source, the entire event 1003 00:55:53,188 --> 00:55:58,518 log of events, the entire history, and your app database that you 1004 00:55:58,518 --> 00:56:01,718 read from, that is just, a result. 1005 00:56:01,988 --> 00:56:06,418 By going through all the events and every event, you might come 1006 00:56:06,418 --> 00:56:08,468 across like a user signed up event. 1007 00:56:08,798 --> 00:56:14,688 , you then translate into a insert into users with all the values. 1008 00:56:15,078 --> 00:56:19,458 And you can change the user table at any moment, you can, instead of 1009 00:56:19,468 --> 00:56:23,608 having one user table, you can have a customer's table and an employee's 1010 00:56:23,628 --> 00:56:26,398 table and just reinterpret the events. 1011 00:56:26,428 --> 00:56:29,668 And so this way, you don't have schema migrations at all. 1012 00:56:29,878 --> 00:56:33,338 You just change sort of the . Interpretation of your event. 1013 00:56:33,748 --> 00:56:36,528 it requires a bit of a different architectural approach and I'm 1014 00:56:36,748 --> 00:56:40,568 building LiveStore all around this idea, which is very different 1015 00:56:40,858 --> 00:56:42,518 from, from other approaches. 1016 00:56:42,878 --> 00:56:47,148 But it's the most principled approach that I can currently think of in 1017 00:56:47,148 --> 00:56:51,228 terms of schema migrations, where you don't do like my schema migrations, 1018 00:56:51,228 --> 00:56:53,438 basically just I design a new schema. 1019 00:56:53,893 --> 00:56:58,423 And I target that and there's no, like, up or down scripts. 1020 00:56:58,733 --> 00:57:02,463 and so that is very appealing, but it has other downsides as well. 1021 00:57:02,713 --> 00:57:07,053 But this is certainly an idea that I, that I think is worth exploring more. 1022 00:57:07,456 --> 00:57:08,186 No, I like that. 1023 00:57:08,186 --> 00:57:12,044 And it also shares, , Similarities with what I'm separately having to do 1024 00:57:12,054 --> 00:57:18,454 for the, native CRDT synchronization, as of TinyBase version 5, which at 1025 00:57:18,454 --> 00:57:21,704 the time we're speaking isn't quite released, but by the time this podcast 1026 00:57:21,714 --> 00:57:22,834 is out might have been released. 1027 00:57:22,834 --> 00:57:24,884 So, hey everybody, go download version 5. 1028 00:57:25,094 --> 00:57:29,334 but no, it's, it's got a native CRDT support and I do that by sending, 1029 00:57:29,604 --> 00:57:32,944 you know, basically little, little messages, but those are the data. 1030 00:57:33,304 --> 00:57:33,914 level, right? 1031 00:57:33,984 --> 00:57:37,814 It's, you know, here's, here's how this cell changed, and I wonder whether 1032 00:57:37,834 --> 00:57:41,754 like there's some higher abstraction for those messages that is then sort 1033 00:57:41,754 --> 00:57:45,824 of schema agnostic or that can be mapped into whatever the schema is. 1034 00:57:45,844 --> 00:57:49,404 Because by the way, the minute you start synchronizing things, the schemas 1035 00:57:49,424 --> 00:57:53,204 getting out of date is going to become an issue very quickly, because maybe 1036 00:57:53,204 --> 00:57:55,684 you've updated the app on your phone, but you haven't updated the app on 1037 00:57:55,684 --> 00:57:57,734 your desktop, but now they're jogging. 1038 00:57:58,004 --> 00:58:01,144 You know, WebSockets to each other and all hell breaks loose because 1039 00:58:01,144 --> 00:58:02,244 the schemas weren't quite the same. 1040 00:58:02,624 --> 00:58:03,864 It's all questions at the moment. 1041 00:58:04,288 --> 00:58:04,548 Yeah. 1042 00:58:04,558 --> 00:58:07,558 I think there's a whole episode just to be done around that. 1043 00:58:07,758 --> 00:58:11,768 Happy to form a little working group around this, but I think that goes 1044 00:58:11,778 --> 00:58:13,798 beyond the scope of, of this podcast. 1045 00:58:14,438 --> 00:58:18,228 but yeah, I think you've come really, really far with TinyBase and this 1046 00:58:18,238 --> 00:58:24,389 is, I think, a very common area of the local-first technologies part 1047 00:58:24,739 --> 00:58:27,979 that is not yet as fully explored. 1048 00:58:28,069 --> 00:58:32,979 and I've just changed my priorities to go double down on that from 1049 00:58:32,979 --> 00:58:36,709 the beginning, since I know like how gnarly this is to deal with. 1050 00:58:37,161 --> 00:58:40,821 Well, can I just say that, you know, from the point of view of exhaustiveness, 1051 00:58:41,241 --> 00:58:45,831 there are many, many unsolved problems in the local-first space. 1052 00:58:46,271 --> 00:58:50,451 And I have a horrible feeling that it's easy to think that 1053 00:58:50,471 --> 00:58:51,961 synchronization is the problem. 1054 00:58:52,021 --> 00:58:54,471 And then once you've solved synchronization, you're good. 1055 00:58:55,066 --> 00:58:56,756 Well, I don't think that's true. 1056 00:58:57,226 --> 00:59:03,086 And the reason I think that is that I've tried building some local-first 1057 00:59:03,106 --> 00:59:07,806 apps that, you know, have things like anonymous versus authenticated access, 1058 00:59:08,056 --> 00:59:14,386 or that have private data versus shared data, or that have online, offline, and, 1059 00:59:14,446 --> 00:59:18,576 you basically multiply this state machine together and you've got like, you know, 1060 00:59:18,852 --> 00:59:22,702 two to the power of four combinations of state that your clients can be in. 1061 00:59:23,092 --> 00:59:23,532 And. 1062 00:59:23,802 --> 00:59:26,292 It's all very well being able to synchronize data back and forwards, but, 1063 00:59:26,362 --> 00:59:29,882 like, you don't want to synchronize a non shared store with another user, or 1064 00:59:29,882 --> 00:59:34,102 you don't want to share an authenticated, you know, you brought a copy of an 1065 00:59:34,102 --> 00:59:38,162 authenticated database to be local, but now you log out and now that's 1066 00:59:38,162 --> 00:59:40,872 got to go, like, the state machine of what it actually takes to build a 1067 00:59:40,872 --> 00:59:44,022 real app like Figma or I don't know. 1068 00:59:45,002 --> 00:59:48,942 It's like, wow, there are lots of crazy, crazy transitions that can change here. 1069 00:59:49,292 --> 00:59:52,472 And I don't think anyone's really thinking about all of those. 1070 00:59:52,472 --> 00:59:55,612 I think we all got hung up on, oh, once we crack CRDTs, we'll be good. 1071 00:59:56,042 --> 00:59:57,402 No, no, no, no, that's the easy part. 1072 00:59:57,632 --> 01:00:00,762 It's not state management, it's the state of the state management in a way. 1073 01:00:00,762 --> 01:00:03,242 It's like, should I be synchronizing this data? 1074 01:00:03,502 --> 01:00:07,552 Because this person isn't logged in, or is logged in, or is online, or is 1075 01:00:07,552 --> 01:00:11,492 offline, or has shared this document, or hasn't shared this document, and 1076 01:00:11,632 --> 01:00:14,192 that is actually unsolved, I think. 1077 01:00:14,702 --> 01:00:19,382 So I built a little sample app called Tiny Rooms a way back, and like, 1078 01:00:19,382 --> 01:00:21,742 the app itself is stupid, it's just dragging rectangles around, but like, 1079 01:00:21,742 --> 01:00:25,182 I tried to model all of these different states, and it was really hard. 1080 01:00:25,182 --> 01:00:26,262 Like, I was, oh god. 1081 01:00:27,367 --> 01:00:30,737 I would hate to be actually trying to build a production grade 1082 01:00:30,837 --> 01:00:33,417 local-first app at the moment because those problems are really hard. 1083 01:00:33,843 --> 01:00:39,213 Yeah, I think the, the benefits are that if you're building linear 1084 01:00:39,253 --> 01:00:43,493 or if you're building Figma or if you're building, Tiny rooms. 1085 01:00:43,783 --> 01:00:49,043 the advantage for you is that you have very specific trade offs, very specific 1086 01:00:49,073 --> 01:00:51,913 constraints that work for your app. 1087 01:00:52,203 --> 01:00:55,783 And so you can take tons of shortcuts that for a more 1088 01:00:55,793 --> 01:00:57,653 general solution are not viable. 1089 01:00:57,933 --> 01:00:59,673 But for your app works. 1090 01:01:00,233 --> 01:01:04,283 And so, given that, well, there, there's many trade offs that 1091 01:01:04,303 --> 01:01:07,943 Figma can make that would not work for Linear and, and vice versa. 1092 01:01:08,233 --> 01:01:12,343 But I think that is where you can still build things in a local-first way, 1093 01:01:12,623 --> 01:01:15,233 that is like competitively, beneficial. 1094 01:01:15,843 --> 01:01:20,003 but I agree having the best of all worlds, is where we're a few 1095 01:01:20,383 --> 01:01:22,423 days away from that, at least. 1096 01:01:22,817 --> 01:01:23,267 Yeah. 1097 01:01:23,307 --> 01:01:25,747 So I think that's one of the curses of being a, a tools 1098 01:01:25,747 --> 01:01:27,307 builder like, like you, right. 1099 01:01:27,447 --> 01:01:30,227 you know, I feel that I'm building shovels for people who are going 1100 01:01:30,227 --> 01:01:31,367 to go off digging for gold. 1101 01:01:31,837 --> 01:01:34,207 but I have to build a shovel that'll work in all different 1102 01:01:34,217 --> 01:01:36,537 types of terrain to be successful. 1103 01:01:36,562 --> 01:01:39,662 Um, and so like I said, you know, is it going to be online, offline, 1104 01:01:39,692 --> 01:01:43,412 auth, not auth, stored locally, stored remotely shared or not shared? 1105 01:01:43,412 --> 01:01:45,832 Like, okay, now 16 combinations, got to support all 16. 1106 01:01:46,172 --> 01:01:48,202 Whereas yes, if you're Figma, you can say, well, no, you 1107 01:01:48,202 --> 01:01:49,412 can't ever use it anonymously. 1108 01:01:49,422 --> 01:01:50,202 You've always got to be logged in. 1109 01:01:50,352 --> 01:01:50,522 It's like, oh, great. 1110 01:01:50,522 --> 01:01:51,432 Okay, fine. 1111 01:01:51,542 --> 01:01:53,442 Like, I've just reduced that problem to eight. 1112 01:01:53,755 --> 01:01:54,145 Yeah. 1113 01:01:54,478 --> 01:01:56,268 Pick your trade offs very carefully. 1114 01:01:56,473 --> 01:01:59,633 And so this is why I'm also, why I'm working on Overtone 1115 01:01:59,633 --> 01:02:01,883 and LiveStore at the same time. 1116 01:02:02,143 --> 01:02:08,133 So if your app is sort of like Overtone esque shaped, then LiveStore 1117 01:02:08,173 --> 01:02:09,423 will work really well for you. 1118 01:02:09,853 --> 01:02:13,943 If it looks rather like Facebook, like a social network, or has like 1119 01:02:13,943 --> 01:02:18,233 some very different shapes and trade offs, then it's not as good of a fit. 1120 01:02:18,233 --> 01:02:22,390 But I think this is also what's so nice about having all those different 1121 01:02:22,390 --> 01:02:26,900 local-first tool vendors is they have all their different starting points and all 1122 01:02:26,900 --> 01:02:28,980 their different thoughts on trade offs. 1123 01:02:29,380 --> 01:02:32,607 And, I think this is where we're going to have a great shovel 1124 01:02:32,627 --> 01:02:34,477 for many different terrains. 1125 01:02:34,853 --> 01:02:35,023 Yeah. 1126 01:02:35,023 --> 01:02:36,083 I think that's exactly right. 1127 01:02:36,143 --> 01:02:37,493 You know, it's a dilemma. 1128 01:02:37,863 --> 01:02:41,773 Do you build a tool that fits one niche perfectly? 1129 01:02:41,813 --> 01:02:45,213 And, yeah, of course, that really informs, and you'll build a, you know, brilliant 1130 01:02:45,753 --> 01:02:47,653 tool for the one job you have in mind. 1131 01:02:48,153 --> 01:02:51,493 but at the same time, you may have limited your market. 1132 01:02:51,533 --> 01:02:54,453 Now, fortunately, I don't have to think about those things because, you know, it's 1133 01:02:54,453 --> 01:02:56,213 not a commercial, commercial business. 1134 01:02:56,473 --> 01:02:58,843 and I'm, but I'm trying to keep as many People are happy as possible. 1135 01:02:58,843 --> 01:03:01,553 And if I'm just listening to the feedback that's coming in on, on 1136 01:03:01,573 --> 01:03:04,203 GitHub and people are saying, Oh, I want it to go off in this direction. 1137 01:03:04,203 --> 01:03:05,743 And, Oh, someone else wants to go off in that direction. 1138 01:03:05,743 --> 01:03:10,103 It's like, okay I'm going to try to, maybe I'm going to 1139 01:03:10,103 --> 01:03:11,373 try to keep everybody happy. 1140 01:03:11,703 --> 01:03:13,153 but then the complexity falls onto me. 1141 01:03:13,303 --> 01:03:16,773 And that's where being a tools builder starts getting hard work. 1142 01:03:16,773 --> 01:03:17,483 You know, this. 1143 01:03:18,540 --> 01:03:18,880 Right. 1144 01:03:18,960 --> 01:03:23,250 I mean, for, for what it's worth, I think I'm trying to actually, write 1145 01:03:23,260 --> 01:03:27,680 down the cases where LiveStore is not a great fit for, and I'm trying 1146 01:03:27,700 --> 01:03:31,320 to be like, very explicit about the trade offs that I'm making. 1147 01:03:31,320 --> 01:03:35,550 So for example, LiveStore right now expects you to load all of your data 1148 01:03:35,855 --> 01:03:38,895 into a single SQLite in memory database. 1149 01:03:39,115 --> 01:03:44,735 Luckily, SQLite is very efficient, like based on my benchmarks and testing, much 1150 01:03:44,735 --> 01:03:47,115 more efficient than like JSON in memory. 1151 01:03:47,115 --> 01:03:51,165 It's much more efficient to keep like hundreds of thousands of rows in, 1152 01:03:51,175 --> 01:03:57,075 in memory SQLite as you keep them in just JavaScript objects in, in memory. 1153 01:03:57,458 --> 01:04:01,168 you need to just, deserialize the, just a few ones that you 1154 01:04:01,168 --> 01:04:04,578 actually want to like currently keep in memory for, for your query. 1155 01:04:05,128 --> 01:04:06,998 And so, but that's a major trade off. 1156 01:04:07,028 --> 01:04:11,228 If you have your database, if that's necessarily five gigabytes. 1157 01:04:11,503 --> 01:04:14,193 Then LiveStore is probably not a great fit for you. 1158 01:04:14,493 --> 01:04:20,073 Or if you don't want to follow the more like event sourced, nature of it, then 1159 01:04:20,073 --> 01:04:21,563 it's probably also not a good fit for it. 1160 01:04:21,573 --> 01:04:26,183 But I think this is what makes it more specific for when it's a good 1161 01:04:26,183 --> 01:04:28,403 fit for someone, whereas if it's not. 1162 01:04:28,813 --> 01:04:31,493 So actually this brings us onto an interesting thing that I think 1163 01:04:31,503 --> 01:04:34,023 this community needs, which is. 1164 01:04:34,598 --> 01:04:38,788 kind of a, a community view of what all these pros and cons are of the 1165 01:04:38,788 --> 01:04:43,548 different solutions and what the decision tree should be if you want 1166 01:04:43,548 --> 01:04:45,048 to build an app of a certain type. 1167 01:04:45,088 --> 01:04:47,238 You know, am I going to be online or am I going to be offline? 1168 01:04:47,248 --> 01:04:48,548 Am I going to be authed or not authed? 1169 01:04:48,918 --> 01:04:52,448 You know, how do I work my way down to what is going to be the best solution? 1170 01:04:52,968 --> 01:04:54,678 Because of course, you talk to a vendor, they're going to say, 1171 01:04:54,678 --> 01:04:55,458 oh, well, we can do everything. 1172 01:04:55,868 --> 01:05:00,788 but something that allows people to know when, you know, when should I use. 1173 01:05:00,808 --> 01:05:04,336 In something like the more mature front end world, people know when 1174 01:05:04,336 --> 01:05:07,570 they should be using React versus Svelte or whatever, I guess. 1175 01:05:07,570 --> 01:05:12,568 There are plenty of places you can go and read up about what the pros and the 1176 01:05:12,568 --> 01:05:14,332 cons are of these different solutions. 1177 01:05:14,332 --> 01:05:18,378 But right now, if you're looking at Database Sync Technology A and 1178 01:05:18,378 --> 01:05:22,578 Database Sync Technology B, they're both local-first, apparently, right? 1179 01:05:22,578 --> 01:05:24,418 But one's going to be better for one thing versus another. 1180 01:05:24,418 --> 01:05:26,188 Who's going providing that commentary. 1181 01:05:26,188 --> 01:05:30,008 And I think we haven't got that critical mass of users yet to be able to say, well, 1182 01:05:30,008 --> 01:05:32,988 I tried it and this didn't scale, but I went to this one and this one worked. 1183 01:05:33,168 --> 01:05:34,068 Someone else saying the opposite. 1184 01:05:34,078 --> 01:05:36,068 And then you can like have that debate. 1185 01:05:36,568 --> 01:05:37,118 and so. 1186 01:05:38,158 --> 01:05:40,458 Actually, this is something I didn't mention, but, you know, one of, one 1187 01:05:40,458 --> 01:05:44,068 of the parts of my local-first journey was, was putting together a directory 1188 01:05:44,068 --> 01:05:45,668 of these solutions that I was finding. 1189 01:05:46,438 --> 01:05:50,558 And so I launched that as the localfirstweb.dev site. 1190 01:05:50,598 --> 01:05:53,498 And that was partly me just trying to enumerate what these 1191 01:05:53,498 --> 01:05:54,718 different solutions were. 1192 01:05:55,128 --> 01:05:57,188 And obviously a community is built up around that. 1193 01:05:57,188 --> 01:05:58,758 And now we obviously have the conference as well. 1194 01:05:58,758 --> 01:06:01,958 And what I'm really hoping is that we see more and more people building real 1195 01:06:01,958 --> 01:06:05,675 apps, learning What trade offs they're having to make, and which of these 1196 01:06:05,675 --> 01:06:07,625 solutions they're then converging on. 1197 01:06:08,120 --> 01:06:11,030 given those and, and I think it's fine to have lots of different types of tools. 1198 01:06:11,040 --> 01:06:13,310 Sometimes you need a pickaxe, sometimes you need a shovel, 1199 01:06:13,460 --> 01:06:16,370 sometimes you need a pan to get the gold out of the river, right? 1200 01:06:16,720 --> 01:06:19,570 But it's not always obvious which tool you should take until you've 1201 01:06:19,570 --> 01:06:21,860 actually gone out into the hills and started looking for gold. 1202 01:06:22,230 --> 01:06:24,600 Sorry to stretch the analogy, maybe it's because I lived in California 1203 01:06:24,620 --> 01:06:27,510 for a while, but you know, I think we're not at that point yet, right? 1204 01:06:27,510 --> 01:06:30,820 We haven't had enough, we've not had enough miners going up into them, 1205 01:06:30,820 --> 01:06:32,210 there hills to go look for the gold. 1206 01:06:32,463 --> 01:06:34,983 so we haven't formed opinions about what the right trade offs are and 1207 01:06:35,003 --> 01:06:36,393 which are the right tool chains to use. 1208 01:06:36,828 --> 01:06:37,658 We will get there. 1209 01:06:37,748 --> 01:06:42,738 So speaking of real apps, it seems like I've recently nerd sniped on Twitter. 1210 01:06:42,738 --> 01:06:48,268 I've recently nerd sniped you into building another app on top of TinyBase, 1211 01:06:48,548 --> 01:06:51,158 which is a local-first GitHub client. 1212 01:06:51,448 --> 01:06:53,708 And you've made quite a lot of progress on that. 1213 01:06:53,778 --> 01:06:54,958 Do you, do you mind sharing more? 1214 01:06:55,233 --> 01:06:55,703 Sure. 1215 01:06:55,813 --> 01:06:58,013 So that was a, that was a clinical nerd snipe. 1216 01:06:58,053 --> 01:07:01,483 That was, that was one of the finest even though I was the victim. 1217 01:07:01,853 --> 01:07:04,813 So as I mentioned, you know, one of my original ideas for an app that would 1218 01:07:04,813 --> 01:07:08,543 use something like TinyBase was an open source dashboard that would let you see 1219 01:07:08,573 --> 01:07:12,313 large numbers of repos and, and, and, and study them and understand them at scale. 1220 01:07:12,580 --> 01:07:14,240 And I just never got around to building it. 1221 01:07:14,240 --> 01:07:17,570 And then of course you said something on Twitter like, Oh, wouldn't it be awesome 1222 01:07:17,570 --> 01:07:19,300 if there was a local-first GitHub client? 1223 01:07:19,320 --> 01:07:19,910 I'm like, No! 1224 01:07:20,680 --> 01:07:21,030 Damn it! 1225 01:07:21,100 --> 01:07:22,300 That's what I was supposed to build! 1226 01:07:22,310 --> 01:07:23,060 Now I remember! 1227 01:07:23,470 --> 01:07:28,340 So I took that and I figured that I would maybe go and put 1228 01:07:28,340 --> 01:07:30,840 my, my code where my mouth was. 1229 01:07:31,030 --> 01:07:36,380 So I started a little repo called TinyHub, which is in the 1230 01:07:36,380 --> 01:07:39,130 same , parent org as TinyBase. 1231 01:07:39,540 --> 01:07:41,200 So I guess you have show notes. 1232 01:07:41,200 --> 01:07:43,700 There'll be a link in the show notes and, people can go check that out. 1233 01:07:44,020 --> 01:07:48,165 And yes, it's a GitHub client for running locally in your browser. 1234 01:07:48,438 --> 01:07:52,148 And I wanted to try and build it as quickly as I could, just to hopefully 1235 01:07:52,398 --> 01:07:55,718 prove to myself that building apps with TinyBase would be fun. 1236 01:07:56,018 --> 01:08:01,428 And what it does is it, there's a quick bit of GitHub OAuth right 1237 01:08:01,428 --> 01:08:05,658 at the beginning, and it then will pull repos that are either starred 1238 01:08:05,918 --> 01:08:09,973 or that you, have personally, or any orgs that you're a member of if 1239 01:08:09,973 --> 01:08:11,933 you accept those OAuth permissions. 1240 01:08:12,113 --> 01:08:16,673 Anyway, long story short, it pulls the GitHub data via the API down 1241 01:08:16,703 --> 01:08:18,983 into a local TinyBase instance. 1242 01:08:19,303 --> 01:08:20,443 In fact, a number of instances. 1243 01:08:20,483 --> 01:08:24,863 It actually has, there's a TinyBase instance per repo, because if you 1244 01:08:24,863 --> 01:08:29,333 pull React, like, okay, now we've got 20, 000 pull requests or something. 1245 01:08:29,763 --> 01:08:35,257 So it will pull data locally, stores it in local storage. 1246 01:08:35,547 --> 01:08:38,277 Yes which seems to be fine, actually. 1247 01:08:38,637 --> 01:08:42,917 , just, it's a, the first hundred records which is the basic 1248 01:08:42,997 --> 01:08:44,217 pagination for, for GitHub. 1249 01:08:44,247 --> 01:08:48,777 So it's, it's not, you know, a comprehensive open source dashboard, but 1250 01:08:48,777 --> 01:08:53,047 it at least gives you A sample of, you know, 100 repos from 100 orgs and up to 1251 01:08:53,047 --> 01:08:54,667 100 pull requests and issues from each. 1252 01:08:55,000 --> 01:08:58,800 And yeah, then just paint a React UI out of that data. 1253 01:08:58,830 --> 01:09:02,110 And if you go check the repo, you'll see I put a little video 1254 01:09:02,110 --> 01:09:03,770 on the front page of the readme. 1255 01:09:04,027 --> 01:09:06,737 And of course, the beauty of this is that now all of this GitHub 1256 01:09:06,937 --> 01:09:08,437 data is sitting in your browser. 1257 01:09:09,052 --> 01:09:14,422 And, it's in memory, but you can flip through it at 16 milliseconds 1258 01:09:14,432 --> 01:09:15,862 a frame, to your heart's content. 1259 01:09:15,872 --> 01:09:19,932 So, you know, you press the, you press the down cursor and it whizzes down 1260 01:09:19,932 --> 01:09:20,882 through the pull requests and it's pffft. 1261 01:09:21,895 --> 01:09:22,905 Views them all instantly. 1262 01:09:22,945 --> 01:09:23,995 Not a spinner in sight. 1263 01:09:24,035 --> 01:09:24,745 It's pretty awesome. 1264 01:09:25,115 --> 01:09:28,565 I'm not sure it's a particularly useful tool at this point because it's read only. 1265 01:09:28,755 --> 01:09:33,445 Um, and it obviously has to do some work to pull the data down at the beginning. 1266 01:09:33,795 --> 01:09:38,125 But it certainly, I hope, emphasizes the fact that if you co locate your 1267 01:09:38,135 --> 01:09:41,915 data with the app, which I think is kind of like what this, this, this 1268 01:09:41,915 --> 01:09:44,772 local-first thing should be all about at some point, it's amazing. 1269 01:09:44,832 --> 01:09:46,202 Like, the UX is spectacular. 1270 01:09:46,442 --> 01:09:49,682 Um, and you can just paginate through this stuff at the speed of thought. 1271 01:09:50,012 --> 01:09:51,752 Which is really amazing. 1272 01:09:52,162 --> 01:09:56,102 I will just add a little side note here, is my philosophy around local-first, or 1273 01:09:56,102 --> 01:10:00,552 my motivation for local-first, apart from being on a boat, is the user experience. 1274 01:10:01,212 --> 01:10:03,392 I'm just tired of looking at spinners, because I'm waiting for 1275 01:10:03,392 --> 01:10:06,002 something to pull down and cache locally and then get thrown away. 1276 01:10:06,012 --> 01:10:08,382 Like, if I can get everything into the browser as soon as possible, 1277 01:10:09,052 --> 01:10:13,182 and then just go through it at 16 milliseconds That's, that's amazing. 1278 01:10:13,972 --> 01:10:18,455 So hopefully that has emphasized that point of local-first. 1279 01:10:18,475 --> 01:10:20,035 Yes, it's about data governance. 1280 01:10:20,035 --> 01:10:20,365 Yes. 1281 01:10:20,365 --> 01:10:21,935 It's about, you know, owning your own data. 1282 01:10:22,295 --> 01:10:22,595 Yes. 1283 01:10:22,595 --> 01:10:26,395 It's about you know, all the other things that are laid out in many, many 1284 01:10:26,395 --> 01:10:31,555 other essays other than mine, but like having a UX like that is really, for me, 1285 01:10:31,555 --> 01:10:33,725 what it's all about, that's the focus. 1286 01:10:33,725 --> 01:10:37,095 , and yeah, so I'd encourage people to go check out TinyHub and that's 1287 01:10:37,095 --> 01:10:41,165 me building what I think is a state of the art TinyBase app. 1288 01:10:42,195 --> 01:10:47,235 I would still say that to make it fully functional, making it read write is 1289 01:10:47,245 --> 01:10:50,852 a little more tricky because whilst GitHub has some pretty awesome bulk 1290 01:10:51,072 --> 01:10:58,002 read APIs the write APIs are a little different and making changes to a 1291 01:10:58,002 --> 01:11:01,232 local datastore and then going back online and reconciling that back up 1292 01:11:01,232 --> 01:11:03,882 to GitHub is not as easy as it sounds. 1293 01:11:04,172 --> 01:11:07,357 I'm sure it's Events are probably the answer to that, is what you'll say. 1294 01:11:07,717 --> 01:11:09,667 Um, but but I'm not there yet. 1295 01:11:09,973 --> 01:11:14,303 Yeah, again, to my earlier joke that we have more tools than, apps at this 1296 01:11:14,303 --> 01:11:18,983 point, I really want to just add to the pool of local-first demos, right, but 1297 01:11:19,143 --> 01:11:23,063 add to the number of apps that show what could be done, and really emphasize what 1298 01:11:23,093 --> 01:11:27,643 the benefits are of this whole movement rather than just building shovels. 1299 01:11:27,643 --> 01:11:30,143 I'm actually going to go out and look for some gold myself as well, 1300 01:11:30,193 --> 01:11:31,383 just to make sure the shovel's okay. 1301 01:11:31,743 --> 01:11:35,523 I think that's a wonderful place to, to wrap it here. 1302 01:11:35,783 --> 01:11:40,643 James, thank you so much for coming on the show today, sharing all of your wisdom 1303 01:11:40,663 --> 01:11:43,043 on TinyBase, the path that led you here. 1304 01:11:43,303 --> 01:11:43,973 Thank you so much. 1305 01:11:44,327 --> 01:11:48,367 It's been a huge pleasure and I wish the podcast the greatest success. 1306 01:11:48,367 --> 01:11:52,647 I'm so excited that you've set it up and honored to be a small part of it. 1307 01:11:52,647 --> 01:11:56,007 And thank you to everybody that has made it this far and, 1308 01:11:56,257 --> 01:11:57,507 followed the TinyBase journey. 1309 01:11:57,917 --> 01:12:00,307 It's been a privilege to work on this and share it with you all. 1310 01:12:00,831 --> 01:12:03,241 Thank you for listening to the local-first FM podcast. 1311 01:12:03,471 --> 01:12:06,981 If you've enjoyed this episode and haven't done so already, please subscribe and 1312 01:12:06,981 --> 01:12:08,541 leave a review wherever you're listening. 1313 01:12:08,911 --> 01:12:10,911 Please also share this episode with others. 1314 01:12:11,211 --> 01:12:14,011 Spreading the word about the podcast is a great way to 1315 01:12:14,011 --> 01:12:15,611 support it and to keep it going. 1316 01:12:16,081 --> 01:12:20,091 A special thanks again to Rocicorp and Expo for supporting this podcast. 1317 01:12:20,261 --> 01:12:21,051 See you next time.