Where people get together and jam!
Only 8 minutes late. No big deal. Welcome to the first episode of Jam Sessions. I'm Ryan Heffner, and I'm here with Daniel Colbourne. Daniel Colbourne is a software engineer.
Ryan:I'll just get software developer. I don't care for the engineer term.
Daniel:I'm the king of the engineers. I build bridges.
Ryan:Exactly. He has an agency called Thunk, and he's also a maintainer and a co creator of a package called Verbs, which is for Laravel. It's a bit sourcing package, and so we'll get into that today. My audio is not gonna sound great. I wasn't able to get the audio setup going, but who cares?
Ryan:We're just doing this thing. We're having fun. I'm gonna get the tweet for Daniel so he can tweet this thing out.
Daniel:I think I just found it.
Ryan:And we'll see if we start getting some people joining in on the stream. Yeah. Here
Ryan:we go.
Ryan:Oh, we got one.
Daniel:Oh, wait. That one might be me.
Ryan:Yeah. It could
Ryan:be me too. We'll see how this goes. But, yeah. I mean, it's actually no rush to get
Ryan:into this just yet. We can get we can Yeah.
Daniel:Sense Spend our time working around the edges.
Ryan:Yeah. Exactly.
Daniel:So where do you live?
Ryan:So yeah. So I yeah.
Daniel:I guess Oh, you said Atlanta. Atlanta. Right?
Ryan:We could do some backgrounds on ourselves. So yeah. I'm down in Atlanta. I've been down here for about 4 years now. And before that, I was up in, New York living out of Brooklyn.
Ryan:And, I mean, I guess, like, my background is I'm actually I'm I'm I'm like a dinosaur in the in the web building space, but, I got my start. I was originally born in Ohio, went to school there, worked at a software, like a document storage software.
Daniel:Nice.
Ryan:And then, happened to go on a trip with some friends to New York for the first time, and I emailed this one agency that I just really like their work. And they happen to reach out on the Monday before I flew back, and so I got into actually have an interview with them. And then that's what moved me to New York, and then I was there for about 13 or 14 years.
Daniel:Nice.
Ryan:And,
Ryan:I worked for that agency for a couple years, then I ended up bouncing over to Vimeo, the video hosting platform, and I was For sure. Director of engineering there. I'm actually, like, the video player team. So I wrote Nice. Rewrote, like, the original, like, Flash player Cool.
Ryan:For Vimeo and get the transition to HTML 5 and bunch of other stuff like that. And then after that, I basically left I was there for, like, six and a half years. And then, after that, I've just basically been bouncing around consulting and contracting with different agencies and companies and did a brief stint as a VP of innovation at this health care company where we are doing remote physical therapy. So we're using, TensorFlow's TensorFlow and, like, this, like, move net pose estimation model essentially track the body, and then it'll it allows you then track reps and other movements all via just webcams, and it's a web app.
Daniel:Nice. Yeah. That's cool. My wife is a occupational therapist. And
Ryan:Alright.
Daniel:In Asheville, there well, you're probably getting some storm related stuff going on there too.
Ryan:That's coming up for sure. My kids just had a half day of school. That's why they just ran down here. Yeah. Trying to bug me.
Daniel:There's, like, insane flooding going on here a lot of places. So Charlotte, didn't go to work today. And so I just heard her doing a OT telehealth in the other room where she's, like, trying to try to talk talk a kid into doing some exercises and stuff.
Ryan:Get the body in the right position or move the hands in a certain way. Who knows what they're doing?
Daniel:Yeah. Yeah.
Ryan:What they're doing. But, but, yeah, I mean, that's just a quick little background on me. And, I mean, I guess, through the course of my career, it's been a both like
Ryan:a blend of, front end to back end. Probably a
Ryan:little bit more heavier on the front end, especially when I was in the agency world, just doing a lot of, like, animations and and that kind of stuff. But then, obviously, just doing whatever it takes to get stuff to save and and and be read from databases and APIs and stuff. So a little bit of everything.
Daniel:Yeah. I I had sort of a similar thing. Like, I I also kind of did, like, a lot of, like, front end agency stuff. I, you know, I started in more of, like, the marketing side of things. So making a lot of WordPress sites and stuff like that and making a bunch of, like, front end things.
Daniel:I was big in the like, I was really deep in CSS, right, as the, like, CSS 3 transition went down. That was like that was like a a great era of CSS. Like, peak CSS tricks era. But, so yeah, I kind of came from that world a little bit and then transitioned into Laravel around or what year it was. It was right before 5.0 came out.
Daniel:So I wanna say it was, like, during, like, 4.2.
Ryan:So that was actually
Daniel:But,
Ryan:it's 2012 or something?
Ryan:Or are you are you No.
Daniel:No. It's later than that. It's later than that. It's, it would be 20 yeah. Yeah.
Daniel:This would be at least, like, 20 15. 14. 14 or 15. Yeah. Something like that.
Daniel:Okay. But, yeah. So I transitioned into Laravel around then, and I've worked at Titan for a little bit, which is sort of like a relatively well known shop in the laravel space and worked there for like 4 years, then I went off and worked at InterNACHI, which is like a it's like a trade organization for home inspectors, and they have, like, a monolith app that is that does, like, 30 different jobs. It's like an education platform. It, it interfaces with like a bunch of different government API's to do licensing and all kinds of stuff.
Daniel:But it's also like, home inspection software and scheduling and booking software and, you know, all sorts of other things. So I worked there, and, that's where I met Chris, who is my, like, co maintainer on Verbs. So he is the CEO over there, and he was my boss at the time. So when I ended up leaving, we are still good friends, and we make verbs together. And then, yeah, at that point, I just kind of went out and freelanced for a while.
Daniel:And then at some point, I had too much freelance work, and ended up starting an agency called Bunk with a couple of buddies of mine. And Bunk is still small. We're about, like, 4 people. We keep it real chill. We're trying to never be 10 people.
Daniel:Yeah. That's our rule. And so, yeah, we're just sort of a small, super targeted dev team. We do a lot of product work. Right?
Daniel:So John is, like, a PM, by training and by, like, career. So he worked at a bunch of various tech companies as a product manager. He started out in VC. And, during COVID, like, learned to code, and he and I built a game together in Laravel. So he's sort of like a senior PM, junior dev.
Daniel:And, yeah, it's a great team, and I really like it.
Ryan:That's cool.
Daniel:And we get to do you know, we have enough control over our own destiny to work on things like verbs or paper cuts, which is this little product we're building on the side and, you know, whatever we kinda feel like doing.
Ryan:Yeah. That's awesome. I mean, I think that blend of, like, open source product and then also balancing client work. Because I think it's like a it's like a perfect kind of, like, circle of life. You get enough exposure to the outside stuff with the client work that you get to, like, bump into stuff and find, like, the sharp edges that you don't get with potentially your own greenfield projects.
Ryan:And then that can then turn into ideas that you then wanna solve, both for yourself and for them and for others. So it's like, it's just I think having client work, even though it can be a little bit tedious, it's just such a great way to just expose yourself to the world and the and the frictions of it to find out ways to find out
Daniel:what's going on with the client. Framework and plugin developers who don't write applications. Right? And you use their thing and you're like, oh, this guy doesn't make stuff. Like, he just makes stuff to make stuff with.
Daniel:So, yeah, it's it's nice to have, like, real world applications for the stuff I make. And so I'm writing verbs 3 days a week for a client right now, and it gives me lots of opportunities to run into problems.
Ryan:Totally. Yeah. The, sorry. I just got I got
Ryan:my buddy telling me I need to turn up my mic.
Daniel:All good.
Ryan:Unfortunately, yeah, I'm dealing with dealing with some stuff that let's see here. Let's crank that up and see if that helps out a little bit.
Ryan:But, yeah. No. That's yeah.
Ryan:When especially if people aren't, like, actually, like, dogfooding the stuff that they actually make, you know, and are building stuff like that, the inner like, it you just can tell it just wasn't really thought through as far as, like, how you use it within your stuff. You know?
Daniel:Totally. Yeah. It's nice too because John is, John's got John's building Papercuts, which is this, like, product management app that he's building. And it's cool because it's fully verbs top to bottom, like everything is verbs, and we just force him to work off DevMain. So, like, we don't, like, let him if we don't let him, like, use a tech version, so he's he's running into the new problems every time you release something.
Daniel:We're like, sorry. You gotta fix it. You know?
Ryan:You need someone that's
Ryan:out there
Ryan:like the and I think I just got my mic working. So
Ryan:Oh, yeah.
Ryan:You're a good mic. Yeah. I mean, you need someone out there basically taking the spear.
Daniel:Yeah, exactly.
Ryan:And especially depending on how much test coverage you have, early on, especially like that can vary. And I mean, for my stuff, other than like, actually like NPM packages or something that I'm publishing, my test coverage for my own personal apps and stuff is pretty much nonexistent. It's just like, oh,
Daniel:I'm I'm guessing. I test everything.
Ryan:Do you do TDD? Yeah.
Daniel:I just I don't know how to write code without writing tests. Like, I'm like I mean, for, like, front end stuff, it's a little different. Right? Like, I don't like test components, really. But for backing and stuff, it's like, I don't even know how I would know that I was done with the future if the test didn't exist to tell me.
Daniel:You know?
Ryan:Yeah. Totally. Well, I mean, I guess it also just depends on the complexity and, like, what the app you're building. Because sometimes, especially if you have a pretty straightforward, like crud app,
Daniel:when
Ryan:you're doing your test, you're like running testing into your RM or something, you know, like,
Daniel:yeah, sure. That kind of
Ryan:wild, obviously, if you have a lot of, a lot of, you know, integrations and stuff, like, you might need to do some of verifications that you're at least, like, hitting those things and something should send, blocking that kind of stuff up. But yeah.
Daniel:Yeah. Yeah. I just I don't know. I kinda came up in the world of just, like, make a test and make a pass. Very, like I'm not, like, strict TV where, like, you know, you have to write out all of your, like, assertions ahead of time and, like, know what you're asserting and make them all pass.
Daniel:But I like, when I'm writing a feature, the way I'm the feedback loop I'm in is, like, running a test over and over and over again. Right? And, like, that's how I'm testing that this class returns what I expected to return. That's how I'm testing that, you know, whatever. Like, I see a lot of people using, like, either, like, logging, like, in the console or using, whatever, like, what's the TinkerWell, right, which is like the sort of like buy for Laravel or whatever.
Daniel:I just do all the same stuff that they're doing in there, but I just do it like in a PHPUnit test. And then, by the time I've, like, gotten the thing to do what I wanted to do, I just, like, write a couple of assertions that assert that it does what I wanted to do, save the test. Now I know that feature is done, and I can push it up. You know?
Ryan:Totally. Yeah. Yeah. Yeah. Yeah.
Ryan:And actually, it Internet's just kinda interesting too because early in my, when I was at that software company in out of Cleveland, I was actually doing, like, e learning development. So I was like Yeah. Building courses and, managing, you know, the submission of answers and stuff, and that stuff just gets so tedious. And that kind of is my transition into verbs and event sourcing and being able to Yeah. Kind of create some, you know, some sort of normality in the chaos that is, managing all those events that are kinda coming through.
Ryan:So can you give us a little background on verbs and what your take is on it, like, compared to, like, traditional event sourcing, and and where do you feel like it kinda fits in, and what makes it better?
Daniel:Yeah. So yeah. So, I mean, speaking of InterNACHI and, like, e learning, like, that that was kind of where Verbs was born was, the Exam System InterNACHI. Right? So they do licensing for home inspectors, a bunch of different governments use those exams to determine whether or not people are licensed home inspectors.
Daniel:And, you know, if someone inspects a house, and then the house collapses the next month and there's a lawsuit, like, someone needs to be able to ask the question, like, was this person a licensed home inspector at this day? Totally. In this state. And so it's like pretty, like, high stakes data that needs to be kept around for a long time, and it needs to be accurate, and we don't want people cheating on the exams and all this other stuff. So, the exam system, like, used to live in this kind of separate procedural PHP legacy app, and, we wanted to bring it into Laravel.
Daniel:And, we had to migrate a bunch of old data, so we sort of wrote this event source system for the new app in Laravel. And then we wrote some scripts that took a bunch of the old data from the legacy system generated fake historical events from that data.
Ryan:Yeah. So like created ad and updated ad, whatever. Yeah. Exactly.
Daniel:They were you know, it was like you have to make some guesses in some places and you have to do some stuff. But we wrote some scripts and it worked. And so then we just generated like a ton of historical event data from the existing, legacy data. Now we just ran like a huge
Ryan:replay essentially. Yeah.
Daniel:Of all of that data slowly over time. Right? And, eventually when it was like in parity, we just had events sort of firing into both systems. Right?
Ryan:Mhmm.
Daniel:And, it was really cool. I gave a talk on this at Laracon EU if you wanna see, like, the whole story of that. But,
Ryan:actually, were you able to, were you able to capture new events while old events were still catching up, or did you have oh, yeah. We were. Rad. Cool.
Daniel:Yeah. And so by the end of it, we actually had it where you could start an exam in the old system and then log into the new system and finish the same exam.
Ryan:Oh, that's pretty rad.
Daniel:Yeah. The data was, like, reflected in both places.
Ryan:Yeah. It
Daniel:was really cool. And so the and like so when we sunset it, we did, like, a 5% deploy, 10% deploy, 50% deploy, 100% deploy. And the data was, like, it's fine to do that. And, like, if if we ever have a problem, we could just roll it all back and use the old system and the data's fine. Yeah.
Ryan:Yeah. Yeah. And the old system was basically still creating the event, so it's kind of a win win on both. Right? Yeah.
Ryan:Yeah.
Daniel:Exactly. Very cool.
Ryan:So, yeah, that
Daniel:that was really fun. But so we used a different we used someone else's package for that. And, we sort of did event sourcing was sort of the traditional way.
Ryan:Was that the sponsor package? Or is it a different one?
Daniel:They're much more they're much more sort of like
Ryan:they use
Daniel:a lot of the verbiage the DDD verbiage, and, like, there's a lot of words that we had to learn. Like Yeah.
Ryan:What was it? Projectors and
Daniel:Yeah. You got projections, projections, reactors, reactors, aggregate roots. You got, you know, there's this concept, they're a little hairy,
Ryan:to
Daniel:wrap your head around. And so when we built verbs, there was like, there were 3 main things we wanted to do with verbs that, like, we couldn't do in that package that like, made us decide it was like worth having a new package, right? And so the first one was, when you fire an event, if you want multiple states, which Spotsy calls aggregate roots, for example, say you add a user to a team, right? And you want the user state to know about that, and you want the team state to know about that. Right?
Daniel:Mhmm. In verbs, you're able to basically have a single event apply to multiple aggregates.
Ryan:Oh, let's see. Aggregates. Okay. Cool.
Daniel:Multiple states in in because we call them states and verbs. Yeah. But in the SPASI package, you had to fire, like, a user joined team.
Ryan:Yeah. Like, names based
Daniel:on the fire, like, a team had user join.
Ryan:Yeah. Yeah.
Daniel:Right? And so you have to fire 2 separate events, 1 to notify each aggregate. That was like super cumbersome, and we hated doing that. And then the other thing that was super annoying was, the there's this thing called an aggregate version mismatch error that we used to run into all the time, which is like, if you if you like have a copy of a specific aggregate checked out, right, so like, I have whatever the user aggregate, which is a piece sorry, for the for the uninitiated, like an aggregate is kind of like a it's an object that's built up. It's like built up.
Ryan:Built up. It's like
Ryan:built up. Of the events. Yeah. Based on, like, the payloads of the of the events that are coming through?
Daniel:Correct. Yeah. And so, like, you apply all of the events from the beginning of time using, like, a specific it's kind of I mean, it's like a reducer.
Ryan:Reducer. Right? Yeah. Yeah. Mhmm.
Daniel:So it's like a reducer that sort of takes all of the events and like compiles them down to some piece of data, right? And so often that's like an object. And so in this case, it's like, say we have a user aggregate that was compiled from all of the events up through event number 100, right? And then, somewhere else in my app, I go retrieve the user aggregate, and a few new events have been fired. Right?
Daniel:Mhmm. And then I now have 2 copies
Ryan:of that
Daniel:aggregate in memory at the same time. And if I save 1, I won't be able to save the other because the one wrote something in, and now the second one won't say because, like, oh, there's been new stuff written since I was retrieved from the database. And so it just sucked that, like, you could actually, like, step on your own feet and, like, retrieve multiple copies of the same thing and then block yourself from saving them.
Ryan:Yeah. Like, what's the recovery avoid that. What's the recovery there? Do you just have to, like, manually resolve 1?
Daniel:Well, so there's a few things. Right? So, like, if it's like multiple requests, if it's like you're doing something and I'm doing something, one of us gets there first, the other one, we have to deal with that problem. Totally. That's like a different thing.
Daniel:I'm talking about like, just as a programmer.
Ryan:Yeah, that's what I'm saying. Yeah, I get another class. Yeah, exactly. Yeah.
Daniel:And so the thing that, the thing that we ended up doing was basically saying like every state in Verbs is a singleton, and so there's only one copy of the user state for user number 1 ever during this request. And so if I retrieve it, now it's put in memory in the like in this little repository of in memory states that we have. And then if I retrieve it again, first we check whether we already have a copy in memory. And if we do, we just give you that copy. And that way, you don't have like 2 parallel copies that have different events on them.
Daniel:It's just like any event that gets added to this thing is added to the same copy, And then we just commit all of those events at the end of the request. Right? Yeah. And so that was huge for us, too, is just like getting rid of this whole like, there's just so much like passing the same state object around, so that you can x
Ryan:You create it in my controller, then you gotta pass it through to some services or something, and then hand it off to, who knows, maybe, like, a weird queue job that's also gonna run really fast. And then who knows what goes on. So you're passing
Daniel:around like this copy of the state that, it's just cumbersome. And it's, you know, it's hard to, it's unwieldy.
Ryan:Yeah, totally.
Daniel:It makes all of your APIs feel very gross. And so we just adding things as a singleton and sort of getting rid of this whole, like, aggregate version mismatching, at least within a single thread.
Ryan:Yeah. I guess when I talked about the Q thing, that was outside the bounds of of, where the memory would come in. But
Daniel:that and then the, the other thing of like being able to apply one event to multiple states like those 2 things were like, Okay, this is enough of like an opinion that we want to go like actually push this and build a really opinionated framework. Yeah. Because also I would say that's the big difference with verbs is it's very opinionated.
Ryan:Yeah. But, also, you know, streamlines a lot of stuff. Because, I mean, coming up with those obscure event names and managing those across the system and what what they mean when. I don't know. It's just so much extra work to go.
Daniel:I I think it's opinionated for good reason. Right? Like, I I think the opinions are good, but it is like it's a departure from like, I would say that event sourcing is like traditionally been something that is most heavily championed by the DDD world, and those sort of like domain driven design guys, and they have a bunch of jargon over there, and they're pretty like dogmatic over there. And so what that leads to is that, like, as soon as you touch event sourcing, and I I've
Ryan:referred to it as event sourcing as opposed to applying your own name. Like, you should just call it verbs at this point. It's like, try to avoid the event sourcing. Yeah.
Daniel:Yeah. But, yeah, like, as soon as you, like, express on Twitter that you are interested in event sourcing, like, a Dutch guy shows up to tell you that you're doing it wrong. Right? Like, within 30 seconds, you know, someone shows up and says like, well, actually, you should be doing CQRS and like, actually, you know, like, whatever. And so by Verbs is like an opinionated project in that we explicitly named things in ways that, didn't, like, respect the DDD tradition of naming.
Daniel:Mhmm. And part of that was to just, like, throw them off a little bit, so that it's harder for them to just yell at you because Yeah. They're not exactly person. Yeah, they're like, wait, is this state like an aggregate route or is it not an aggregate route? And I'm like, I don't know.
Daniel:You tell me. Like, you know, it's like, if you don't like, if you don't understand what I'm doing deeply enough to criticize it, then, like, maybe we can get rid of a lot of that, like, surface level rock throwing, you you know? Mhmm. Yeah. And, and ask you to like, well, I don't know, why don't you try it?
Daniel:And then, like, and then we can talk about it and jump in the Discord. And we've had, like, we've had some guys, like, hop in the Discord and just start going off. And, it's always a good time.
Ryan:We figure out how to cool them down. You know? Like Yeah.
Daniel:Well, no. We, we did have a role in Discord one time that we made for 1 guy, which was just a it was a DDD concern troll. And then we, we made a channel called Verbs Concerns, and you could only join it if you were if you had this role in Discord. If we only give it to 1 guy ever. Because there's 1 channel He's
Ryan:got his own channel there. Yeah. Amazing. Yeah. That's awesome.
Ryan:And then I guess, like, explain a little bit about the setup. So obviously, you said that there's events and there's a state. When you're implementing verbs, what does it kinda do into, like, behind the scenes in the app to kinda wire all that stuff up for you?
Daniel:Yeah. So you've got, events, which are just classes. Right? So you fire an event, which means, you know, you instantiate this event with a bunch of data, and then you stick it on a queue of events that will be committed at some point.
Ryan:Did you have control of that, like via some like hooks or something? So essentially like when the initial request comes in, you can kinda start queuing it up and then just commit it all at the end or something?
Daniel:Yeah. Exactly. Well, yeah. So we I mean, in the API, it just looks like you say, like, you know, user registered colon colon fire, and then you pass the data into fire that you need to pass into the event. And then what that does is creates a new event and puts that event on the docket, you know?
Ryan:Gotcha.
Daniel:And then at some point, you can call verbs, colon, colon, commit, which you actually don't have to manually call because it gets called at the end of every request automatically. Cool. But if you want to commit earlier for some reason, you can call it. And so when verbs when verbs commit gets called, we basically take that queue of events that have been fired, and we flush it and basically like execute all of the handle methods, on all of those things. And so that's going to write models to the database, send emails, sort of any like, any not in memory effects that that event would have.
Daniel:Gotcha.
Ryan:And
Daniel:so then there are in memory effects that it's going to have. And so states are those sort of like aggregated objects aggregated from event data, right? Mhmm. And so we apply data to states immediately as soon as you fire, But we don't commit it, or we don't commit those events and then thus, like, write any, like, permanent effects to the database or send emails or any of that stuff.
Ryan:Gotcha. So you have that that like, basically, a collection of singletons that are getting their states updated. And then at the end of that request, you commit all those?
Daniel:Exactly. And that way, it's like so we're updating these states in memory, but, like, if something goes wrong, like, we just don't commit any of those events. Yeah.
Ryan:And and at the same time, you can always reference that latest state that was influenced by that event without having to figure out some sort of weird middle system of, like, what's the new memory version of this thing versus, like, trying to, like, do additional reads and stuff. That's cool.
Daniel:Yeah. And, you know, there there are, like, really cool like, the reason we did the singleton thing was to avoid these, like, version mismatch problems, but there are just, like, a ton of awesome side effects of it. Like, for example, do you use Livewire at all?
Ryan:No, I haven't. But I need to go I need to try it out.
Daniel:It rules. It's like the best way to build web apps. But, Yeah. No controllers, right? No controllers.
Daniel:Yeah. So if you're using Livewire and you've got a property on your Livewire component that is a verb state, right? So say I have like a user state as a property on my LiveWire component, I can fire an event, or even better, like I can like call a function on a class that calls another function on a class that calls another function on a class, and like 3 levels deep in this like call stack, an event gets fired, which updates the state, and the instance, which is a property on my LiveWire app, is the same instance as that one that got edited, right? Which means that all of the values in my views are immediately updated as well. Right?
Daniel:So I don't need to, like, I don't need to, like, re fetch that data from the database. Like, it's total optimistic UI. Right? Where it's like, oh, I know or it's back end optimistic, you know? Yeah, yeah.
Daniel:But like, I know, you know, because like, there's this is interesting thing about optimistic UI, because there's like, there's 2 levels of optimistic UI, right? It's like, does the front end know what the back end is going to do? Right? Mhmm. And then there's like, does the back end application know what the database is going to have?
Daniel:Right? Yeah. And so that's, that's what it is here is that, like, the back end application knows what the database is gonna have. Even before these events have been committed and any models have been written, this in memory thing has been updated, and so I can update my views based on that. It's really cool.
Ryan:That's wild that that that's how it works because so, basically, you have LiveWire submitting these calling these methods and basically updating that that singleton state. And then that somehow then well, I guess, LiveWire well, it return it basically returns as DOM. Right? And then it and then it just in an email. And then DOM merge into, or HTML, and then it gets DOM merged into, the current state.
Ryan:So you can actually have, like, an in memory singleton across requests like that.
Daniel:So well, so LiveWire passes state up and down between requests.
Ryan:Uh-huh.
Daniel:And so you can rehydrate the JSON representation of that state object.
Ryan:Gotcha. Gotcha.
Daniel:Yeah. So you could have the state, like say the state only has like 2 properties and it's like 2 integers or whatever, right? That's going to compile down to like a JSON object with 2 props, Right? And so when it gets pushed back up, it'll get rehydrated into an instance of that state class. Right?
Daniel:Yeah. And then we fire an event, that instance is going to get updated. Now my views, which are bound to that data, are also going to update.
Ryan:Update. Yeah. Yeah. Wow.
Daniel:And then after the response has been returned, then we're going to commit those, events to the database and, like, send off any emails or do any side effects stuff.
Ryan:Totally. Yeah. So, essentially, you can work with that. Like, everything that kinda gets handed off is all in memory. And then then the work begins once that kind of commit hook kicks in and starts to fire fire things off.
Ryan:That's cool. Actually, there's a question from, Len Woodward.
Daniel:Hey. We love Len Woodward.
Ryan:Is there any practical reason to ever use data off your model when the state is just so convenient?
Daniel:Yes. Totally. There is. So I when I start an app, I don't make models. Like, I just like I'm like, I'll make eloquent models eventually.
Daniel:But, like, right now, I wanna move fast, so I'm just gonna make events and states, and then I will let necessity tell me what models I need, right? And inevitably, I end up making them. And the reason I end up making them is because querying is so good once you have database tables, right? And so at some point, you need to make a table with filters or something, right? And when you make a table with filters, you really want to have a database table that you can query and you want to be able to write some SQL to get the correct things back.
Daniel:Because obviously, you could make a bunch of states and just like put like arrays of IDs on those states and then write some crazy nested built mapping filtering stuff to find the correct things. But it's just going to be slower and worse and harder to maintain. Like, databases exist for a reason. Yeah. They're a good solution to a good problem.
Daniel:But, for individual records, like if I'm dealing with like, I don't know, one task in a task manager, right? A lot of the time, I'm just mapping, I'm just dealing directly with the state for that task. And I'm not even loading the model out of the database for that page. Right? Yeah.
Daniel:Because it's like I don't get anything extra by making this extra database request. You know?
Ryan:Yeah. Totally. I mean, actually, that brings me to a project that I was kinda playing around with is this idea of, like, this non schema relational database where, essentially, you kinda have, like, an entry. An entry has an ID, has a type. You know, it has some basic, your created at, updated at, whatever, and then it just would have data.
Ryan:And then, also, there would be Yep. You know, relations to that entry where you could basically make, like, a named relation, and that relation could either be an entry or a collection. And a collection is just yet another either array of collections array. I don't know if you if you've ever used, like, Contentful, essentially, and you're sitting there trying to, like, map out, weird, you know, mapping out all the JSON scheme as they go with these models and stuff. What you end up realizing is just like there's really, like, 3 things that you're working with.
Ryan:You're working with, like, entries, relationships, and collections. And if there's just, like, an easy way to kinda compose those but yet dictate that from almost, like, the front end side of the code, you could still get all the benefits of a database by being able to query by relationships, you know, have the fast lookups by IDs, but then not really have to, like, manage the actual underlying data that's kinda floating around with those. You know?
Daniel:Yeah.
Ryan:It it get it gets a little hairy when you start wanting to do, like, deep searching on stuff. You have some stuff in, like, a JSON thing, and I know there's some limitations there. So essentially right. At least I think so with, like, Postgres. There's only a certain number of, like, columns that you can expand that data out to if you actually wanted to do, like, deep searching, if I'm not mistaken.
Ryan:I think there's some sort of
Daniel:Well, I mean, it's it's also just, like, really useful to be able to do, like, join based filtering or whatever. Right?
Ryan:Exactly. So like,
Daniel:hey, I want to see all of the, tickets that are in progress that are created by an author who is an admin. Right? Yeah. Like, that is an impossible thing to ask a JSON array. You know?
Ryan:Totally. Well, yeah, it wouldn't be a JSON array. It would be, it would be basically all a bunch of pivot tables. And then and then but you would have to be based off the type. You know?
Ryan:So there would you'd have, like, type of author or something like that
Daniel:or Yeah. Yeah.
Ryan:Committer or something. And then but you yeah. You would have to you would have to know so much about it. It I mean, it kinda starts to blur the line between, like, GraphQL and, like, your traditional kind of, like, ORM thing. Mhmm.
Ryan:Yeah. It gets wild. It's one of those things where I, like, I can see it, and I'd love the the, like, flexibility of being able to use something like that because then you don't you just start writing the the real guts. That stuff that people actually interact with. And I guess like that with the first table.
Ryan:You know? Exactly.
Daniel:Like, the events table is just like ID type data, and the state snapshots table is just ID type data. Right? Yeah. And so both of those are like, they just exist. There's a bunch of code that we we've written some code for like interrelating states and stuff like this.
Daniel:We're going to write more code for like for like relationship detection and stuff like that between them. It's not that important because in sorry, it's not that urgent rather because I think like most of the time, the move there is to just fall back to, like, eloquent models and database table like relational database stuff at the time that you need it. Mhmm. But, like, it does push back the point in my prototyping where I have to do that. If I like, if I have better relationship management on states, then I can, like, push back how far into prototyping I can get before I have to start writing Alcoa models.
Daniel:You know?
Ryan:Yeah. Totally. Essentially, like, you could basically have, like, post created and then append, who knows, an attachment or something like that. You know, like and if you were to basically associate that attachment with that original created post but then have it all within memory, there'd be you could almost kind of scaffold out these kind of dynamic states where
Daniel:Yeah. Totally. And I mean, like, I I can't stress this enough. Like, my it's hard to talk about outsourcing because, like, all of the common, arguments for it are like, Oh, it's really safe, or Oh, it's really flexible, or like, Oh, you're, you can do replays. All these things are cool, and I definitely think that they're cool.
Daniel:That is not the reason to prevent sourcing to me. Like, to me, the reason for event sourcing is like event like modeling the world after what happened is so much easier Yeah. Than doing the mental abstraction of like turning what happened into past We
Ryan:get here.
Daniel:Noun based tables. Right? Yeah. And the reason it's called verbs is it's literally just like, well, store what happened, store the verbs, and then like, you can extrapolate the nouns later. Like, don't worry about what nouns there are and what properties of those nouns have changed right now.
Daniel:Like, just put a bunch of verbs in your database, and, like, the nouns are, like, they'll happen. You know?
Ryan:Yeah. I mean, the beauty with that is also it's like, yeah, you can have your models get updated and and then basically almost have, like, an audit log of of the events that occurred to basically accumulate this this one state, the current state of the model. But what's also interesting is, like, I think about it when it comes to, like, reporting. You know? How long did it take for this status to go from, you know, registered to approved or something or or, you know, any of these other things that to draw that line, and then you'd have to have all these kind of, like, registered ad, approved at weird fields on your model if you're trying to do it on one flat surface versus actually having the events to come in, and you could see exactly when that happened and the duration that it took without having to kinda overload your model with all this kinda frivolous data.
Ryan:You know, it's,
Daniel:And you're not asking developers to know how this data is going to be used in the future.
Ryan:In the
Ryan:future. Yeah. They store it. And that's what I'm about. Like, totally reporting.
Ryan:It's like
Daniel:Yeah, exactly. I don't know what questions you're gonna wanna ask this data in a year and a half.
Ryan:Exactly. Yeah.
Daniel:So like the the cool thing about like, for example, this is an example I always use is like what happens when, like, you hire a new marketing guy, and the new marketing guy comes in and says, like, hey, what was our churn between August October of 2022? And you're like, I don't know. Like, what what do you mean? Well, hopefully,
Ryan:you had some third party service set up that was, like, monitoring that. Like,
Daniel:if you event sourced all of your, subscription events, like, even, like and this is, if you're going to event source, like one thing as like a test, just like start storing some events, don't even do anything with them. Right? But like, as like your first dipping your toe into event sourcing with an app, I would say just like start storing some events on billing. Right? So like, anytime someone swipes a credit card, you know, ups, like upgrades a subscription, gets a free trial, turns a free trial into a paid subscription, cancels the subscription downgrades the subscription, right?
Daniel:Just store that event in the database, don't even do anything with it. Right? And then, maybe maybe that's like a good opportunity for you to like, send Slack notifications, or something from inside your event. That'll that'll get your feet wet with like, Oh, these events have listeners, and I can do some Put
Ryan:some webhooks with that data or whatever, like Yeah,
Daniel:yeah, whatever you want to do. The key is like now you are storing data about the most important part of your business, which is like the money in money out stuff. Right? Mhmm. And, when you, a year from now, wanna know like, Oh, like I just found out that this guy made this YouTube video about us 2 years ago, and I wonder if that drove any subscriptions.
Daniel:Yeah. Yeah. And you're like, I don't know, let's pull a report, you know, and like, you do it and all of a sudden you find out like, there are like a ton of new French subscribers, the our number of French subscribers, like spikes right here, right, based on the piece that we're storing with these events, and like, you know, all of this stuff. And we all of a sudden see like, oh, yeah, this French YouTuber who made this video about us in 2022, like, totally did spike our numbers or whatever.
Ryan:Totally. And then you start laying on layering on other stuff, like when something was tweeted, when a certain blog post went up, or you did, like, a redesign or something, and you can start correlating these things to actual events that are happening within your system and just it really opens up the floodgates there. Yeah.
Daniel:Well and then when you start looking at, like, services like Baremetrics or Mixpanel or any of these kind of, like, event driven analytics, like app analytics
Ryan:platforms Text management or whatever. Yeah.
Daniel:Yeah. Like, they're just they're doing the same thing, right? Like, you're just firing events and sending them to them, and then they're aggregating them. Right? Yeah.
Daniel:Like, but basically what they are is like, good defaults for what questions to ask your events, you know? But like, if you like, I've helped people integrate barometrics and stuff into existing apps. And all it is is like, you're just fire you're doing exactly what I'm saying about, like, the billing stuff. But, like, rather than firing a verbs event, you're firing, like, a baremetrics event. Right?
Ryan:Yeah. Or baremetrics. I was actually consuming the Stripe webhook that is then
Daniel:Yeah. Yeah. Yeah. Right.
Ryan:Accumulating those things. And so like, what I would
Daniel:always say is like, hey, if you're gonna like fire off an event to Mixpanel or baremetrics or something from your app, why don't we just fire a verbs event that then fires the Mixpanel
Ryan:event? Totally.
Daniel:And then now you have your own copy of that event, like stored with all of that data in case you ever wanna get rid of them or ask a question that they don't support or do what you know? Like, now you get your own event stream.
Ryan:I even think on the opposite side too to have, basically, a verbs event set up to a webhook and then being able to capture the payload of that webhook. And essentially
Ryan:Totally.
Ryan:For whatever reason, your app goes down or there was some bad code in something or you weren't you're pulling the wrong property off of something weird because you didn't understand the payload that was coming through or the or the payload somehow differs, you know, between hooks that you just you weren't aware of. The observability of some of that webhook stuff can be tricky, especially depending on how much you're getting, you know, depending on how, like, noisy it is and or how reliable it is. But to then have your own snapshot of that without relying on them to somehow figure out a way of retriggering those webhooks to repopulate your app, you you still have the history. So to be able to have that verbs table set up to just capture those would be huge.
Daniel:That would be sick, dude. I should make a I should just make an open source repo that is a Like a webhook consumer? Just a webhook consumer.
Ryan:Like a Stripe
Daniel:is all
Ryan:it is.
Ryan:Actually. I mean, that Yeah. I really wanted to narrow it down.
Daniel:And literally, you just spin it up on a separate server. Right? So that like if your app happens to go down for 24 hours, you don't miss all of these webhooks. And then you could basically just, like, go in there and say, like, hey, replay all the webhooks from here to here to this URL. Yeah.
Daniel:And so then, like, you could proxy all those webhooks back to your own app
Ryan:if your app is going down there. Fire them against your app. Totally. I mean, I've actually I I think, I think Honeybadger may have something similar to that, like, HookRelay or something something similar. But, yeah.
Ryan:Or I think, actually, just being able to do that against any webhook that you're not really ready to, like, consume the actual webhook. Like, you're not ready to to do all the processing after it, but just to queue it up and get it early. You know? You have newsletter subscribers that are going into resend to an audience, but you're not ready to you don't have a welcome email set ready yet for them or whatever.
Daniel:Yeah. Yeah.
Ryan:But you wanna have that automated, but, you know, it's not in your you don't have a design that's not in your repo yet. So to basically be able to add that, run all those existing ones, and then for every net new one, be able to have that fired out. That'd be those little types of things would be super valuable.
Daniel:Yeah. And so this is what I like, I think it's easier for people to imagine this in terms of like, 3rd party apps, because like, or 3rd party, whatever, integrations, like, those are things I don't control. Right? And I feel like, oh, well inside of my app, I do control things. And so I don't need to worry about those same concerns like inside the four walls of my own code base.
Daniel:Right? Yeah. But I would argue, like, actually, you do, right? Because like, even if the event isn't like a webhook from the outside, it's just like someone doing website activity on your website, right? It's like, okay, well, but you might not yet have like, in the same way, you're like, Oh, I don't have like a welcome email ready for these, like new ConvertKit subscribers or whatever, right?
Ryan:Yeah.
Daniel:Well, you also might not have some other feature ready for like, new, you know, users who create an account on your website. Right?
Ryan:Totally. Yep.
Daniel:And so, like, you can just capture your own data as events, and then build the functionality for those events later.
Ryan:Oh, yeah. It comes both sides
Daniel:get all of the benefits.
Ryan:I mean, I could easily see especially when it, this is, like, a common thing that happens. Like, you typically an app starts out, and they have a user's table. And then they wanna somehow introduce, like, a layer of, like, accounts or account members. But then spawning all those account members and understanding who might be the owner and whoever else is so tedious. But if you actually have the events
Daniel:such a common one.
Ryan:And you know that, like, this person created this, and you could then replay that history in the order that it happened. And you can make some basic assumptions about, like, who to apply as the owner and then who to add as additional members and whatever else. You know, all those weird refactoring things where, to your point, the noun didn't exist, but now it does. Now it takes talent and the capital is
Daniel:common one is, like, plans, right? So like
Ryan:a lot
Daniel:of times, like in like the MVP of the SaaS, you're either like subscribed or you're not, right? You either are paying me or you're not paying me. There's one price.
Ryan:Yeah. You know? Or maybe it's an ID.
Daniel:So I don't
Ryan:know. Yeah. Yeah.
Daniel:But like, you know, you're paying me, but at some point, like, I need to go to like, Oh, well, now I have like my cheap plan and my expensive plan and my enterprise plan and my free plan, you know? Yeah. It's like, okay, well, I have all these people who just like are paying me. Do I like create like a legacy plan to put these people on? Do and then like, I have to write some script that like migrates all these people onto the legacy plan and like, whatever, whatever, right?
Daniel:If you had just, captured an event like a user subscribed event at some point, right? You can and so what happens in your database is like user subscribed, and then we like set like user is subscribed to true, right? Yeah. Well, at some point, like, you're gonna delete that user is subscribed column, and you're gonna instead, add like a plan ID column, right? Yeah.
Daniel:Like, you can just change what happens in the handler of that event, replay the events, and like now, all of our user are on the correct plan, and you don't have to do, like, a big migration script or whatever to, like, fix all of those users.
Ryan:Yeah. Or the next time you end up refactoring the plan ID out because you actually want, like, a subscription object that can have the core plan as well as, like, the number of seats or whatever else. You can basically just start running that thing in. And to your point, you don't have to do this day 0. You know, you like, you can kind of reconstruct those events up until you've basically had this epiphany of creating this, like, subscription ID.
Ryan:And this is why you go back and regen. You know?
Daniel:This is why it's so this is what what I'm talking about when I say, like, this is, like, for me, using like Livewire and Burbs in tandem is like far and away the most like productive stack for prototyping, like in the universe. Because like, I'm just out here hacking and slashing being like, I don't know, we'll figure it out later. Doesn't matter. Got the events, who cares? You know, so I'm just out here like, firing off events, verbs, verbs all the way, you know, and then like, at some point in the future, I'll be like, oh, we should really make like, you know, plans with team subscriptions and all this stuff, you know, all this crazy stuff, or like, you know, Caleb just did this whole thing with, with flux where he did this huge launch, took a bunch of pre orders, for $99 now has now has like different 3 different tiers of pricing.
Ryan:Some Team pricing, some of that's like unlimited, right, or forever, like Yeah.
Daniel:Right. Exactly. Imagine imagine those were just events. It was just like Yeah. Fortunately, there was only one type of thing.
Daniel:It was a preorder. And so, like, it basically is an event, you know?
Ryan:Yeah.
Daniel:Yeah. But, like, imagine that there had been, like, 3 different types of preorders or whatever. Like, having events for that stuff would be, like, insanely valuable to just, like, reproject and re architect your whole thing, you know?
Ryan:Totally. Yeah. Even if it was all just mapping to a singular subscription or do you know, just doing one thing, but you had all that data when it came in and then to be reapplied. So, actually, yeah, we're at the hour mark. I'm not sure if you wanna keep on jamming.
Daniel:Mind. Yeah. I can go a little more.
Ryan:No. But, I mean, I I I don't know. I'm I'm totally in on on the event sourcing thing, and I I definitely need to spin up a Laravel app and, get something brewing on it. Yeah.
Daniel:The thing you were talking about, about, like, sort of the differences between activity logs and event sourcing, to me and this is again, like, this is just, like, bad marketing or communication on my part or on the part of event sourcing as a whole, but, like, I'm not doing a good enough job, like, explaining the the value of event sourcing because people come up to me constantly and are like, oh, I need an activity log, therefore I need event sourcing.
Ryan:Yeah. That could be totally different. I mean, that's just a point in time, the activity log. But
Daniel:Well and it it is like that is something you can do with event sourcing. Like, that's Totally. That is a feature you can build with event sourcing. But, like, you could build an activity log without event sourcing. Exactly.
Daniel:Yeah. And a log is just that. It's a log. It's an after the fact record of what happened. Mhmm.
Daniel:Right?
Ryan:After the log is gonna have potentially relationships that you would want to use to then show the all the events for this account or this user or the like, on this project that you wouldn't necessarily be trying to drill into, like, your events to then recreate said log in real time. You probably want those to be, like, discrete things that are just kind of actually have, like, real relationships, I'm guessing.
Daniel:Well, so I the way I do logging is actually, like, all state based with events with events. Like, the the way I do logging is all state based. And, like, when an event happens, I have, like, how is this event represented as a string in a log, you know, whatever. I've got like stuff like that. Like, I've got like interfaces and traits on my events that say like, this is a logable event and like, here's how it's represented when it's presented to an admin, here's how it's represented when it's presented to the user.
Daniel:Like, here's, like, a view that you can use to render this into a into a template if you wanted to, like, render this as a log entry in a template that has, like, clickable links or
Ryan:Oh, but you can do that with strictly with, like, the actual event that's logged within verbs.
Daniel:Exactly. Yeah.
Ryan:Oh, so Yeah. We we've
Daniel:got this is a totally undocumented but public repo called verbs history. That's like a verbs history plug in, and it's undocumented because, like, I wanna keep breaking it. And so I'm not willing
Ryan:to write docs.
Daniel:Yeah. I'm not willing to write docs because as soon as I write docs, people are gonna start using it and yelling at me if I break it.
Ryan:Totally. But
Daniel:it's public and you can use it and just, like, suck it up if it breaks. But it's, it's really cool and fun. I really like doing it that way. But, yeah, the point of an activity log is like, the reason to use event sourcing versus an activity log. And the primary difference is the events come first, then the changes.
Ryan:Right?
Daniel:Exactly. Whereas in an activity log, the changes come first and then the log, right? Yeah. And, you can't reconstitute the changes from the log, right? Yeah.
Daniel:Whereas in in an event system, like, you can reconstitute the changes from the event stream.
Ryan:Yeah. I think actually, we yeah. We were talking earlier, and, yeah, essentially, the event is a ticket. Right? The the receipt could be the log that happened because of the that event occurring, but it's not a to your point, it's not guaranteed that there is gonna be an activity log.
Ryan:Like, that the the payload in that might not have validated, or it could've who knows what could've happened that it could've got rejected or or not even never even written as an event, potentially.
Daniel:If the event so you can fire an event, and then it cannot end up validating. And if it doesn't end up validating, it will not end up stored in your event stream.
Ryan:Right? Gotcha. Yeah.
Daniel:And like your event stream is the ultimate source of truth. Right? If it ended up in that database table, it
Ryan:It happened. Right? Yeah. Yeah. Yeah.
Daniel:And so you have to now deal with the consequences of it having happened. Right? So you really don't want it to end up in that database table if it didn't happen, right? Yeah.
Ryan:And if Actually, it
Ryan:can't end up in
Daniel:that table.
Ryan:It was it was yeah. It should it would never get it would never get written if it was in a past validation problem.
Daniel:Yeah. But but if it ends up
Ryan:if it ends
Daniel:up in that table, there still might be effects that didn't happen. Right?
Ryan:Yeah. The state might not necessarily reflect what that event was assumed to occur like, to have updated or something.
Daniel:Yeah. Like, your post postmark might be down that day like it is all the time, apparently. But, postmark might be down that day, and so the email didn't get sent. Right?
Ryan:Exactly. That doesn't mean
Daniel:the user didn't subscribe. The user did subscribe. They just didn't get an email about it. Right? Yeah.
Daniel:Like, well, fortunately, we have a record of them having subscribed, so we can do something about that.
Ryan:Yeah. We just might not have the, that opposite or the the next event that says, like, email sent, you know, or or some welcome welcome email sent, potentially, which could be Exactly. Yet another log off of the response of that, postmark success, you know, thing. And then at the at the at the same time, you could have submitted it. They could have responded with a 200 and still not sent the email because that sounds like that's what actually is happening.
Ryan:But
Daniel:Yeah. Yeah. Yeah.
Ryan:At least you got something in your logs to know, like, what happened when within what period of time, and you could replay those for that period, like, if you felt like there was, you know, if you hear about some instabilities or something.
Daniel:Yeah. Totally. But, yeah, I I just you know, once you're sort of a little event filled, like, once you've, like, built some things this way, it kind of, like, corrupts the way you see the universe, you know, like Yeah. Totally. Everything nothing is the same anymore.
Daniel:Everywhere I just see, like, discarded data that you'll never get back. I was like, oh, look at the way look at where we're only saving, like, a very, like, two dimensional representation of this data, and we have no clue how it got here or where it came from. And if anything breaks, like, we'll just we'll be hosed.
Ryan:There's no way for us
Daniel:to get it back.
Ryan:You know, you got 2 things. You know when it was created, and you know when it was last updated. And then everything else in between
Ryan:there is just mystery.
Ryan:Uh-huh. And And we know they have
Ryan:$8,000 in their account. Why? I
Daniel:don't know. Is that true or full like, whatever. If you're talking about finance and transactions and all this other kind of stuff, yeah. Just Crazy.
Ryan:Just manipulating a number is not the truth. Yeah. That's something like that. Man.
Daniel:But, yeah, it's really cool. And, I mean, there's something really cool to be said about, I know I keep harping on this, but, like, I really think that, like, people should be playing with, like, Livewire like, verbs verbs pill to people should be playing with Livewire verbs integration because I think it really is like, LiveWire is very similar, because live to Verbs in that, like, it's just events being fired. Right? Yeah. And then the DOM is sort of like the 2 dimensional state.
Daniel:Right? And so, like, what ends up getting what ends up getting changed is like the HTML that is in your browser right now. But to do that, like, you're firing an event up to the browser, or sorry, you're firing an event from the browser up to the server, then the server is firing a verbs event, then that's updating the state, then that's updating the DOM, which is then being pushed back down to the browser. Like, it's all sort of 1 in the same, and it's just like there's like the 3 levels of the stack, like the browser up to the server up to the database. Right?
Daniel:Yeah. And like, if you can the more you can sort of like mirror the shape of those things, the better. And I'm sure like the real, like, back end JS people would tell me, like, oh, well, like, wouldn't it be even sicker if, like, you could have, like, a front end version of your state that was being mutated in real time along with your back end version of your state so you'd do, like, truly optimistic UI? And that also would be insane. And I've thought long and hard about, like, making, like, a front end JS version of the verbs state application stuff so that you could, like, do truly optimistic front end UI, which would be great.
Ryan:Well, I mean yeah. I don't you know, I actually I kinda live more more in the JavaScript space lately than I have. I mean, actually, when I was at Vimeo, that was actually, a PHP monolith for the most part. And then I actually started writing some of the first my first React app that I wrote was actually this thing called, like, the Vimeo video organizer. It was actually using Backbone for all the state management and stuff.
Ryan:It was using just React.
Daniel:Knockout JS?
Ryan:It was actually we we ended up using React in place of Knockout because we were running into weird memory issues and just like Yeah. Yeah. Their stuff was just so wild. So we're using, like, React, like, 0.9 or to have, like, mixins and all this other wild stuff. Hell, yeah.
Ryan:But, yeah. That was my first taste of React. I think the one interesting thing is, especially to your point about actually having like a a a local verbs or something, is in the, in the JavaScript space, obviously, local first is a big thing that people are talking about right now. And the fact that you can basically run SQLite in your browser via, like, web assembly is pretty rad, and then having that, like, potentially stored to, like, an OPFS or something. To be able to write to, like, a common interface that then writes to a local that then somehow could replicate those those, those same events up to, a hosted, you know, instance is pretty rad.
Daniel:And, like, the whole, like, promise of PWA is an offline mode and all of this stuff is, like, dependent on something like that. Right? Totally. If you look at, like, applications that do offline mode well, like, what they're doing is they have, like, a local event stream that when you get back online is transmitting those events up to a server.
Ryan:Well, they
Ryan:know the last one that they got when they were before you went offline. Right? They they know, like, the hash or or key or whatever it is. Then they know everything that you wrote since you were offline, and then they basically have to do a check to say, like, well, this is what I have.
Daniel:How are these people?
Ryan:Have. Hold down the events that that were ran, and then somehow merge those with the ones that also happened, locally. Or I guess you could also I guess it just depends on it, how you wanna set up your, your kind of, resolution of those things, whether you play back everything that happened on the server and then rewrite yours over top of it since you're offline or vice versa. But, I guess, last event typically wins. You know?
Ryan:But at least you have a record of everything, so you could go back and resolve this if you have to.
Daniel:Most most things will just tell you that there's, like, a conflict and that you have to resolve it. Right? Like, that's what Dropbox does. That's what Google Drive does. You know, like, if you have like, I have a Google Doc, you have a Google Doc, we're both in offline mode, we both get onto an airplane and make separate changes to
Ryan:these Google Docs.
Daniel:And then we both land, they resync, it's just gonna tell us like, you have conflicting versions of this document, go figure it out.
Ryan:Yeah, totally. Which one do you want?
Daniel:But it is theoretically feasible to do some you know, there's all these diffing algorithms. There's like the Myers one that Git uses. There's all sorts of diffing algorithms. Algorithms. So you could theoretically, like, in your validation of your events,
Ryan:do some different algorithm thing and
Ryan:see if there's an actual
Daniel:conflict in the diff. And if there's not, then that event's valid. So in theory, you could interpolate those 2 event streams, and then, like, check the diffing algorithm on each one. And then in theory, if there was no conflicts, you could merge them all.
Ryan:Yeah. It's kind of the promise around, like, CRDTs and stuff. Right? Essentially.
Daniel:I don't know what that algorithm or what that acronym means.
Ryan:Oh, it's,
Ryan:there's a there's a package called auto merge, and, essentially, it's like it's a it's a way to format, changes that are essentially so you're, like, resolvable.
Daniel:Okay. Cool. Cool. Cool.
Ryan:Yeah. I'm actually blanking on what all the all the the letters in that acronym mean right now. But,
Ryan:yeah. Yeah.
Ryan:So yeah.
Daniel:I mean, that that stuff is so interesting, and I'm, like, I'm kind of obsessed with, like, offline mode as a concept.
Ryan:Yeah. So am I.
Daniel:I play, I play this game called Hearthstone Battlegrounds. It's like a card battler game by Blizzard.
Ryan:Uh-huh.
Daniel:And it's like online and you're playing against somebody else. And every once in a while, like, you'll lose Internet for a second, or like your connection will freak out or something. And then you reconnect. And it's so interesting because all of the things that happened in the time where you were offline are played back in front of you in, like, 2 app speed.
Ryan:Kinda like when Zoom slows down sometimes and then have like, does, like, a rapid catch up. Yeah.
Daniel:Yeah. And so it's so interesting because I'm like, oh, dude, this game is using, like, an event stream. Right? So I do this, I do this, I do this, and it's doing exactly that. Like, as soon as I come back, it's like, bro, you missed a bunch of events.
Daniel:Here's all your events. Play them back really quick So the guy knows what happened. And then we can move on, you know? Yeah, it's just so interesting, every time you see systems, like once you've like gone a little event heavy in your head, you can start seeing systems be like, Oh, that's an evented system. I know because they have this feature that they couldn't have if they didn't have events.
Daniel:Right?
Ryan:Oh, I
Ryan:think it goes both ways too. You can actually figure out when someone didn't has that system kind of, but didn't implement it quite right. So I don't know why I still use this Apple Podcast.
Daniel:But Mhmm.
Ryan:Sometimes when you're jumping in between Wi Fi and, like, 5 g or something or, like, Wi Fi and cellular and coming back, the last event that it had when it was on Wi Fi was, like, a certain playtime, then you'd happen to drop off. It's still obviously, the audio is still playing while you're transitioning networks. And then all of a sudden, once it reconnects, it's like, wait. No. You should be back here.
Ryan:Even though, like, few minutes could have, like, occurred, and it's actually it should be writing those events to your local. And then just saying that, no. That happened, you know, 2 minutes ago. I got new events that says you're at, you know, 13 minutes, 53 seconds. But it'll actually jump back sometimes.
Ryan:So they're definitely there's systems that are doing it, but sometimes not always right. Yeah. Yeah.
Daniel:Yeah. No. I love it. Have you used Linear, the, like, ticketing app? So like Linear, I love using Linear because there there's all sorts of opinions I have about, like, web apps that Linear is doing really, really well.
Daniel:And one of them is that, like, there's a lot of places where, like, you don't want a big form with a submit button. Right? Totally.
Ryan:Yeah. I think
Daniel:that we we as developers like to make big forms with a submit button at the bottom. Why? Because we like CRUD, and we like to have a create or, like, a store method on our controller that just accepts a giant payload of everything that's gonna go into this database table and then sticks it in there, and we can validate it and have required fields and all
Ryan:that stuff. The update's that same JSON payload
Ryan:Exactly.
Ryan:With one value changed. And he was like, really? Come on. Like, let me just know that I mean, I guess that's actually may in, I guess, in the verbs world, are you doing a sort of comparison between, like, the previous state and then the event that's coming in and only kind of, like, storing the difference between that states? That way, you
Daniel:Ideally, the event that's coming in only has the difference.
Ryan:So yeah. The you have to do your you have to clean clean the payload to that event ahead of time, essentially.
Daniel:Well, ideally, you would be sending a separate event. Right? So rather than having, like, a
Ryan:The big master payload. The big the big save updates button that just sends the whole Right.
Daniel:So like in the case of linear, right, you've got like reassigning a ticket. Right? So like I'm gonna change the assignee from like you to me. Right? Mhmm.
Daniel:In a Laravel, like, CRUD app, where it's like a big blade form with a submit button at the bottom. Right? And you're just gonna, like, update your models. The normal thing to do would be like, you change the assignee ID, and then you fire like model you then you hit like the update URL with the payload that has the assignee ID changed, right?
Ryan:Yeah.
Daniel:And so the like one step into event sourcing, and this is what everyone thinks that they wanna do first, is like, what if I just had, like, a ticket updated event? Right? And I'll say ticket updated, and then I'll pass the assignee ID, and then I'll update the assignee ID. But I think that's, like, only marginally better. Right?
Daniel:It doesn't give you any of the cool flexibility because what actually happened isn't that the ticket was updated. What actually happened was the ticket was reassigned. Right? Mhmm. And so if we have a ticket assigned event instead of a ticket updated event, ticket assigned only takes a ticket ID and an assignee ID.
Ryan:Yeah.
Daniel:Right? And so, like, we're not filtering any data. Like, we just only had the correct data to begin with.
Ryan:Yeah. You're not getting that master payload of the entire task with its name Right. And the assignee ID, and then just that one value change. You're just getting that one discrete.
Daniel:And so this is, like, the reason I think that, like, LiveWire is the right way to be building these apps right now is because and, you know, and I think, like, in in the JavaScript world, like, you could also be doing this with, like, you know, server side components and stuff.
Ryan:Or whatever. I mean, you know, it's just yeah.
Daniel:But, like, the the thing that sucks in,
Ryan:like, having the sort of Yeah. You have
Ryan:to have to have,
Ryan:like, because you don't wanna create endpoints with ticket slash assigned, and then it gets a to ID with an update of just the I you know, of
Daniel:Yep. Exactly. Yeah. And so that's that's the thing that sucks in the, like, Laravel back end React front end world is that, like, oh, now I have to create a route for each of these actions. I mean, if it's just for each of these actions.
Ryan:Any restful API. If, like, if that's the way you're gonna develop your API, then it then that's regardless of the, you know, what you're using on the back end or front end. That's just a API design choice right there. You know? But Right.
Ryan:It kinda sucks.
Daniel:And so Livewire have to build software where we're gonna see thing. Yeah. Yeah. Exactly. Livewire abstracts that out and like you don't there's only one endpoint in Livewire.
Daniel:It's like Livewire slash update. Right? And then the Livewire slash update endpoint is the thing that will actually, like, call a method on your livewire component. So you can have a method on your livewire component per action Yeah. And just, like, trigger those methods willy nilly all you want.
Daniel:And so we're working on some stuff right now for the, like, JavaScript front ends, where, you know, there's a few, like, quality of life improvements we could make. One of them is, like, obviously, it makes total sense for you to be able to route to an event without a controller in between. Right? Mhmm. Like, who needs this controller in between if the purpose of this endpoint is just to fire this one event?
Daniel:Right? Yeah. We can do the validation in the event itself. We can have this control so we have authorization and validation built into verbs. Right?
Daniel:Mhmm. So, like, you just You give me
Ryan:the type, you give me the data, you just fire the event. Yeah.
Daniel:We just fire the event right there, and that's one request. Then the only other thing that would be like a little bit annoying is like, Okay, but I still have to have a route for each one and I have to name those routes and whatever. Right? And so my theory is that you could just have states that you basically say like, Make a route to this state. I'm a little fuzzy on this yet, like the API still escapes me a little bit.
Daniel:You could say like, Make a route to this state. All of the events that apply to this state, we know that those events apply to this state because
Ryan:of Whatever this key
Daniel:this data analysis.
Ryan:Yeah. Yeah. Yeah.
Ryan:Yeah. So
Daniel:we can, like, statically analyze all your events, figure out which ones belong to the state, and we could, like, dynamically construct the URLs for you, where we say, like, user or ticket slash assign, right? So I would just say, like, Hey, make me a bunch of routes for the ticket state, and it would be like, you know, API.whatever.whatever/state/ticket/assigned because it would or slash ticket assigned is what it really would be. Right? Yeah. And so it would say like, okay, here's a state called ticket, and then what events are on this?
Daniel:Well, the ticket assigned event is on this. So, like, let's make a thing for that. So that's like sort of the vision for the, like, React and Vue front end, like, inertia world people, is that like, we would sort of dynamically construct a bunch of routes for you based on what events you have, and then you get almost the same level of, like, convenience as the Livewire people do.
Ryan:But Yeah. And you know what? You might not even have to do the full path. You know? You might not have to do that dynamic path unless you wanted to kinda keep it feeling kinda more restful.
Daniel:Yeah. You could just make, like, a single, like, event thrower endpoint.
Ryan:Like a post. You know, a single just, like, throw your stuff in here, and we'll figure it out based off of what you're passing us, and
Ryan:then
Ryan:validate it against what we know to be a a specific state instance. I mean, that's basically how GraphQL works. You you just throw everything up in the body, and you you hit, like, a single slash GraphQL.
Daniel:Totally. Okay. Here, if you're listening to the show if you're listening right now, go make a pull request of verbs that does this. Right? Route route, colon colon, get slash events, and then you route it to verbs: API endpoint.
Daniel:Right?
Ryan:Yeah.
Daniel:So and then make that basically create, like, a that's just like a a function on the verbs. I think it's on the broker. Right? Just add a function on the broker that's like an event handler that accepts, an event name and a payload, and then it,
Ryan:fires a
Ryan:bit. Right?
Daniel:Yeah. It it would have to be a post request.
Ryan:Yeah. Yeah.
Daniel:Yeah. So somebody go make that. I bet by the end of the day, we can have this merged in.
Ryan:We got a 156 people watching. So
Daniel:Oh, hell yeah.
Ryan:Someone someone might get happy and go do that real quick.
Daniel:Yeah. Github.com/hirethunk/verbs.
Ryan:How is thunk going, man?
Daniel:Thunk's good, man. Thunk's thunk's great. We are, we're zooming. We're small, but we're, we like being small. You know?
Daniel:I never wanna get to the point where I can't, like, talk to everyone about what they did today Yeah. Within a day.
Ryan:You know? Your point about under 10 people, I think that is you know, you could basically have 2 biggish teams, you know, of, like, 3 to 5, or you could have 3 groups of 3.
Daniel:Like, 3 3 person teams is
Ryan:like the dream. Ideal. Yeah. Yeah. That's definitely the sweet spot.
Ryan:I feel like some of the best stuff that I've ever worked on is just, 3 people jamming and just
Ryan:Well, I,
Ryan:since I
Ryan:was like
Daniel:when I was, like, 21, I was working at this little agency, and me and this dude named John worked together. And, we hired a contractor, and there were, like, the 3 of us jamming on something. It was going really well. And I was just, like, hit with this thing. I was like, dude, 3 man dev shop is, like, the dream.
Daniel:You know? And like, my entire career, like a lot of my beliefs about like teams and organization and stuff have just been informed by this like low key belief that like Freeman Devshop is the dream. Squads.
Ryan:Yeah. Just like these little squads.
Daniel:And there's this whole thing. We don't have time to get into it right now, but there's a whole episode of No Plans to Merge a while ago where I, like, pitched this dream of, like, a federation of 3 person dev shops that specialize in different things. So you've got, like, an iOS team over here and like a Laravel APIs team over here and like a Laravel event sourcing team over here, and people are just out here. Then there's people who are just selling, right? They're going in like landing business and then like accumulating like, a conglomeration of 3 person dev shops behind
Ryan:the scenes. Episode. It was a co op episode. Right? You know what we're talking about?
Ryan:Like episode. Yeah. I'm so into that idea. The co op episode is so
Daniel:crispy. Center.
Ryan:In a in a capitalistic society and also, like, trying to, like, weight value or contributions to
Daniel:It's very hard.
Ryan:Distributions and and the whole thing.
Daniel:Yeah. I mean, obviously, I'm here just, like, running a capitalist ass business, profiting off the excess value of my friend's labor. Here we are.
Ryan:You know?
Ryan:It is loaded up. But at
Ryan:the same time, you have to basically put a little bit of that money aside for when you don't land a project, and you and you can carry people through. So, I mean, there's no there's no excessive profit.
Ryan:Well, right.
Daniel:That's the thing. We've got 100 of 1,000 of dollars in the bank for for dry spells.
Ryan:For rainy yeah. Exactly. For the lack of us.
Daniel:It's just like it sure would be sick to just, like, have that and, like, you know, buy a Lamborghini or whatever. But, you know, that's that's what we have in the bank right now. Totally. Like, keep keep us afloat in case in times of trouble. Yeah.
Daniel:And there have been times of trouble.
Ryan:Yeah. Yeah. I mean, it it it definitely it's it's a wild time, I think, for people who are out there either, like, getting laid off or trying to find a job right now. But Yeah. Thankfully, it seems like you all are doing good.
Ryan:I've been trying to keep busy. I have, like, 2 or 3 different clients that I'm kinda ping ponging between and, just trying to figure it out. But
Daniel:Yeah. I mean, most of our work comes from other devs. Right? Like, other devs are the ones who know about us and who find us and hire us.
Ryan:Correct. Like, you
Daniel:know, we're not figure
Ryan:at companies and stuff who bring you in kinda. Mhmm. Yeah.
Daniel:Yeah. Yeah. And we're trying to, like, improve our, like, normie funnel. Right, where it's like people who aren't programmers, we want to talk to people who aren't programmers, but have software issues. You know?
Daniel:It's incredibly hard to, like, figure out how to do that without niching down all the way and being like, Oh, I go to, like, health care conferences. Yeah.
Ryan:How do
Ryan:you how do you get on the radar? Right? Like, unless you're posting to LinkedIn and doing a bunch of other stuff and, like, talking outside of the group of basically other engineers or other dev shops, it's so tricky. I'm gonna be very choppy. Contributions are they they don't land with the normies.
Ryan:You know? Like, I'm into it.
Daniel:Yeah. Yeah. Well and that's the thing. It's like, I think we're gonna probably like, we've got a couple of ideas of going through like partnerships with other service providers, like, you know, consultants who do other things for people, you know? Totally.
Daniel:And, I think like those sorts of partnerships are gonna be very valuable to like, get us in the door. Especially if it's something where it's like, hey, like, say there's, like, a tax credit that you could be getting if you did something a little bit differently. Mhmm. Well, like, we can build the software to do that. Here's some guys I know who are good at building software and, like, blah, blah, blah.
Daniel:You know?
Ryan:Yeah. That actually that to circle back to the whole, like, e learning, there's actually stuff like that, I think, that people are available, like, that are available to companies. Right? Like grants and stuff if you're doing any sort of, like, education based stuff than doing stuff
Daniel:I bet you there are, but I I'm I'm pretty stupid
Ryan:when I'm trying
Daniel:to solve all of that stuff. I'm I write code.
Ryan:So yeah. Totally. I mean, same here. It's like that if someone made a service where you could basically just, like, search around and find free money Yeah. Oh my god.
Ryan:In the easy way, then they're
Daniel:There's that guy with the question the question mark suit. There's that old guy
Ryan:with the question
Ryan:mark suit. Yeah. He's got a big look.
Daniel:That guy. We need that guy for, like, business tax credits.
Ryan:For real. Hell, yeah.
Daniel:And then, like, also, put our contact form on your website, question mark guy.
Ryan:Yeah. For real. Man, that's super cool. So are you building anything for fun? I know, obviously, you have paper cuts, and then, you all did the the game for Laracon US, which seemed like that was a pretty big success.
Ryan:And those demos game
Daniel:was awesome.
Ryan:Demoed so good on on stage. I mean, it was it was so sick to even have the, the migration kind of, like, rerun there, live. It was pretty rad.
Daniel:It it that was sick. Yeah. Yeah. It that was, like, the most fun, making that game. And, you know, Thunk was born out of making games, right?
Daniel:Like, we built games, during COVID. That was like, how John, like, became a programmer. It was like in 2020, we started building a game. Yeah. And like, he had written a little bit of code before that, but, like, that was the time where it was like, look, dude, like, we're building this game.
Daniel:Get on board. You know? And so he started writing code, and then he was PM ing it, and I was, like, leading the dev. And then at some point, he was writing way more code than I was on it. And, that was kind of like the origins of Dunk.
Daniel:So we love building a game. So anytime we can do that, we will. And John's got an idea for another conference game right now, I think, for for EU. So, we're building a new conference game, as well. But we're also paper cuts is like, nearing a public beta.
Daniel:At this point, we've done a bunch of user interviews and a bunch of conversations with folks. And, you know, it's it's sort of John's baby. It's, like, right up his alley of, like, how he it's, again, very opinionated about, like, how we like to do things.
Ryan:Yeah. Because it's all about, like, kinda, like, feature requests. Right? Or or requests being made to team. Yeah.
Ryan:Exactly. It's all about sort of like
Daniel:it's all about sort of like fielding, problems that people across your organization have. Yeah. Like, It's like non technical stakeholder facing stuff. Yeah. I think it's papercuts.
Daniel:Io. Let me make sure.
Ryan:Yeah. I think it's Get
Daniel:on the mailing list if you care. Yeah. Papercuts. Io. Hop in there, get on the mailing list, we'll send you an email about it.
Daniel:But, yeah, that's it's super fun. We're using it internally. Actually, Internashi is one of our clients now, and John is, like, doing all of the PM for them.
Ryan:Alright.
Daniel:Yeah. So he's, he's using paper cuts internally at InterNACHI, and he they have a bunch of different teams across the organization. They've got, like, the education team and the marketing team and all these other people. And they all everyone has requests of the giant monolith that does everything. So
Ryan:Yeah. That's awesome. So Yeah, man. Yeah. I mean, we're go we're an hour and a half in.
Ryan:This has been rad talking to you. I can't wait to
Daniel:doing this.
Ryan:I can't wait to actually, drill it, like, dig into verbs and start playing around with it.
Daniel:Totally. If you do, hit me up. I love to I love to pair on stuff with it.
Ryan:Yeah. I will. Yeah. I have a few apps in the works right now, but it's all a matter of carving out enough free time. I got 2 kids
Ryan:Oh, yeah.
Ryan:And quiet work and stuff. It's hard to really get too many hours in on this stuff, but sometimes you just gotta do the old late night late night jam at the time. 2 AM.
Daniel:Dude, I do one the other day. I did one from, like, 4 to 8 in the morning.
Ryan:Oh my god. I
Daniel:hadn't done one in forever. It was awesome. I cracked, and I cracked a design problem. It was amazing.
Ryan:That's amazing. There is something that opens up at night for something for whatever reason. It's like I'm not distracted by, obviously, not Slack or Discord or I don't I don't think anything's happened on Twitter that I'm not gonna distract myself with. I could just, like, throw on some tunes and get into the code and and just get be there. Yeah.
Ryan:So rad. Well, awesome, Daniel. Thank you so much for joining. This is the first episode of Jam Sessions. Hopefully, we can do another one, and we'll I'll keep try to keep in touch, and hopefully, you can do the same.
Ryan:Sounds great. Maybe I'll have to come up and visit you in Nashville. Are you still I think I you told me before that you're looking to maybe move to Philly. Is that still in the works? Or
Daniel:Yeah. So we're here for now. I think we're targeting, like, beginning of December ideally as our move date. Oh, wait. When?
Daniel:Sorry. Beginning of December, I think.
Ryan:Oh, December. Man, right around the corner, man.
Daniel:Yeah. So we'll see. Cool. My wife is, like, putting her notice at her job, and then, you know, we're we're, like, right there. You know?
Ryan:Oh, man. It's real.
Daniel:It's real. It's a real thing.
Ryan:So you're gonna be, like, in Chris' neck of the woods. Right? Is he is he Philly?
Daniel:Yeah. Chris is up there as well. And then John, my partner, is in New York, so he'll be at a train ride away from New York.
Ryan:Hour hour 15 or so. Brad. Len would Len Woodward says he had a dope late night code jam session. He implemented Tetris in Laravel prompts. I love to
Daniel:see it.
Ryan:Yeah. Good games. You know?
Daniel:GG volume?
Ryan:Alright, man. Well, you have a great night, and, thank you so much. And we'll, try to do this again sometime. Alright, bud. Alright.
Daniel:Bye, guys.