Become a Creator today!Start creating today - Share your story with the world!
Start for free
00:00:00
00:00:01
Ep. 2: Over-engineering migrations even more! image

Ep. 2: Over-engineering migrations even more!

S1 E2 ยท Over Engineered
Avatar
340 Plays1 year ago

In the second episode of the podcast we talk with Tim MacDonald about a few other approaches to how you might manage other operations that happen before/during/after a database migration (or really any deploy step). Tim pitches a lower-level approach that spawns a whole new line of thinking.

We also touch on some of the responses to episode one, including:


Transcript

Introduction to Overengineering

00:00:06
Speaker
All right, welcome to another episode of Overengineered, the podcast where we ask the very important question, what is the absolute best way to do things that don't really matter? I am here today. I'm sorry, I love the premise of this. The fact that I get to do this is so much fun.

Meet Chris and Tim

00:00:29
Speaker
Yeah, definitely. I'm here with my friend Tim McDonald.
00:00:34
Speaker
my internet friend, hopefully real world friend after Laricon, that will be fun. Yes. And before I introduced him, I realized last episode I never introduced myself. My name is Chris Morrell and I am a PHP developer. I've been writing PHP code for 20 plus years and just thought there are lots of topics that
00:01:00
Speaker
come up over and over, but never feel important enough to really give a lot of time to. And we just thought, let's sit down and really arguably overthink them. So without further ado, Tim, do you want to introduce yourself? And honestly, I don't, I don't know. Like what's, uh, what's your, give us the short version. What's your, what's your story? How'd you get to where you are right now? I mean, the short version is I'm just a random PHP developer. That's, that's it really.
00:01:29
Speaker
How did I get to where I am? Uh, started off like as far as development stuff goes, uh, started off doing like WordPress stuff, worked at like a WordPress factory or it was just like website after website after website. Um, during that time I Googled what's the difference between JavaScript and jQuery to try and understand that world. Cause I just didn't understand what that even meant.
00:01:51
Speaker
And then kind of started looking into the app world and building apps. And at one point I was heading down the path, like I built some iOS app and Android apps, and I was heading down that kind of mobile path. And I was like, eh, the web's kind of cooler. Want to come back to the web. You know, found Laravel, fell in love with that, build lots of apps. And yeah, here I am still just kind of writing PHP and loving it.
00:02:16
Speaker
And now you are on the Laravel, the Laravel team, right? I am. Yeah. Yeah. Which is, which has been amazing. It's, um, yeah, it's, it's really cool. How long has that been? Not quite a year. Okay. Yeah. So coming up to a year, I think soon, which has just flown by. That's absolutely nuts to think about, but yeah, coming up to a year, I think. Hmm.
00:02:39
Speaker
Nice. All right.

Laravel Schema Migrations

00:02:41
Speaker
Well, let me, let, part of, part of why I am really excited to have Tim on is I was talking briefly about the concept of this show and I said, for example, here's, here's, here's a thing that we might talk about, right? You've got,
00:02:56
Speaker
You've got your migrations, and in Laravel there's a very straightforward story for schema migrations, but what do you do when you need to manipulate data in some way that's not part of the schema migration, right? Do you just like, the example that we gave last episode just briefly is like you're doing sort of a roles and permissions feature, and you've just migrated a roles table, you've just migrated a permissions table, and
00:03:25
Speaker
You know at the base level you're gonna need to create a couple of roles and a couple of permissions to get started and it's not. That those are the only roles and permissions that are gonna be in the system but you know like immediately i need you know a couple of guest role and admin role the staff role something like that couple of things.
00:03:45
Speaker
And ostensibly, maybe the feature that you just pushed won't even work unless there's some minimum amount of data in that database that you just created. So yeah, we explored a little bit of the different ways you might do it. Do you just throw it in the migration directly? Do you write a custom command that you have to then SSH into your app?
00:04:08
Speaker
app server and Ron after the migration or a couple other things. And we're gonna come back to them in a couple things that folks said on like Twitter and Mastodon. But what really intrigued me was Tim said, I've got an idea and it's not anything that I've heard anyone else say. And so I've been very curious to hear what this idea is.
00:04:32
Speaker
So before we get into any of that, like let's hear it. I've been waiting weeks for this just to jump straight into

Innovative Data Manipulation Techniques

00:04:39
Speaker
it. Yeah. So as you mentioned, Chris, like you kind of planted the seed that you wanted to chat about this with, with, with developers. And that day I was riding home on my bike and as all good problem solving, like that was when I started to like, think about this problem, we'd dig deeper and kind of like start to pick it apart. And I was like, Oh, I wonder if you could do X, Y and Z. Cause we had something.
00:05:02
Speaker
come up with Laravel Forge recently that kind of helped me kind of put all this together on the bike ride home that I think there's like a deeper level too. So I think that as Laravel developers, we've all kind of thought about this problem, right? Because we've all had to tackle it at some point once an application kind of gets down the line far enough, you know, you need to do something like this. So it's something that I think a lot of us have put some kind of thought into and I myself had put lots of thought into it. And then yeah, it was just,
00:05:30
Speaker
that ride home, something just clicked and I thought, oh, this could be cool. Now, full disclosure, this could be an absolutely terrible idea that would never work and has a big hole in it. And you're going to point it out in the first two minutes of this podcast. So that's it. I can't wait. But my idea is Laravel Envoy, essentially. Okay. Say more.
00:05:53
Speaker
Yeah. So Laravel Envoy is a tool to kind of have deployment scripts for your application. I think we should define the problem. Like I don't know. You've kind of talked about the problem in the previous podcast, but because there's a few things, right? Like you want to solve the different problems in different ways. And so one thing you talked about was the migration stuff. Now, when I think about the migration stuff, there's a couple of things. One is maybe I've just added a new column to an existing table.
00:06:23
Speaker
Yeah. And I don't want to, I think you need to solve and say it's like a column you want to add, you want to populate quite often. I find myself wanting to add a column, populate it, and then make it non-nullable.
00:06:39
Speaker
So you want to make sure that it has all those values in it. Now there's no point in writing a migration that adds the thing, then writing a cedar that populates, or you can write a cedar class, but like running the cedar as a separate process and then running another migration to like make that non-nullable. And I think in that case, if you're kind of, if you're tacking on a column, then what you want to do is
00:07:00
Speaker
add the column, just loop over all the rows, populate it there, and then make it non-nullable. I guess depending on the size of the database, maybe that isn't practical. You do actually need to do that in the background if you can. If you can launch the app and it can run without those values there, maybe it has to be blocking whatever.
00:07:21
Speaker
I think something like that just has to be in the migration. So I think to answer that particular problem, I think that is like, do that in the migration. That's kind of my opinion for as long as I mean, like all things, right? It depends. Yes. And I do think that there are a lot of cases. I mean, and yeah, this is where it comes up is like, yes, but what if that table has 10 million rows in it, right?
00:07:51
Speaker
And, you know, realistically, going back to the, it depends. The answer is, well, when you have to deal with 10 million rows, maybe you have to have a different solution. Yes. But yeah, I'm with you so far.
00:08:03
Speaker
Yeah, absolutely, absolutely. And perfect case scenario is you probably add that column before you deploy the feature, then you populate it in the background, and then you make it non-nullable, and then you deploy the feature when it's already populated and pre-filling itself and do a staged migration or something like that. So that's that.
00:08:27
Speaker
I'm jumping all over the place. I'll get to the on voice stuff. So my thinking is like there's some tasks that you need to do where you want to like run a migration. Okay. I'll run a CDOP. Okay. So that's fine. We can define a CDOP and we can deploy our application. We can SSH in and do PHP, artisan, DBC, whatever, and run some CDOP to populate stuff. But you might only want to run that CDOP.
00:08:52
Speaker
Do you, do you see the production? I've never run a seat. I don't really use Cedars ever. So, but I've certainly never run a Cedar in production. Sure.

Laravel Envoy's Role in Deployment

00:09:00
Speaker
Sure. I have worked on applications where that was the process that we kind of used. Uh, the Cedar kind of helped us, you know, reproduce and test before we did it, you know, just running an artisan command or something like that where you're tinkering or something. So yep, definitely, definitely done the Cedar approach. Um, but it like.
00:09:21
Speaker
It's not any different to just doing it in a migration at that point, right? It's kind of the same thing. I guess the difference is with migrations, you can blow them away if you use the DB dump stuff. Whereas you see that kind of will stay in your code base. Yeah, that's a good point.
00:09:37
Speaker
So what my idea is is Laravel Envoy and augmenting the functionality that already exists there so that you can have commands that only run once on a server and commands that run on one server once, commands that run on all servers once. Well, there's already commands that run on every single deployment. That's just kind of what Laravel Envoy is.
00:10:07
Speaker
Can you, I have a vague, I've never used Envoy. I think of Envoy as like a Ansible or a puppeteer kind of, or a puppet, I guess, like a deployment tool. Is that like?
00:10:25
Speaker
Yeah. So I imagine that you run it locally to connect to remote machines. Am I thinking about it differently? No, that is, that is, that is correct. Yep. What you're thinking about is correct. So you kind of have on void local, you have your, um, your kind of on voice script. Uh, it's a blade file. So it's on void blade.php in your repository and you specify the servers. So you can say my web server is this IP address. My worker server is this IP address can be an array of those.
00:10:55
Speaker
And then you can write different tasks. So one task might be caching the config or caching the routes. One task might be restarting the workers. One task might be pulling or get down and running the migrations, things like that. And then you can write these tasks, and then you can also write a story. And a story is like a composition of tasks. OK.
00:11:20
Speaker
essentially blade decorations on top, top of bash scripts, or is it running in PHP or, okay. No, no, you've, you've got it right. Yep. So it's, it's blades. You're running bash scripts. So, you know, you're seeding into a directory running PHP artisan Q restart, things like that. Okay. All right. I'm with you so far. Cool. So the thing with Envoy is that it's stateless, you know, it just runs the same things, the same commands every single time. Sure.
00:11:48
Speaker
So if you want to restart your queues, you put that in the story and it goes and restarts the queues. It doesn't know that it's done in the last deployment. It doesn't know that it wants to do it every time or whatever. And the reason that I've come to Envoy is that I feel like this is somewhat of a general solution, again, when it suits, to quite a few problems that are all related.
00:12:16
Speaker
Yeah. Which is, you know, when, when do you run that seater? How do you get that seater to only run once on a machine? Right. But so it's like, it's, how do you make change to state once? Right. Right. And all sorts of different types of changes, right? Exactly. So there was a update we did.
00:12:43
Speaker
on Laravel Forge where we updated the dependency and the environment variables that it needed had to change. And without those environment variables, so it was like
00:13:00
Speaker
We'll just call it Laravel underscore foo. And that was what it used to be. And now the new environment variable is Laravel underscore bar, whatever. I'm great with examples. And what happens is if you haven't got that environment variable changed, the framework can't actually boot. Okay.
00:13:23
Speaker
which is kind of similar to if you haven't made the change in the database, the application can't actually run. I feel like there's kind of a similarity between those. Yeah, I can see that. And so what I was thinking is can we kind of come up with a solution here where I can say, you know, hey, Envoy, this Cedar should have been run on every, every deployed environment at just once.
00:13:48
Speaker
whether it's to like seed my admin roles or something like that. And so I could create a task in Envoy that's like seed admin roles. And then I tell Envoy, you know, do this thing once per machine, or, well, it wouldn't, no, this would be just once per, because it's a shared database. Per deployment, I guess, per environment.
00:14:13
Speaker
And then what it could do is run that thing. And now this is again, where it starts to break down for serverless, but just create a, have a directory of files that are just flags to indicate what tasks have been run for that particular machine. And that's why you'd want to put that would live on your server itself rather than locally on your machine.
00:14:36
Speaker
And I could see pretty easily having some sort of driver concept where maybe the default is a file system based thing, but you could do just like migrations, you know, you could have a database driver and, you know, this task ran on this device or this machine, you know, and this environment.
00:14:55
Speaker
And the reason that I kind of was thinking more file system, yes, absolutely. The reason I was thinking file system based is that, you know, what if the task is to like update the credentials for the database? Like it has to do that before it can work out whether or not it's run that task before. And so that's where I think that kind of falls, falls apart, but it would, you know, it would still be possible if you just never changed those credentials or had a dedicated system for this shared data somewhere.
00:15:22
Speaker
Right. Yeah. I mean, and you're always going to, you're always going to run into that. Cause what if the, uh, you know, what if the task is, you know, swap from like a local file system to like a block store mounted file system or something, right? You could go the other way and make the same argument. Are you? Yes, definitely.
00:15:40
Speaker
And then what I thought would be cool is, you know, we don't want to be writing bash, like sometimes we, because I feel like there's my, my thinking on this, and I think you, you kind of messaged me about this as well. Like, what about someone saying maybe a different migrations directory, or like, I had had an idea for like, Laravel actions. And I know Laravel actions is like, I think that's a thing. But
00:16:04
Speaker
you know the idea that it's like the same as migrations but for just other stuff and it like saves it in the database and it's just like a PHP file you can do that and so then I was thinking well Envoy could
00:16:18
Speaker
You could do bash scripts or you could point it to a PHP. If it ends in .php, then it can just be a PHP file. If you just want to use PHP and not write bash, then that's fine. But maybe the framework can't boot for this particular thing, you still just want to write PHP for it.
00:16:38
Speaker
then having a PHP file wouldn't mean that the application would boot. There'd just be a function you could call like boot or something that would actually then boot the framework. So then you've got access to the framework to be able to do whatever it is you wanna do. Yeah, this was, again, I don't know that this is like all stringing together, but in my head, I was just having these like, wait, is this a thing? Maybe this is like a solution to all of this. I don't know.
00:17:04
Speaker
Well, yeah, I mean, this is definitely, I see what you were saying in that it's like, this is solving the problem at like a different level, right? And thus it's kind of like solving, it's a much more generalized solution, right? Cause it's not just about data migration anymore, it's about
00:17:24
Speaker
And what we call orchestration, I don't know if that's a universal term, but like, just orchestrating the deployment of your system, right? And all the different pieces that go into that. I mean, right now, the thing that I'm a little caught up on is, right, we use Envoyr, right, the Laravel product, to essentially do this, right? We've got a bunch of different,
00:17:55
Speaker
deployment hooks at different steps in the deployment process, and we're running different commands, and most of them are either the standard artisan commands around caching configs or restarting the queue, and running migrations, for example.
00:18:20
Speaker
And then it could, but it could just be a Bash script or something like that for example. And so in that story, I guess I can imagine saying, I guess you still have some of those deployment hooks, but you're essentially just using Envoy to trigger the Envoy story for that stage. Is that kind of what you would imagine?
00:18:49
Speaker
I imagine that there's...
00:18:52
Speaker
there's a story for each deployment, right? Like let's think about, now I haven't used Envoy for disclosure. So I'm just thinking Laravel Forge deployment script, you've got CD into, you know, timact-onal.me and then git pull to like pull down the latest git information. You do PHP artisan migrate, you do PHP artisan optimize whatever to like cache the routes and cache. Like those are the things I wanna run
00:19:20
Speaker
every single time. That's my deployment story. But then I'm writing a PR. I'm writing a new feature. Actually, you know what? What I'm doing is I'm augmenting an existing feature. I'm changing an existing feature. And the existing feature puts information in the cache. It puts
00:19:44
Speaker
a list of drink bottles in the cache. But the structure of the drink bottle data, right, before it was like brand and name.
00:19:55
Speaker
as like a list, maybe we've changed that to kind of be a more complex data structure and name is now title or brand or something like that, whatever, we've just changed the structure. So the data in the cage. We've got a first name and a last name column and we've realized that that's fundamentally flawed and we're just going to a name column. Is that a...
00:20:18
Speaker
I love it because that's just good in

Integrating Envoy with Existing Systems

00:20:20
Speaker
general. Thank you. As I said, not good at examples. Perfect thing. We're putting a list of names and we've got first name, last name in the previous iteration, and then we realize, you know what? Not everyone has a first name and a last name. We should just be asking for the name.
00:20:40
Speaker
If we then say, hey, Kesh, give us all the people's names, our application locally is going to work because we're just using the array driver or something locally. So every time we change the structure of what goes into the Kesh, it just is valid on our system at all times because it's only for that one invocation of the app or whatever.
00:21:00
Speaker
But in production, it's still got first name, last name. When we deploy this feature that has changed and is expecting name to come back, we're going to say, give us all of those people, loop over them and pull out the name, and we're going to get an array offset. Name does not exist because first name and last name, they're in the cache. They leave in the cache already. For this one deployment, when I deploy this feature, I want to bust the cache.
00:21:27
Speaker
So for this one, like I've just written this feature, I've got a PR up and I've got to say, hey, whoever deploys this, can you like do a, can you SSH in and run PHP artisan cache clear to make sure you clear the cache after you've deployed this thing becomes this kind of manual step.
00:21:46
Speaker
Or if there's like a third party system, you know, I've got to go and log in and it's like, it's separate system. But I want that command that I have to run when this PR is deployed to be a part of the code base itself. Okay. This is starting to click more for me. I see I understood where you're going, but this, this is making more sense. It's like this particular deploy needs this thing to run at some point during the deployment story. Yes.
00:22:17
Speaker
but it only needs to run once. I don't want to bust the cache on every employee. Just once with this particular deploy. So I go into my Envoy script and I say, Hey, I've got a new task that's called bust the cache. And maybe I, yeah, we'll just leave it at that for now. It won't complicate it. So I create a new task that's busting the cache and I say, run this on one server and only run it once.
00:22:44
Speaker
And then that feature, now the commands that have to be run to make this feature okay are now part of the code base. They're a part of the PR. I love that. It gets merged in. It gets deployed and this just happens. As it deploys, it runs that thing. It persists somewhere that it has run that commands with the code as the code has gone out.
00:23:09
Speaker
And then next time we deploy, so we spin up a new feature, merge it in, deploy it. It doesn't bust the cage that time. Right. Right. So, okay. So I was just looking at the Envoy docs a little bit in the background and let me see if, if what I'm picturing lines up with what you're kind of describing, right? So in Envoy, I see there's a, there's basically a couple of.
00:23:35
Speaker
excuse me, blade directives. One is at task and one is at story, right? And at task describes a specific individual task. And then at story is a collection of tasks that run, right? So you might have like a deploy story or an install that calls like the update code and install dependencies tasks, right? These are the examples in the docs.
00:24:02
Speaker
And what I could see imagining is, I don't know if you could use at once because that's already part of Blade, but in an ideal world, there would just be another section of your Envoy script at the bottom that's just at once.
00:24:21
Speaker
And maybe it takes like a, an argument you call it by default. It only runs once per environment, but you could say like, you know, per machine arrow true or something like that to say once per machine. And, and maybe by default, then when you run a story, excuse me, it just, it just includes
00:24:50
Speaker
or, you know, it'd be even cooler. What if you're once referenced a task, right? So you could say at once and then, you know, in the arguments you take an array, you know, after arrow update code, right? Or before arrow update code. And then any story that calls that task or anything that ends up calling that task
00:25:16
Speaker
Right, checks, okay, was this once triggered for its requirements? If not, run it in the lifecycle either before or after, whatever the different hooks are. Does that kind of line up with what you were thinking?
00:25:32
Speaker
I was definitely the concepts there, a hundred percent, but the kind of that dependency between the task definition and like the command that it should run with, I was more thinking that, um, so the task definition itself would specify. Should it be run once? So you'd kind of say, all right, I want to create a task that is busting the cache and only, and this only gets run once. And then in the story you'd say, you know,
00:26:02
Speaker
update the code, install dependencies, bust the cache.
00:26:11
Speaker
migrate the database. And that way it's in the story. You can kind of see it in what order it's got to happen, because it might have to happen before the code is pulled down, or it might have to happen after the composer has done its install. So having it kind of as a part of the story itself of like, what does the next deployment look like? Here's the order that these things have to happen in. And if it's already happened, then it just won't run. It's just a no op.
00:26:35
Speaker
And it'll just go to where the next item in the story is. So rather than having a separate once directive, it's like this particular task is just marked as only running once. Or maybe in the story itself, it's like wrapped in a pseudo like at once and then, or it's like bust cache colon once.
00:26:55
Speaker
Yeah, I would like, I feel like I would want something because the downside of that is then, you know, a year later, right? It gets to be kind of fuzzy.
00:27:08
Speaker
What's happening in this deploy story, right? Yep. I hear what you're saying. And if, if someone, if you've deleted it, because you could go and delete those things as well, right? Like you could go and delete bust cache, but then if someone, yeah, like you're saying like 12 months later comes and goes, Oh, I need to bust the cache. I'll create a bust cache, run this once, but it's already, bust cache as a task has already been run once. So there needs to be some kind of like identifier. You need to be really careful about that. Yeah. Yeah. Yeah. Yeah.
00:27:39
Speaker
That's interesting though. I mean, I like the concept. I think, okay, to step back.
00:27:49
Speaker
Like immediately I'm just like, yes, this is, this is interesting because now we're solving even more problems. And I love that especially. I mean, this podcast is called over engineered. So like, this is perfect for that. On the flip side, we are introducing a ton of, of new complexity for what had previously been like,
00:28:17
Speaker
just run an eloquent query inside of your migration, right? So is this a solution that you have to, is this a solution to that problem or is this a solution to sort of orchestrating deploys that also happens to potentially have an answer to that question as well?
00:28:43
Speaker
Yeah. I mean, it's a solution to something. I'm just not sure. 100%. Well, yeah, but that's why we're talking about it. I love it. Yeah. So it's hard because I keep kind of coming back to, yeah, the migration stuff. Like if you want to, so let's, let's talk about like the concrete example of populating the different roles and permissions, right? Cause that was like a good one that we can grasp is like, is you want to create the table and then you want to fill it out.
00:29:13
Speaker
So let's, let's walk through those steps of what this looks like. I'm sorry if we're repeating stuff from the last episode here. I didn't listen to make sure that I wasn't kind of incepted by their ideas, but yeah, the, you know, uh, what you, you'd create a migration, create a table that's roles. And then probably underneath the kind of skeet, you go schema, colon, colon table, create the roles table.
00:29:40
Speaker
And then maybe outside on the next line, you'd say, you know, uh, roll, colon, colon, create many, and then you'd kind of pass big long list. Cause you know, in, in this migration file, you know that that table doesn't yet exist. Uh, sorry, the data doesn't yet exist. None of the roles and stuff. Cause you just created the table.
00:30:00
Speaker
Yes, but the problem is you in practice, you typically have to also, this is, this is kind of the, one of the gotchas and we didn't really talk about this last episode. I don't think, but one of the gotchas is you really only want that to run in production or you don't want it to run during unit tests typically. Yep.
00:30:27
Speaker
So, so yes, but you usually would have that wrapped in some sort of if statement. It might just be if app, if not app running unit tests. I'm trying to remember exactly, but that was, cause that was how this all came up maybe a year or a year and a half ago was we ran into one of those gotchas having to do with testing. Mm-hmm.
00:30:51
Speaker
But yeah, that's just an aside. Just kind of pop the domain. Yeah. Yeah. No, that's, that's a great point. A hundred percent. And so that, you know, we can deploy that, that works. Everyone's happy. So, so where's the problem? I guess the problem is you might do a DB dump. No, is it schema dump? I think it's schema dump and that.
00:31:13
Speaker
blows away all the migration files and just gives you a dump of the schema to quickly be able to like regenerate the database without running through each file individually. It's just like, here's what you need, take it as is. Right. So then you've lost all that. That's right. So now when you run PHP artisan migrate, there is no, you know, if we're not in unit tests, then loop over these and create like that code doesn't even exist in the repository anymore because those files have been deleted. Right.
00:31:42
Speaker
And so I think that's where the problem starts to come in. Is there other places where this problem, where like the problem arises? There's that. Yeah, there's that. The unit testing problem actually is really just, if you do this a handful of times, then, you know, if you're like us, we've got 20,000 lines of feature tests, like for features that have nothing to do with that particular dataset, now you're,
00:32:13
Speaker
you know, inserting a bunch of records and then removing a bunch of records on every test that hits the refresh database, right?
00:32:23
Speaker
So that was another issue. Yeah. I think there's a philosophical issue of just like, this feels a little gross, right? Like this migration system seems to be meant for one thing and now we're doing something else in it and it just feels a little dirty. But, um, you know, to one of the, one of the followups, I got, I got a couple of really interesting,
00:32:47
Speaker
responses on Mastodon and on Twitter. And one of them was basically just like, just use migrations because just use migrations, you know? That was Brennan White. And his basic point was like, kind of to the same point that you were making in the beginning, you want something committed to the repository that represents this work, right?
00:33:16
Speaker
And so that's what we use migrations for is to have sort of a chronological list of the stuff that needs to happen to the database.

Alternative Solutions for Data Migrations

00:33:25
Speaker
And so maybe I'm mischaracterizing his response because it was pretty detailed and I'll add a link in the show notes or something. But, you know, and I do really appreciate that. It's like, yes, maybe it feels a little dirty, but also
00:33:43
Speaker
this is a great system that already exists. You can get around a lot of the issues. And that's what got me. So I had messaged you and said the one, so another response I got unmasked on was Lucas Heller said, what they do is the database migrator, you can pass it a directory parameter, right? So by default,
00:34:07
Speaker
it looks in a specific directory or it looks in all the registered migrations directories. So like there's the default migrations directory. And then if you use something like modular, like something else might register other migrations directories, but the migrator has those directories that it runs through, but you can pass it a directory and tell it just use this directory instead. And so his basic take was,
00:34:34
Speaker
if you have like scheme migrations that you wanna run and then at a separate point in your deploy, like your automated deploy process, you have data migrations you wanna run, create another directory for the data migrations and then just have that run as a separate step in your deployment script. And I think that's really clever. I think it's,
00:35:01
Speaker
it really is interesting. And honestly, that may be the thing that I'm leaning towards as the cleanest solution to this problem. And then in there, if you have something that's tens of millions of rows that need to be processed and they can be done asynchronously, I don't have a big problem with spawning some big batch job
00:35:31
Speaker
from that migration. But then you have that single point of, did this get run exactly once for this environment? It doesn't answer the question of if something needs to be run on each machine. So if you have
00:35:49
Speaker
I don't know, if you have some sort of file system type or, you know, if you have different Redis caches per app server or something like that, and you need to do something specific to them, this would not solve that problem where yours would.
00:36:09
Speaker
I mean, I love both of these solutions, right? I love the first one, which is just like, well, just use the thing that exists. Like I'm all about that a hundred percent.
00:36:20
Speaker
the idea of having a different directory and then just still using PHP artisan migrate is like, that is, that is very cool. I really like that idea. Yeah. Yeah. I mean, then that's quite nice for like a new developer as well, right? Because, you know, like the, the roles and permissions thing, like that's like, let's keep like leaning on that example.
00:36:42
Speaker
A new developer comes along and you've blown away the schema. And like, but everyone's, everyone ran the migration. So they've all got, you know, admin and super admin and moderator, and they've got all the roles in their database, but this new developer comes in, runs the migrations. And it's like, I don't have any roles in my database. It's like, I'll just run the actions because they don't, I'm calling them actions, whatever the other migration directory is, they don't get blown away. Like they're just permanently there. So it's like a, how,
00:37:11
Speaker
how you set things up, I guess it is problematic if schemas change. You'd have to then update the data as well. So there's that kind of dependency there, but it's still very interesting. It's good for production. It's good for production and getting that data kind of moved from A to B. And if you are in a situation where you have a team where folks are coming on and off,
00:37:34
Speaker
Like in your bumping up to this against, uh, bumping up against this a lot, you could probably add some sort of thing that's just like, you know, it reads the schema and only runs that data migration. If the schema is like the sheep that it expects, right. And then if it, you know, if at some point in the future it gets to a place, it's not ideal though.
00:37:59
Speaker
Yeah, the whole issue of doing the schema dump throws another wrinkle into it that I hadn't really thought about yet. Let me give you the two other, there are two other options that came out. Let's just put them all on the table, because this is interesting. So another response that I got on Twitter, Ed Grossviner,
00:38:26
Speaker
said he just has a run once command and that command just gets run at each deploy but it basically just takes the md5 of itself and just aborts if it's run that
00:38:43
Speaker
that hash before, right? So you can either store that hash on the file system or you can store in the database. I love this. I think this is so clever. And so it's just like, if you change the run once commands handle method,
00:38:58
Speaker
It runs, but then the next time it runs, that hash has been executed. So it just ignores it. And I think that's like, I think it's probably not the right solution in a bunch of cases, but I think for the right developer or small team of developer, it's just, it's beautiful. I think it's a very clever solution.
00:39:22
Speaker
I really like that idea. And this has reminded me of, and I'll talk about it after we've kind of gone through the other solutions, but Taylor had a tweet about this kind of concept as well. And it reminds me of that implementation. I think the one thing that would trip me up is like someone runs PHP CS fixer and like changes a rule and now that everything's changed and you didn't want that thing to change and now it happens. But I still like it's,
00:39:49
Speaker
It feels like in the same kind of headspace that I was in around, like there's like a marker to be like this thing is, you know, whether it's like, you know, just an empty file as a flag or like the MD five hash of that whole file. Yeah. And I dig that. And then the last, the last solution that we came to at the end of the, the last episode is this is very confusing because of the, the Laravel actions package that
00:40:16
Speaker
is pretty popular, but there is a package called, it's like Laravel actions by the dragon code or the dragon code actions, dragon code actions.
00:40:31
Speaker
Okay. Well, that's amazing. Or actions by the dragon code for Laravel is the official title, I guess. And right. That's a great title. I just want to, what a good package name. It's a little unfortunate, you know, just from, from a name confusion perspective, because Laravel actions is, is so popular. Yeah. But, um,
00:40:56
Speaker
It's essentially what you had kind of talked about at the very beginning. It is migrations, but for anything, right? It gets, they get the, the, whether they have run gets stored in the database, just like migrations, but it can be, you know, any type of thing and they can be scoped to an environment. So you might have like an action that's just, you know, at, at, at internet, actually we have a, you know, PHP artisan dev setup command that,
00:41:26
Speaker
does a bunch of stuff if you have a new dev environment, including pull a bunch of sample data and set up the NPM packages and all that stuff, right? And effectively we could replace that with something like this that's just a bunch of actions that only run in the local environment.
00:41:51
Speaker
And this is probably the other one that's most appealing to me because it's essentially all the things that we like about migrations, but it's just another space that's dedicated to things that don't feel right for migrations and are a little bit more
00:42:12
Speaker
abroad than migrations. And it kind of gets into what the Envoy solution is, where it's like, you can run anything. But because it's within the context of an artisan command, it doesn't have some of the advantages in that. If you need to change out the database connection or something like that,
00:42:43
Speaker
you're already inside of a booted app, it's gonna be problematic in a different way. And that's assuming the app can actually boot as well. Right, right, sure, yeah. But yeah, those are just the other things that came up and I think there's a lot to be said for all of them. I love how much there is in just this one little tiny question, right? Yeah, yeah, it's great. I didn't realize all this stuff existed so I'm loving it.
00:43:13
Speaker
Yeah, so Taylor also kind of had a solution to this, which he tweeted about quite some time ago, which was, again, it feels very similar to all these things, where it's, I don't know what the command was, but I believe it was essentially an artisan command. And the artisan command just had a list of classes. And we'll just call the classes like actions, right? So whatever it is, one of them is populate roles.
00:43:42
Speaker
And the class itself was just an invocable object. So it's just essentially just a function wrapped up in a class. And when you called, when you kind of invoked the class, it was, and I don't know how to say this word. It's one of those words that I've read everywhere. And then when I say it out loud, I'm always like, yes, thank you.
00:44:04
Speaker
Now I don't even have to say it. I need to record that and then I can play it every time I want to say it. I damn potent. Yeah. Yeah. Oh gosh. Yeah. So that, you know, they were all responsible for working out whether or not they should run themselves. So the roles. Okay. Okay.
00:44:22
Speaker
thing might be like, hey, do all of these roles exist in the database? If they do, just return. Don't do anything. If they don't, maybe insert the ones that don't yet exist. And that would allow you to update that when you add a new role that has to exist.
00:44:39
Speaker
If you had admin and moderator, and then six months later you want to add a super admin, you just go in there and say, add super admin to the list of roles. It goes, do all three of these exist? Yes, they do. Don't do anything. If they don't, all right, work out which ones don't exist and just insert that one, which is definitely an interesting approach as well. And then all you do is end up with a really nice clean list of just classes that are just objects. And you're kind of in that same.
00:45:07
Speaker
in that same world, except that the class itself is responsible for working out whether or not shit run.

Standardizing Deployment Tasks

00:45:14
Speaker
Right, right. Yeah, I like that approach. I mean, I like that approach. I wish, you know, I wish, I think what I like about some of the other things that we've talked about is there's a formality to them, right? Where you're, you're using sort of,
00:45:38
Speaker
Maybe it's not a core convention, but you're using some sort of convention that's prescribed by the package, whereas something like this, because we can come up with our own homegrown hack together solution for anything, right? This is- Oh, yeah. And I mean, quite honestly, a lot of times that's maybe the better solution for things, and maybe we reach for
00:46:06
Speaker
more sophisticated solutions too often. So there's, there's an argument for that too. But yeah, it just feels like if there was a loose standard around this type of thing, whether it's something like what you're proposing or whether it's this dragon actions thing or whether it's like a formal, like, um,
00:46:37
Speaker
like a formal flag in the migrator that tells like that you can do PHP artisan migrate and then you can do PHP artisan migrate dash dash data or something like that. And that's like a special thing or dash dash after and dash dash before to be like, these are the things that run before migrations. These are the things that run after migrations.
00:47:02
Speaker
I like, I would love to see there be sort of a standard way that's not standard just to one team, but like sort of standard to how you approach it in Laravel. But that said, you know, we've got, we've got a bunch of great options here. And for the most part, everyone gets, gets along just fine with, with what we've got. So definitely.
00:47:26
Speaker
But I like that, I like that idea of just a bunch of classes that are just really self-contained, responsible for figuring out, do I need to run? And then if so, this is what I do.
00:47:38
Speaker
Yeah, exactly. It's quite nice. And I guess in a way that's kind of influenced my thinking on the Envoy thing as well, where it's just a list of tasks to run. And the tasks themselves, again, they can be responsible for working out if they have to run, or you can use Envoy's kind of once command to like, it'll remember if it's run.
00:47:57
Speaker
You know, it's, it's almost like, you know, composing your solution out of all these ideas, um, I think is, is definitely probably most of the time the way to go. Right. Cause there's so much context that one's going to need for their house. For sure. Yeah. And I think probably your system, like the number of servers you're deploying to and the size of your team is going to change, change this pretty significantly for sure. If you're on serverless or yeah, or if you're on serverless for sure. Yeah. You know, if you're, if you've got.
00:48:27
Speaker
30 app servers to deploy to, or one app server to deploy to. It's going to change the story. Definitely. Or zero, right? If you are going serverless. Yeah. Amazing. Okay. There was something else I didn't actually realize was kind of in this world that I PR'd to the framework that we just like, I kind of like,
00:48:52
Speaker
lost motivation for it. But I thought it was really nice to begin with. And now I'm kind of coming back around to it after having this conversation, which is the idea of having, I called it like warm, like warming your application. Okay. So imagine
00:49:09
Speaker
A request comes in and the first time you hit this endpoint, you need to go and download a text file with a few IP addresses, put it in the cache, and then the next time a request comes in, it just can pull it out of the cache. Well, the first person that hit that had to pay the price of downloading those IP addresses from a text file of the Internet. That very first visitor has to pay the price of warming the cache. Whereas if you could do that as a part of your deployment story,
00:49:40
Speaker
then no one has to pay the price of the cache, or everyone kind of pays the price in the downtime if it's part of downtime, or maybe you can essentially go, yes, if the first request comes in, they have to pay the price, but maybe no one will hit it by the time we've actually already warmed the cache up. So what I kind of envisioned was, in the console kernel, there's a kernel.php in our applications, in our framework application.
00:50:10
Speaker
there's a schedule function. And the schedule function allows you to define jobs to run as a cron job, essentially. So you can say, every five minutes, go and do this thing. Every day, update this particular thing, or send this web request to make sure that I know my server's still running a cron or whatever. I imagined a new function under that called warm.
00:50:39
Speaker
which would essentially tie to a PHP artisan warm command. And in there, you could kind of warm your application. So you could say, all right, go download this file and put it in the cache. And so you do PHP artisan migrate, PHP artisan cache warm, and it would warm the cache or warm your application, whatever that would be for your particular application. And so
00:51:05
Speaker
So yeah, so you'd migrate the database, you'd run this command, that command would then populate, go and fetch that file, put it in the cache so that when that request comes in, it doesn't have to go and download itself.
00:51:17
Speaker
And then, you know, there's probably ways you could then implement some of these things we're talking about where you could say, you know, once on the end of it, like fluently call, you know, run this thing and then do it once. And if you do that, then it persists that it's run, it's done it somewhere. And then it's kind of not so much this huge new concept. It's just like the scheduler, you know, in a way that the migration aspect is just like the migrations. It's like quite nice that it's a similar concept.
00:51:46
Speaker
I love this. Okay. Okay. Because I, cause I'm thinking, I mean, let's, let's, let's over engineer this. Like why, why just have a warm option, right? We've got the scheduler, right? That, that says,
00:52:11
Speaker
you know, basically put this command in line to be run when some other command gets executed, right? That's effectively what's happening is like, tell the scheduler to run this command daily at noon or whatever.
00:52:28
Speaker
But really all that saying is like, put this in a list somewhere. And then when I run PHP artisan schedule run or whatever the command is, I can't even, you know, you, you set that up once and then you never look at again. So I can't think of what it is. Okay. All that's doing is saying like, here's a bunch of commands that have been queued up to run, which ones should be run right now. Right. So what if.
00:52:58
Speaker
there was like that same concept, but just generic where I can essentially say, put this task in this bucket with these constraints. And the scheduler is just one year. It's almost, it's almost a step. It's an abstraction above the scheduler where the scheduler would could be an implementation of the same thing. Right.
00:53:27
Speaker
where its job is to run all the commands that have been scheduled. But you could also have a warm bucket, right? That's for cashwarming. Or you could have a pre deploy bucket or a post deploy bucket or a pre migration bucket or a post migration bucket. You could just have
00:53:52
Speaker
any number of buckets as you need it. Right. And so, so now PHP artisan schedule run just becomes an alias for like PHP artisan command. I don't know what this would be called, but like,
00:54:07
Speaker
bucket run and then the next argument is like cron or something. That's basically just one of the buckets, right? And then you could provide the nice convenience features on top of that like once or on one server. The things that the scheduler does, you could pull the ones that are useful outside of the scheduling concept
00:54:35
Speaker
into the more abstract system. But that's kind of an interesting idea. Yeah. Now I'm getting excited about it again. How funny. Yeah, I think 100%, like, you know, obviously the scheduler can only run per minute.
00:55:01
Speaker
But we want to run at a specific instance. We don't want to have to wait to the end of the minute for Cron to trigger this thing. But yeah, being able to just be like, these are the things that you run when we're warming or when we're pre-deploying or whatever. Which, again, we can achieve this through artisan commands that are just in our deploy script or whatever. Event listeners that listen for the migration started or migration ended and do stuff. But I like the formality of having it in the kernel and
00:55:30
Speaker
the idea that it's the same concept and like you said, like being able to say, you know, maybe the warm commands, um, receives like a task builder, whatever. And you can say, all right, task run. And then you say, you know, whatever the task is that you have to do, you know, download this file or maybe it's probably accepts a closure, right? And you know, downloads a file, puts it in the cache and you can say, you know, on one server and it's the same API as the scheduler.

Pre-deploy Strategies in Laravel

00:55:57
Speaker
where it's on exactly. That's, that's quite nice again, that there's no new concepts and you get to use the existing concepts. I mean, frankly, it could be, it could be the schedule object, right? What if I want this, this task to run daily, right? Like if I deploy six times in the day, I only want it to run once.
00:56:22
Speaker
But if I, when I deploy, if I run it, you know, noon on, on Thursday, and then I do six deploys, but then I deploy at one PM on Friday, I want it that command to run again. Right. So you're, you're saying, you're saying that you want to be able to like to, to have the ability to say, all right, run this command on one machine once per day.
00:56:53
Speaker
when I deploy, if I deploy. Right. Right. Daily, daily now becomes like just an additional constraint. It's like, run it, but only run it once daily at most. Right. Or, or maybe it's once monthly or, you know, whatever, whatever. And you'd have to.
00:57:14
Speaker
you'd have to split it out, right? Because some things wouldn't make sense, right? Like daily at, right? It's not going to work in this context because you don't know what time deploys are going to run at. So the current sort of schedule object would need to be a subclass of some parent object that's got less configuration, but you could potentially move in more than
00:57:43
Speaker
you know, you've got without overlapping or then paying or like you've got a bunch of things that would make sense in these more abstract contexts. So then you essentially, instead of just having one scheduler that runs with PHP artisan schedule run, you would essentially have as many schedules as you wanted.
00:58:13
Speaker
And I might run PHP artisan schedule run every minute on a crime. And then I might run PHP artisan schedule run pre-deploy and run that every time I deploy. You know what I mean? Like, I like the idea of having it there or co-located. That's, that's quite nice. And it's the right place. It feels like the right place to put them. Right. Cause that's where we already.
00:58:41
Speaker
define all of these commands that need to run at different times in our app. Like it kind of, even if I didn't know where to go, that might be a place that I'd start, start just to see what happened. Hmm. Yeah. Yeah, definitely. I love this. Hmm. Yeah. I dig this as well. This is cool. And then, um, you know, right.
00:59:07
Speaker
Those commands could essentially do what Taylor's solution was, you know, in that command. It could just be, you know.
00:59:15
Speaker
before I execute, I check to see. And if, if there's nothing for me to do, I just, I just abort, right? Yep. And you can, if you have some task that's potentially going to take four hours to run, you can use process control to spawn off a background process, you know, you can, you can, or you can push things to the queue or.
00:59:38
Speaker
In your command, you can manage the resource needs in a bunch of different ways, but it's still the same single story of where and how you define things. Yeah. Yeah, I think that's nice. Interesting. I love that we came to a solution that no one can use because it doesn't exist yet.
01:00:06
Speaker
Yeah, 100 percent. That's that's the way to do it for sure, because then we have to build it. So that's nice. I I don't know. Maybe maybe I'm over. Maybe I'm oversimplified. I don't know. It seems it seems really it seems like a really good. It seems like a really good sort of like.
01:00:28
Speaker
synthesis of all the different things that we've talked about that uses Laravel's existing way of doing things really nicely. Does it actually, so it sounds good in a vacuum. Does it actually like coming back to the practical like problems that we're trying to solve? Like what would that look like in code? So, you know, we've got a, so writing a feature that needs to bust the cache then
01:00:58
Speaker
In the, I'd go into the kernel.php, the console slash kernel.php. I'd go into the, we'll call it the warm function for now. It's a warm function receives the task. Maybe it is just all in the schedule, maybe not, but we'll just give it a separate function for now just to kind of keep it separate. And I'd say task once, and then whatever the command is to bust the cache, you know, PHP, artisan cache clear.
01:01:27
Speaker
because I've tagged it with once and, you know, not, I don't care about other deploys. It's just like, just this one time I want you to run this. I guess we still have that problem of you need some kind of identifier so that you can, you know, that it's this particular iteration of the deployment that way we're going with. I mean, maybe once just takes a string. Yeah. Yep. Yep. Actually that's, that's a good, good point. Maybe. Yeah.
01:01:57
Speaker
Yep, some kind of identifier. And then we can merge that in. It gets deployed. The warm command gets invoked when we deploy. It busts the cache. Everyone's happy. Next time it deploys, it doesn't happen. Everyone's happy. So that works.
01:02:13
Speaker
Yeah, I mean, you know, to your point, the roles and permissions thing, right? Now, now we're going, we're creating say two migrations, one for the roles table, one for the permissions table. Then we're creating a new command or maybe we're creating a cedar that seeds the roles and permissions. And then we're going in the kernel and we're adding
01:02:41
Speaker
to the post deploy hook run this cedar once. It is, I mean, it's a little less directly connected for sure. Yeah, there's some separations there. You know, which I think any of these solutions that we've kind of talked about
01:03:10
Speaker
do have that fundamental problem of like, when you introduce abstractions, things are more abstract, right? That's gonna happen. I like it a lot for a lot of the things that you have to do as part of a deploy, you know? Like for example, right, we use Sidecar to,
01:03:41
Speaker
deploy Lambda functions for rendering PDFs with Puppeteer. And right now, when we went all in on Sidecar, we had to add a couple of different things to the deployment process, right?
01:04:05
Speaker
there's a command that has to run as part of our deploy to push the new versions of those sidecar functions up. Arguably, in this world, I could have just added like, we already have a post deploy hook set up, that's just PHP artisan, whatever, to run this new thing that we're describing.
01:04:32
Speaker
And I could have then just gone into the code and said, like, just put this sidecar command in that bucket to run. And then I wouldn't have to like mess with our actual deployment process to add a new command to the deploy, which is nice. I do like that. And I think for the thing that you're describing, like the caching, some, some sort of, okay.
01:04:57
Speaker
We just wanna grab some data file. We do this with a list of all of the official, what are they called? The routing numbers for bank accounts. We just pull that list of routing numbers and the bank name so that we can offer them for autocomplete in a couple of different places and validate them.
01:05:23
Speaker
So like something like that, right? Just, just make sure that at least every once in a while, those routing numbers get pulled down. And you probably got something like that in the schedule, right? Like once a day go and like, yeah, but if you're spinning up a new environment, then maybe that, you know, midnight isn't for seven hours and that new environment doesn't have that thing. I mean, you probably hooking it up to a shared cache, but yeah, yeah.
01:05:53
Speaker
Yeah. Yeah. I mean, I think that maybe we stumbled upon a great solution for another problem. Again, it's definitely a solution. I'm just not sure what to. Yeah. Yeah. We've got a hammer, man. We just got to find some nails. Exactly. This has been great. This has been really fun. We've been going for a little over an hour.
01:06:23
Speaker
I could go. I could keep going. But we could go, Dave, I'm sure. And I do want to just like put out there. Right. There are there are a bunch of there are a bunch of these. And I love if you have any of them that you want to share for for future episodes, I'd be curious, like one sort of sub topic of this or I think it's it's really an entirely it's a topic on its own, but like
01:06:51
Speaker
Another one that I really want to come back to is referencing specific records that exist in the database in code, right? Cause that comes up all the time.
01:07:03
Speaker
I know that this vendor, this specific vendor exists and I need to do some special thing and there's a whole feature in the system that's all about grabbing data from their proprietary system and pull it in, but it needs to reference that vendor and it's like,
01:07:25
Speaker
Do I hard code that ID? Do I use like some sort of like lookup table all the time? There are a bunch of different questions about how to approach that. How do I do it in testing? How do I make sure, you know, because an obvious solution is just seed all the vendors. That's why it touches to this. It's like, well, just seed all the vendors that you know you need.
01:07:51
Speaker
with maybe known IDs and then set the auto increment to a thousand so that the first thousand IDs are reserved. There's like ways to do it. But then for all the other features in my app, I've seated 80 vendors into the database that I'm never going to need for that feature test or something, right? You hit those same problems. Yeah. And there's a bunch of stuff around that and
01:08:18
Speaker
Like there, we've, we've, we've approached it in a bunch of different interesting ways. I think the, the one that seems, I don't think it's, I don't think it's the silver bullet. I don't think it always answers it, but sort of like inverting that where you actually store like the, the fully qualified class name of the thing in the database so that you, you are
01:08:47
Speaker
Well, I'll just leave it at that. Another one is another huge one that I think this, this, I don't know if this comes up for you all the time, but status transitions.
01:09:02
Speaker
and timestamps associated with them, right? I have a model that might go from like new to assigned to scheduled to completed, and then maybe to like some issue status and then back to new because like something happened where the whole process needed to start over, right? And I want
01:09:26
Speaker
I want to have a string in the database that is the status of that thing because I'm gonna be querying for assigned jobs all the time. I don't wanna be querying against some pivot table. I don't wanna be doing some weird complex query on a bunch of timestamp columns. I want just a column that's called status so that I can do the queries that I'm gonna need to do 90% of the time.
01:09:51
Speaker
And it can be duplicated data all the way down, but it's like, it's so much better to just see that value there looking at the database as well. Exactly. But at the same time, I'm going to want like timestamps associated with all those transitions. And a lot of times those timestamps are also going to be fundamental. It's like, well, I want to know if this was scheduled. When was it scheduled at? If this was completed, when was it completed at?

Status Transitions in Laravel Models

01:10:18
Speaker
And I think that there are a bunch of, there are a bunch of different ways to approach this and I have yet to find one that feels like this is the thing that I'm going to reach for first. I think that all the solutions that we've come across at least are things where all of them kind of have bad trade-offs and I don't, I'm not confident that I know which one I think has the least bad trade-off. Yeah.
01:10:44
Speaker
Another one is custom. In that particular problem, I think the thing that, you know, now I'm going to under-engineer stuff because I haven't thought about this at all. But the idea that like, you know, are you ever going to have to query against the updated date of the status?
01:11:01
Speaker
If you're not, it doesn't have to be a dedicated column. You're never going to query against it. Or if you are querying against it, is it something like you're manually querying against so you don't really care about performance? In which case, whack it in a JSON column and then just have a status updated at or status. That could even be an array so that you can keep the timestamps and see that now you're doing event sourcing in a JSON column in a database. Maybe that's not a good idea. I don't know.
01:11:28
Speaker
No, I have been down that path. Another one that I have on my list is custom relations. And I don't know if this is a thing. I wonder if this is only a thing that I ever deal with.
01:11:44
Speaker
but we deal with customer relations all the time. And part of that has to do with, with we've got a bunch of legacy data that's not modeled in a way that Laravel's eloquent really likes to work with. And so in a lot of cases we'll just deal with the inefficiencies and it's fine. But in some cases the data is, is
01:12:11
Speaker
used so heavily that we need to query it in really performant ways.
01:12:16
Speaker
And so there'll be a lot of like these, these very, very specific customer relationships that have to do weird joins or unions or joins and unions against multiple columns. And, you know, have to handle the eager and non eager scenario. And I just, I, I, I'm more just like, is this something that other people deal with? And how do other people approach these, these,
01:12:44
Speaker
any data that's related and not just, oh, this is a belongs to or has many, you know?
01:12:50
Speaker
I want to hear this episode. I'm nowhere near smart enough to even contribute to the episode, but man, I want to hear this episode. The fanciest stuff I get done with Eloquent is like installing one of Jonas, is it Stademy? Jonas Stademy's packages. That's as fancy as I get with Eloquent. You know, there are some, he's got some mad packages out there. Yes. Are incredible. And we do the, you know, the limited like,
01:13:17
Speaker
being able to add limits to your has many's and stuff like that. Like there's, oh, let me just go grab one of his packages, install it. And then I can just add a limit to the relationship and it just works under the hood. It's doing all these wild like table or one of the window functions and stuff that I have no idea how they work.
01:13:38
Speaker
but we've never had a problem. Yeah, exactly. They just work. They're so good. Yeah. Relations in Laravel is my little pet peeve. I feel like
01:13:54
Speaker
I have in my head sort of like a way that I think relationships could work that would make building complex relations a lot easier, but because, I don't know, I can see why they kind of evolved in the way that they do, but now it would take pretty much a whole overhaul of that system, which obviously no one wants to see happen.
01:14:23
Speaker
but I just bump into relations all the time. But I'm wondering, do you have any of these that you're like, man, I bump into this and it's always like, eh, we can make it work. But, uh, I'm sure that they are right. Like we all bump into things like that. And then, you know, you bump into it again at some, you know, later down the line.
01:14:42
Speaker
I don't think I haven't got anything off the top of my head, but I'm definitely going to start like writing them down as I come up and I'm going to be sending them to you so that you can solve them for me. Amazing. I love it. I love it. Well, this has been so much fun. Agreed. And, and now we're going to have to like build a whole thing. So I love that too. I'm going to be thinking about this and maybe I, you know, I was wondering, is there going to be a third episode on this topic? Cause that was the other thing like,
01:15:12
Speaker
I kind of envisioned this idea where we have these like mini seasons of, of two or three episodes. And I, and I was, I was kind of thinking, I don't know if there's going to be a third episode, but maybe there will be. We'll see. It's going to be, I'd love to hear it. But, um, yeah, I don't know. Thanks for, thanks for coming on. This has been great. Yeah. Thanks for having me. It's been fun. I hope, I hope we do it again. Yeah.
01:15:39
Speaker
Please. I can, I can over engineer anything you throw at me. That's my bread and butter. I got it. All right. Uh, good night. Catch you later.