A composable, versioned toolkit for Laravel projects

Welcome to No Compromises. A peek into the mind of two old web devs who have seen some things. This is Joel.

And this is Aaron.

We like to keep a unified set of tooling, and we want to always bring our tooling forward when working on our projects. So, if we're using Composer, we want to use the most recent Composer within reason. You know, the newest PHP, the newest Laravel. And it's nice to kind of have a reference on how we do stuff. So, for a couple years, we've published a sort of skeleton of maybe, "This is what a Laravel project would look with all of our tooling set up in a brand new greenfield thing."

And without any functionality, just kind of like, "Here's all the tooling, probably configured the best that it would be on this project, as if it was a real project." And then you can take a look at that, it's on our GitHub page.

Recently I was looking at it and I'm like, "Wow, it's been out of date. We haven't updated it for a while. There's new Laravel versions, all that kind of stuff." And I decided to start looking through it and update that. There's a lot that's changed since last time we touched it.

Please elaborate. Because I have some things in mind, because I think I did the last update, but I'm just curious. Because when we talked about doing this, I was thinking to myself, "There's not that much that changed," so I look forward to hearing what you came up with.

It was over a year ago that the last update happened.

Oh, man.

So it's on Laravel 10.

Well, to be fair, we don't actually... It's not about actually creating installable Laravel application.

Well, right.

It's more like how we set up the Docker containers and our Composer scripts, and things like that.

Right.

So maybe I didn't actually bump it forward. Because I thought I did it for at least for Laravel 12, but that still would've only been six months ago. All right, I trust you.

Yeah, basically that was my consideration. I was like, "I know that we're not creating a skeleton." And a lot of the projects we go and work on, we're not starting brand new, so it was always kind of a skeleton-ish project, but-

It's a reference. I think of it as a reference guide.

Yeah. So as I was kind of doing this process, thinking about how to update it and upgrade it, I started thinking of two different challenges that came to mind. One was, we have a way of doing stuff with Tailwind 2 or Tailwind 3, or with View 2 or View 3, or with Laravel 10, and then stuff changed to 12.

So there's kind of this history in some of the projects we work on aren't always the most recent version, or stuff like that. And if I want to look at maybe a client's project that is an older version of something, I might actually have to go back through the history of our project standards, into old commits, to try to find out when we had set up something that was more in that version.

Okay.

I didn't really like that idea when I was starting to look forward. I was like, "There's some good stuff here that would be fine if we're touching something with a little bit older stuff." And when I say older too, sometimes we choose to use a little slightly older technology because it's more proven.

For example, when a new version of Livewire comes out, I want to use it but I'm not going to use it right away. I'm going to let some of the features and bug reports and all that kind of stuff work through. It's not always that the newest version is even what we want.

We have this sort of juggling of, this is how we do it at a certain version, we want to do it for newer versions. Some of our projects we need to know the older stuff, some of we need to know the newer stuff. I started thinking about like, "Well, maybe I should make tags." Tags for different stuff like that.

Sure.

We can kind of look through that history and see what that is. And then I kind of started thinking, "Okay. Am I being a little silly here?" Because I am attacking it like a project, like a installable thing, yet we keep saying it's not installed, it's not something to build off of.

So why am I stuck at building a framework-based configuration here? Can't I, I don't know, read the manual and find this stuff myself? Well, yeah, but then I have to remember where to find all this stuff, and I have to remember how I did it, and maybe I have to look in old projects.

Then I started thinking about, "What are some of the things that I know about programming in general?" So back up away from PHP and Laravel, and what I've learned from other languages, JavaScript, and you know, even some of the stuff I'm learning about AI.

And I started thinking like, "Well, there is ways to kind of restructure my thought process about this set of documentation or tooling," is let's make it into more of a composable sort of thing.

So instead of having a whole project installed, because a lot of the stuff... Like, we install Laravel and then we, you know, modified a few files, the rest of it we never really touched. It was just more configuring external additional tools.

I started thinking, "Why do I need to actually have a version of that configured?" Most often I've done it in a client project and been like, "Yes, this is what I agree with at this time, I'm going to keep a version of that."

You know, and kind of say like, this is the most newest version and our best practice. Why don't I just start making folders based off the tooling and the major version, and put the needed files inside of there? I'll give you an example.

I just want to make sure I'm following. If you looked at our thing before this change, you would've seen essentially the Laravel app skeleton. Like, 95% of it we didn't touch.

Right.

And now you're saying, what if it's only... Like, it might still have bootstrap/app if we do something in there, but it's not going to have config whatever, if we don't touch it?

Probably not, no.

Okay.

I'm saying one step further removed. Which is, what if we sort of said at a top level what are some of the toolings that we use and what are some of the versions? I'm going to pick PHPUnit.

I know that the PHPUnit configuration for the XML, PHPUnit XML, is different for our configuration between 11 and 12.

Correct.

Some projects have 11, most have 12. I want to have both of those XML doc... I want both of those space and time available to me so that when I go to a project, I know, are we using 11? Are we using 12? Can I look at my reference and pick the thing I want from there? I would make-

So if 13 is what we use, then you just create a new folder for 13?

Yep.

And if it's the same as 12, would you just have two copies of it, or you haven't got there?

Yeah. Because that would be what... I would doubt it would be the exact same because...

Right, it never is.

Is, you know, if we're looking at stuff, stuff's going to change in that file, but also we learn something new each time. I'm not saying I have to go back and update some of these, I might if I re-implement it again. If I've learned some stuff that all the way I get to PHPUnit 13 or something, I've learned how to do something even better in PHPUnit.

For example, linking to the XSD that describes the functionality inside of the XML file. I didn't know about that until maybe 12. Then next time that I touch the 11 stuff, if I find that helpful, I might put that functionality in there if it's useful.

But I'm not really concentrating on updating stuff that's older. I have a pretty good concept of, at that point in time, what I thought was best and I'll update it if I have to reach back there.

Yeah. Because I'm just thinking, the thought process I'm having is like, what if we had been doing this from the very beginning? PHPUnit, whatever, 4 or 5. Like, you'd have all these folders, you're clearly not going to go back through every single one.

Right.

Your rule of like, hey, if I'm about to pull in, like we just joined a legacy project or something and they can only run PHPUnit 8, then we might, "Oh, you know, this isn't quite how we do it."

Maybe we then decide to update the version 8 in our repository, but that's the only thing that would cause us to go backwards.

Yeah, exactly.

Okay.

So I'm thinking about it's less of a sort of project structure and how all these things fit in but more of like a top-level tooling set where it's like maybe there's a README that describes why you might use each one of our tooling.

And by our tooling, I mean our configuration of a tooling that exists in PHP. Why might we want to use this XML? Or, why would you go inside of PHPUnit folder? Well, there's going to be an XML and maybe some other stuff in there.

So it kind of says at a high level, you might go into this folder to find some configuration, go find the version you want, and then that's kind of our reference going forward.

All right. Is there a folder for Laravel then, or that doesn't exist?

Well, I don't know. I haven't really thought that through because-

Okay. Let give a specific question because maybe this will help us decide. PHPUnit is a good example because there is the phpunit.xml. And maybe there's a Composer script or something that we'd have in a Composer.json.

But there's also ways that we structure tests. Like, we like to use refresh database and we like to have... I know we have a deep inspection trait that we bring in sometimes. Would all of that stuff live in the PHPUnit folder because it's test related? Or, what are your thoughts on that?

I don't know. That's an interesting question. I guess maybe if you agree with this process, I'll find out when I make this change.

Okay.

But I don't know if I would say that would go in the PHPUnit. I don't know. Because on one hand I can see because it's related to that, but on the other hand it is actual configuration customization we're doing to the Laravel project.

Correct.

Which would be nice to see in context. And this is a really good example because that is a place in the Laravel project that we do heavily modify, but other places we don't. So, if you were saying, how do we register Horizon with a specialized gate check?

I might just have that in its own Horizon folder, I'm not sure. Because it's why I don't necessarily need a whole application to show one or two different lines inside of a service provider, and I was afraid it might get lost in there, too.

It would. For sure it would. It's like, "Well, which of these are the files that I actually changed that are relevant that I need to bring in?" I mean, Horizon's another good one too, because we use Docker and we have a Docker composed file and we would have to have a container for Horizon.

Like, would that go in the Horizon folder, or is there a Docker folder that would have all the stuff possible in it?

Yeah, I think about that as maybe... Again, we're just kind of brainstorming, I guess we'll find out when we publish this repo. But I look at the Docker stuff as it all kind of goes together.

So in my mind, even though I might implement JavaScript inside of my Laravel project, I'd probably not necessarily have the JavaScript in the Laravel folder. It'd probably be in some other folder named after the JavaScript functionality.

Okay. I mean, it definitely solves a problem. I have felt the pain of... especially when you're straddling versions. Like PHPUnit bumped major versions pretty rapidly in the last few years so even with some significant changes to how the configuration file is built.

So I do like the way this approach solves that problem of versioning. And you know, we still have some more things to figure out in terms of how the technologies overlap. Again, we've talked about maybe we use Pest for the browser-based testing.

That's going to interact with our PHPUnit test so do we just have a testing folder then, or do we still have a Pest and a PHPUnit folder? We'll have to figure that out.

Right. And then does the testing mean, "Well, that's JavaScript testing in there too," you know.

Yeah, right.

There's a lot of stuff to figure out. I'm sort of going at the level of a technology is the organization. Because that's kind of how I tend to think. And I don't think about Horizon.

I think about Docker and then I think about Horizon in Docker. I don't think about like, "What do I need to get Horizon working?" I think, "What do I need to get Docker working for this Horizon container?"

I would feel the same way. Because there was a project where we just added Horizon to it. I wouldn't look for Horizon folder, I'd go to the Docker configuration. Like, "How did we set up the Health Check for Horizon and have it restart itself?"

And all that stuff's going to be in Docker and that's... I think about it the same way.

So I think the takeaway here is, you know, we kind of looked at this and I said, "I'll make some changes." I wanted to kind of surprise Joel on a podcast. We hadn't talked about this before, see what his thoughts were.

And then to kind of look at stuff you're doing over the time and say like, "Does this still make sense?" I don't think we were wrong with how we set it up the first time, I just think that we're more right now.

Yeah, it served a purpose at the time and it worked great as we were rolling this into new projects. But now that you've lived with it for a while, you're like, eh, it breaks down in a couple places, so I think this is a good evolution.

Aaron, I wanted to talk about your vinyl collection. You have a collection of records.

I absolutely do not.

You don't?

No. I listen to lossless audio.

You're going straight from the masters and straight into your bone-conducting headphones.

Absolutely.

Okay. Well, I just find that weird because this is a record podcast, right? We talk about records. I mean, when I... 10 minutes before we recorded my calendar thing, said, "In 10 minutes, record podcast."

Oh. Documentation is so tough. I don't even know why I took on this project of making more documentation for ourselves. Ugh.

You know what? Developers don't like documentation, myself included, but it does really add a lot of value to the project. If you'd like help getting your project better documented, head over to nocompromises.io and we can help.

No Compromises, LLC