Fake drivers with Jason Beggs

Michael:

Hey. I'm Michael Dyrynda.

Jake:

And I'm Jake Bennett.

Michael:

And welcome to episode 163 of the North Meets South web podcast.

Jake:

Hey, everybody. Welcome. Welcome. First question that I want to ask everybody who's listening. Have you ever watched one of our videos on YouTube?

Jake:

If you have, and you can reach out to me on Twitter, I want to know if you clicked on the link because I had such an incredibly awesome background. Now here's the here's the, you know, the gist of this. Michael and our guests today, mister Jason Beggs, so I'm gonna introduce himself in a minute, were giving me crap about the fact that I have a white shiplap background and a missing light switch cover on my wall and have for, like, last forever for both of our podcasts. I mean, how long have I been podcasting in front of this wall? Usually, I have a happy birthday banner up here too Yeah.

Jake:

Michael. It's been years.

Michael:

Look, at least it's not your bedroom anymore. So that's that's

Jason:

It's true.

Michael:

Step up.

Jake:

That's true. That's true. So speaking of cool backgrounds, yes, Michael's background is looking very nice. And then our guest today, mister Jason Beggs, he's got some, guitars back there. So everybody welcome Jason Beggs to the show.

Jake:

Jason is a member, now officially, of the Laravel team. If you go check him out on twitter at jasonlbeggs, x.com/jasonlbeggs, you'll see on his cover image, he has a picture of the entire Laravel team and then an arrow pointing that says me. And there's Jason. And I think Jason just, like, in the middle of Laracon, accepted, a position on team Laravel. So, Jason, congratulations.

Jake:

Way to go. Nice job. For anybody who might not know you, you've been in the community for quite a long time. But tell us a little bit about yourself, kind of what's your background in Laravel? When did you get started?

Jake:

And, what what brought brought you on to team Laravel? What are you doing over there? All those things.

Jason:

Alright. That's a lot.

Jake:

Let let me start let me start easy. How long ago did you start in Laravel?

Jason:

Yeah. So I think probably 7 years ago. 7 to 8 years.

Jake:

Yeah.

Jason:

Yeah. So I've been working with Laravel in various JavaScript frameworks and then Tailwind for as long as it's been available. Yeah. Yeah. I don't even know how I got involved in all the projects I've I've helped with, but at some point, I started.

Jason:

I converted the Laravel News site to Tailwind, way back, I don't know, 4 years ago, probably. And then after that, Taylor hired me to convert to laravel dotcom site to Tailwind, then I ended up building a redesign. And then I built the Laravel News redesign, and I I don't know. I just ended up building a ton of different, designs in the community for Laravel and other people over the years, and they asked me to help with the the latest project they're working on, Laravel Cloud, and, did help with it up until Laracon probably 2 months before Laracon I started. And then, right before Laracon, they decided to, invite me to join the team officially.

Jason:

Finally talked me into it. So Nice. We announced it. Yep.

Jake:

That's awesome. I know that, like, your, your MO, what you're well known for is, like, pixel perfect, compliance with design files. Right? So, like, somebody hands a design file off, and I've heard other people say this, like, everything is spot on, like, to the pixel. Like, margins, exactly perfect.

Jake:

Padding, exactly perfect. And so you've made a bit of a name for yourself just being the guy who can deliver on getting everything to look exactly as it looks as a designer hands it off. Give it to Jason. Jason will knock it out quickly. That's the other thing.

Jake:

I've heard you just, like, knock it out. Like, you just, like, go after it, get it done quickly, and it's done perfectly. So, yeah, I think, your work has lent itself to, giving you lots of great opportunities, which is which is pretty cool. And I've even tried to hire you a couple times. I think at one point, I maybe did have you do something for us.

Jake:

I think maybe I actually you might have you might have done a tailwind site for me one time. I think I had it designed, and I think you did it. Maybe. I think that happened once.

Jason:

Maybe. I don't know. I think so. Said many. I don't remember.

Jake:

Yeah. Yeah. I think that happened one time. But, anyway, stoked to have you on the show today. And, actually, we're not on the show today with you to talk about, like, the design aspect of things.

Jake:

We actually are on here to talk about something else that you've been working on, at Laravel. So, like, the Laravel cloud platform. So we could talk about Laravel cloud because it's kind of unveiled and, you know, it's out there in the public. I know, however, there are some things that we might have to dance around a little bit as far as, like, how we talk about what we're gonna be dealing with today. But just to get down to the nuts and bolts of it, essentially, and I'll put this preface out there.

Jake:

It is 11:30 PM Jason's time. So, thank you for coming on the show, first of all. But we're gonna try and get to the point of this so we can kinda get through some things before, before it's midnight. But you had put out a tweet the other day, that said, you know, in Taylor's talk at Laracon, everything in his talk was hitting real infrastructure. Like, it was actually spinning up real servers.

Jake:

It was actually provisioning new databases. It was actually spinning up queue workers. Right? All of those things were happening from the stage for real on the Internet. Yep.

Jake:

However, like, some of the difficulty, with that is when you're handing things off to someone who's not necessarily going to be interacting with or maybe you don't want them to have to interact with the real version of those things, like a designer, like yourself, or somebody who's dealing with interactions and things like that. The question is, how do they get the application to function as it would if it was hitting the real thing but without having to give your own credentials for, like, you have to set up your own, you know, AWS, credentials and do all that nonsense. Right? So that's that's really, like, the challenge. As a as a designer, how do I see that model that pops up after a database is provisioned without actually provisioning the database?

Jake:

Do I have to do some janky weird sort of, you know, voodoo magic to get it to pop up and now I can design it? And so what you had said is you have since gone back and added fake providers for different things inside of Laravel Cloud so that you can basically interact with the application locally without having to ever hit anything outside of your local machine. Does that sound like a pretty good summarization of of what you were saying in that tweet?

Jason:

Yep. So, basically, the whole 2 months before Lyricon, when I was building a lot of the front end, I didn't have access to any of that stuff. Didn't have any of it set up locally, so I was kinda flying blind on some of it. I would have to hard code, like, show the database pop up or whatever Mhmm. Mhmm.

Jason:

Just temporarily just to be able to see it. And then Nuno, he would, like, test it in the dev environment and stuff where there was actual everything was actually set up, or, like, for Git installations, like, I'd have to, like, manually go in the Git provider's table and add an entry just so it would, like, recognize that there's a repo in there. So, yeah, it's a little bit difficult when you're dealing with a lot of different, APIs and different, I guess, third party providers that you don't necessarily even if you do have access to them, like, you don't necessarily wanna spin up an AWS server every time you hit deploy locally. Like, if you're just if you're only wanting to test, like, front end interactions, you don't wanna have to actually spin up a server and then figure out how to spin it down later, and, like, you're just gonna waste a lot of resources and money doing that kind of thing. So, yeah, Nuno kind of spearheaded that effort.

Jason:

He got the Git providers set up, and then, he made the Git fake kind of provider. So, like, when you basically, how it works is when you first set up an application, if it's the first application that you've set up in cloud, it'll pop up and show you, like, connect to GitHub or connect to GitHub account, connect to your GitLab account, connect to your Bitbucket account. And then if you're locally locally now, we have a 4th option that's called git fake. So if you click that button, it kinda just loads a hard coded list of repos you can choose from, and each of those repos has hard coded branches so you can just kinda select those and go through it, and everything just kinda works locally. You can even fake the deployment.

Jason:

It'll recognize that it's a fake repo, basically. It'll fake a deployment, like, go through all the build steps. It looks like a a actual deployment and everything. It's pretty cool how it works, but

Jake:

yeah. But what it's actually doing on the back end is is so, like, let's talk through that, like, git fake one. Right? So Yep. You would I'm guessing you have some sort of, like, driver or or something like that where you say, like, it's it's GitLab, it's Bitbucket, it's GitHub.

Jake:

Right? Is it is that kinda how it works? It's like you have some driver implementation that says, like, we are going to talk to this particular type of API endpoint, And then do you have some sort of, like, interface or contract that says, like, it should interact with these particular methods, but you're swapping out the drivers? Is that kinda how you're doing that?

Jason:

Yeah. So, basically, there's a source provider interface that the GitLab, GitHub, and Bitbucket, classes all implement. Then there's also the Git fake one, which basically, like, each of those has, like, I don't know, Git account details methods, Git repos methods, Git, branches methods, that kind of thing. And the git fake one, basically, instead of hitting an API that goes out to GitHub, it just returns an array of repos. So, like, we have, like, Laravel slash beep, Laravel slash cloud, Laravel slash forge, all in there.

Jason:

And that's basically the options we get to see. Select one of those and when it creates the repo in our table or repositories table, it'll set that type on the repo's table. I think it's on that table. It could be, I don't know, Somewhere it sets a type that's git fake. So then every time that repo is loaded, it knows to use that driver, basically.

Jake:

Yep. Yep. So to interact with anything on that particular, you know, record, that that repo record, it's going to use the Git fake driver to do that. No. I I absolutely love that idea.

Jake:

And the one thing I was gonna say, and, Michael, I'd love to hear if you guys have had to do anything similar to this, is it also makes it much, much easier to test the non happy path. Right? Because if you're dealing with a real provider, trying to figure out how you get it to actually give you an error, like, if the username is incorrect or if there is some sort of rate limit that you hit or if you don't have permissions to get access to that particular repo or if there's only write access and not or, you know, there's only read access but not write access or things like that. Right? Like, what are all the different non happy paths that could exist?

Jake:

And they might be different based on which provider it is. Those are very difficult things to do or to, imitate so that you can see the UI when you're dealing with real world implementations. And so one of the things that a fake provider does is it allows you to be able to, in the way that we've done it in the past, and and that Stripe and some of these other things do, is they'll have, like, magic tokens essentially. Right? We're all familiar with, like, when you're dealing with a Stripe sandbox, you put in 4242-4242, that's a thumbs up successful.

Jake:

Put in 414141, it's like, that's a CVV error. You put in, you know, 43, that's like a insufficient funds. Now that's not really what it is, but those are the ideas. Right? And so with these fake providers, you can even, have this idea of if I select this particular repo that's getting returned, I should get this type of error when I try to do some particular thing.

Jake:

You know? So that's one of the really nice things I like about those fake, those fakes is you can test the non happy path much easier. Michael, have you ever implemented this sort of pattern in your own code base?

Michael:

No. We've I mean, we we use a lot of, the saloon library that for HTTP requests. And so we we fake things, but not not like and that's faking them in the context of our test, not to the extent

Jake:

we're being

Michael:

able to kind of swap these things out with fake responses in the execution of, you know, opening the application in the browser. And I think that's a really good idea, especially when you've got other people that need to look at it, you know, whether it's product people or it's, you know, UX people or QA people, whatever, in your business, where they wanna be able to go and just, like, click through and do some exploratory testing and and test different scenarios. There's there's no real way of doing that. As you say, Jake, there's no way to kind of fake what a failed response is or something's inaccessible. So I like I was really, interested with this this approach that you've got in terms of, you know, effectively being able to fake out the entire real application in in a dev or, you know, QA environment to kind of be able to do that and hand that off and and see what it actually feels like to experience it.

Michael:

Because it's one thing to write a test with a fake that's like, yeah, when this happens, we hit this endpoint, we get back a a failure. Like, this is what we expect to see back in the response. But in terms of seeing the actual application in the context of, like, this error comes back or you get this toast not or this modal notification or whatever based on the inputs, I think that that was what really interested me the most. And in a large application where you would have to, like, retrofit this stuff and figure out, like, you we certainly haven't. People probably don't implement in with this kind of behaviour in mind, where you can fake out interactions with external parties quite so simply.

Michael:

So, yeah, that's that's why I kind of said, you know, I'd be interested to to hear more about this and and how it might be implemented because I think there's there's certainly a place for in you know, a lot of these real applications that are they're hitting up external services.

Jason:

Yeah. I think, it's easier in cases where you you have multiple providers anyways because typically, you're gonna have some kind of interface that they all adhere to. So it's a lot easier to to write a fake provider for Mhmm. Those cases. But, there are a couple cases like the deployments and stuff, where we're just using dependency injection to swap them out always locally no matter whether it's a real repo or not, which I might we might change that to where there's a EMV variable or something that you can use to toggle it.

Jason:

So you could test real deployments locally if you wanted to. But,

Jake:

I love that you're talking about this because if we have dealt with the same thing, we so, go ahead and finish your thought if you if you have more to say on that because but I I wanna loop back to that in just a minute as far as, like, how you're swapping them out with the ENV as

Jason:

well. Go

Jake:

ahead. Yeah. So, so what you're what you're saying there is that, you know, with the deployment, for instance, typically, you're deploying the same way every time. Right? Like, you're saying, like, we're always deploying to this particular location.

Jake:

Our deployments always act the same way or interact the same way. But maybe you've now extracted that sort of to a contract so that you can have a fake deployment driver as well. Mhmm. And so this is what we do currently with anything that interacts with a third party API. What we do is we always we always do this regardless of if we actually can interact with it for real or not.

Jake:

We will create a contract first. So I will create let's just say we have a Laravel cloud. Let's say I'm talking to the Laravel cloud API from my local application. So I would create a Laravel Cloud contract, but I just call it Laravel Cloud. That's that's the contract, but that's the that's the class I'm gonna call it.

Jake:

Actually, sorry. Let me let me let me re resay that. I'm thinking through how our how our convention is. We call we call it Laravel cloud, contract. Then we would have a Laravel cloud HTTP gateway, and we'd also have a Laravel cloud fake gateway.

Jake:

Then what we do is in our service provider, we bind to that contract either the HTTP version or the fake version, depending on whether we're in dev or act actually, not depending if whether we're in dev, if we're in production. For in production, we bind to the HTTP. If we're in dev or testing, we bind to the fake. And then we also have a facade that references that, and we would just call that Laravel Cloud. So that facade would say Laravel Cloud.

Jake:

So that does the service location to go grab out of the container whatever was bound in the service provider. And so in that way, it's really nice. In our tests and in our local dev, we just hit we just locally hit it. No big deal. But in the case that we want to test it with production, we do exactly what you're talking about, which is we have a ENV that says, should fake, trying to remember how we use it.

Jake:

But, basically, should we use production in local? And then in our service provider, we just say, like, if not in production, so or or if in production or should use production in local, then we load the HTTP one. And so, it is nice sometimes to be able to hit from local the real thing, but not super often. Most of the time, we're wanting to hit the fake. But that's how we do it on almost everything is we always swap them out at the service level, or the, you know, at the service provider level, typically, because we don't have a lot of things that have 4 or or 5 or 6 drivers or 2 or 3 even.

Jake:

It's usually we just have the HTTP and the fake one. But, yeah, I could I could see that being, you know, especially where you we only have, like, the one sort of deploy method. You have to extract that to a contract in order to be able to use a fake so you can see that the other way. So that's interesting. I I'm it validates for me so much of what we've been doing for the last couple years.

Jake:

I talked I remember talking to Sam Curay who built Saloon and asking him these same questions. And he was like, no. We don't do that. We don't we don't provide fakes like that. That's not something that Saloon does.

Jake:

And I was like, really? Because we really need that. And so it makes me feel so glad to know that team Laravel is doing the same thing, that pattern. It makes me feel so happy inside.

Jason:

I think A couple of people have mentioned, like, maybe extracting it to a package, but I'm like, I don't know the the different implementations depending on what services you're interacting with are so different. Like, I don't know how you could even start to extract that and provide, like, good APIs, but I don't know. Maybe somebody smarter than me can figure it out.

Michael:

Yeah. I think I think in the in the context of Saloon, you'd you'd have to introduce a layer on top of it. Like, everything would have to create, like, some service class that accepts the connector, that that swaps those things out. And so you you're always gonna be building an extra layer on top of it, in order to facilitate that kind of thing where you can kind of switch out. Like, this connector goes here, and it returns, like, fake responses for everything and things like that.

Michael:

So it's definitely it's more work, but I think what it what it provides you at the end of the day is is really good. Like, we have a lot of issues with with Salesforce, because we use Salesforce for, the CRM component of our main application at work. And there's not really a good testing story for it in terms of a UAT environment that you can hit to do things with. Like, we have to spin it up. And that that environment is run by our, product people.

Michael:

It's run by our Salesforce developers. So it can, like, change state and things like that all the time. Whereas if we could swap out how we're connecting to it and just return fake things based on the inputs and things like that, that would be a lot easier. I suppose it's never really occurred to me to, like, these these things that we do in testing to swap out the underlying connection to return some fake data to then figure out, okay, well, how do we actually translate this into running the code, you know, in the browser? Good for thought, though.

Jason:

Mhmm.

Jake:

Yeah. To me, I think people typically think, well, I have, like so here's the reason why I don't think it occurs to many people to do this and why it occurred to me so early. I'll start with why it occurred to me so early. And I'm not saying because, like, I'm not I'm not patting myself on the back. The reason I'm saying this is because we had an application that depended solely on having a connection to an API.

Jake:

This API connection was crucial. Like, we could not do anything without this API providing the data that we were going to display on the screen. You could think of it almost like an SPA with a API layer back end, but it lived, like, over there. So our Laravel application was fetching stuff from this API and then displaying it on the front end. Well, the there was no way.

Jake:

This the system that we were interacting with with this API is this crusty, old, old, old, old, old legacy legacy database. And so it just became painful so quickly to try and get in there and modify those values in order to get what I needed on the front end. I was like, there's gotta be a better way to do this. And so, actually, I was watching Adam Wavin's course testing Laravel, and he talked about this with Stripe way back then. So, like, hey, instead of it, like, interacting directly with Stripe, create a gateway that's going to interact with Stripe.

Jake:

And then you can create your own implementation locally so you don't have to constantly hit Stripe. I was like, that's genius. So that's what we did. And, I don't think many people have that. I think most people are on occasion, you know, once in a while going and hitting an API to go update a one resource, and that's it.

Jake:

Right? And so it doesn't really matter for them. It's not, like, critical if they fake it out or if they don't. But in this particular application for you, Jason, like, everything you're doing is basically gluing together somebody else's repo to a cloud provider. So it's like everything is API interactions.

Jake:

Everything is like that. So how do you do that? How do you, you know, view the UI without having those actual APIs? And so now the need has arose for you guys to be like, we gotta figure out a better way to do this, at the same place I had to be, like, 6 years ago when we were trying to figure this stuff out. So, it is just interesting how you end up arriving at some of the same conclusions and some of the same patterns.

Jake:

What I would really like to do, I hear you saying somebody's extracting this. So thinking about extracting this to a package. I don't know if Taylor's accepting talk proposals for LairCon for next year yet, but I was talking to a couple people for about, maybe making this into a talk idea. Because I really do think there's a lot of interesting patterns around this and how you could introduce some of the benefits of this in a way that, I think would be really beneficial, not even in a package. You know, like, if I like, in my state machine stock, I never talked about a package.

Jake:

I think it's because in some instances, you don't need one. Right? In this instance, I don't necessarily know that you need 1, and I don't know that there's a one size fits all solution for this sort of thing. But if you understand the concepts, it's not that hard to introduce it yourself. Yeah.

Jason:

Yeah. Yeah. The the code is really simple. Like, it's not complicated at all

Jake:

Mhmm.

Jason:

But it's so powerful. And, like, before, like I was saying, like, I couldn't, like, do anything hardly in the app, but now you can literally get clawing the repo, set it up like a normal AOL app, and you can go register an account and start clicking around, doing anything you want pretty much, and it all just works. Like, isn't actually doing anything, but, like, you can do all the front end interactions locally, and it just works. It's pretty sweet.

Jake:

Yeah. I know. Incredibly powerful. So, Jason, if if, you know, you're you're thinking through, like, for Michael, like, if he's interacting with Salesforce, you know, just kind of using what you guys did as a reference maybe for, like, the deployment thing. You know, he doesn't have, like, multiple drivers necessarily, but, like, he just has this one thing he's interacting with.

Jake:

Like, what would you suggest as, like, maybe a path or, like, even, like, a first step to, like, how could they start to implement this pattern? What would be, like, the first thing they would need to do in order to be able to make, like, maybe a fake Salesforce thing? Yeah.

Jason:

I guess it depends on how your current code is architected. If you have, like, a Salesforce class that's doing all the interactions, you have a bunch of methods on there that calls out to Salesforce and does stuff. Extracting an interface that that implements would be the first step. Then you could create the fake implementation of that interface and then probably just toggle them based on the AMD key or however you wanna do it in your service provider.

Jake:

Mhmm.

Jason:

That would be the most straightforward thing. If you're not using, like, a Salesforce class or something, I don't know.

Jake:

How are you doing it, Michael?

Jason:

Extract 1.

Michael:

We're we're using like a 3rd party package that does it, which actually complicates the process even further because all of this stuff is like it's built for Laravel, but everything is just bound to the container. And we actually have 2 two different Salesforce instances that we need to, like, dynamically switch with. So it's like this this this whole debacle around even just using the package on its own. But I I conceptually, yeah, I'd say, you know, we're gonna have to we'd have to abstract over the top of it. We'd have to

Jake:

You would.

Michael:

Inject this stuff. We'd have to create our own interfaces and service classes and whatever else so that we can handle swapping it over. Because there's it's a bit more involved than just saying, like, use this thing. Because using this thing also goes and, like, handles authentication tokens and, like, authenticating with the API to get request tokens and handling refresh tokens and all that kind of stuff. And so it's it's not architected in a way that's meant like, I assume whoever wrote this package, like, built it for their use case and it kind of got out of hand where everyone started using it, but it was not really considered as, like, people are going to use this.

Michael:

It's like, I built this thing and off it goes. And so

Jake:

Yeah. Sure.

Michael:

You know, there's there's a lot of stuff out there like that. And unfortunately, the overhead of trying to implement this all ourselves from scratch is just, you know, you make do with what you've got available sometimes. But I think yeah. I it it seems at a high level to to kinda summarize simple enough. Right?

Michael:

You create an interface, you create your implementations, and then use container binding, to to switch them out whether based on environment or based on, you know, the environment that you're running in or some specific environment variable or things like that or configuration switch and and and going from there. Yeah. Like, I don't I don't think there's anything crazy or wild or beyond the norm for a Laravel application. It's just a matter of, like, deciding at the outset, like Jake did with the with the Stripe stuff. Okay.

Michael:

This is how we're going to implement each of these things so that we can just swap them out on the fly if we have to. And and, like, that's the effort that you need to go to, which, you know, it it introduces this third avenue of, like, fake data where lots of us are probably already doing this. Like, here's our application and here's our HTTP fake that we're using in our tests. But this is kind of like giving that HTTP fake in the context of looking at the application inside of the of the browser in a

Jake:

non production environment. Exactly correct.

Michael:

Yeah.

Jake:

Yes. It's like a third leg on that stool. People typically consider, like, my production and dev environment as, like, one thing and then testing is completely different thing. Right? So, like, in your tests, yeah, you use the HTTP fake and then assert what should come back and all that stuff.

Jake:

Right? So you you mean you are you're doing the same thing. You're faking the data in the test. Effectively. Yeah.

Jake:

Effectively. And so like what you said is you're basically allowing yourself to do that in the in the local layer. And so it's it's hugely beneficial. The one thing I will say that's really actually nice about this pattern too is if you are if you have not yet figured out the API layer implementation so quick example. We have an API layer that we're integrating with, but we have to wait to get approval for this API.

Jake:

We don't actually have the API yet. It's we're not sure what it is. But what we've been able to do is we've been able to create the gateway, create a fake version of it, and then shape the data how we think it will come back and interact with it that way. So we're basically stubbing out what we think it's going to look like. And then when we actually get access to the real API, we can shape the data coming back from the API to look like what we expected it to in our fake.

Jake:

Right? We can sort of marry those up. We can say, well, this is what we've been coding to. This is sort of the interface that we created that we said it should look like about like this. And so when we get the really API, let's say that there's, like, one more key above that level of entries.

Jake:

Well, I just strip that off and now I just return the entries. You know what I'm saying? No big deal. So it allows me to, as a developer, to develop independently of the actual implementation of the API, especially if I don't know what it's going to be yet. Or if I'm trying to figure out what I want the shape of it to be.

Jake:

Like, if I'm the one responsible for creating the API that eventually will be used, I can play around with what I want the shape to look like as I'm consuming it and then later go implement what the what that should I write the code to actually make that happen. You know what I'm saying? So I get to sort of decide in advance what I want that API layer to look like, and then I can code it to work that way.

Michael:

Mhmm. Yeah. Are are you just returning, like, arrays of strings? Have you got, like, typed data in there? Are you using, like, read only DTO, like, plain plain on Good

Jake:

good questions. In some of them, we're just returning arrays. In the some of the more serious apps that we have, we are returning DTOs, typed DTOs.

Michael:

Mhmm.

Jake:

And then, in each of those sort of DTOs, in some of those cases, we'll have, like, fake versus a lot of DTOs so that we can assure that in production and in our fakes that they come back with the same shape if they're larger objects and things like that. But I think that when we're starting yeah. Just arrays and strings.

Michael:

Yeah. I think the DTOs are nice because it doesn't matter what your third parties are returning. Like, you are shaping the data into what you want to consume in your application. And so if you're just coming up with that interface, you're saying, I want this, this, this, this, and this in these, you know, these fields to be available. Then when it comes time to mapping the actual API responses, you don't have to worry about how that data is calculated or where it comes from or if it's like in a nested object somewhere in the API response because you're always getting back a DTO in the shape that your application expects.

Michael:

And I think this is something maybe if people are using DTOs for the first time, they kind of get themselves in this trap of just mapping 1 to 1, what the Yeah. What is being returned from the API response rather than mapping the stuff that you actually need in a way that you want to be able to access it. So, yeah, I think that's that's a pretty good approach. And, like, PHP makes it really easy to to create these, like, read only classes now that, you know, just takes I

Jake:

forgot about that.

Michael:

Yeah. So, like, we do it a lot where we would just have, like, a read only class with a constructor that just set some properties, and that's about it. You know, for for a lot of things, especially things that we're passing around inside the application, anything that that breaches the, like, request response, border. Like, if we're sending stuff back to our front end, for example, we use the sparsysdto package because then we can do all the TypeScript conversion and things like that. But anything that's just bouncing around between the request that goes, you know, from a controller into an action or it gets passed around to somewhere else, a lot of the time, we're just using these read only classes just so that we've got some structured data to to fling around.

Michael:

And those may then convert themselves into to spaCy DTOs when they get sent back. By handling them internally, it means you get, like, IDE support, you get type completion, all of this kind of stuff, or type support and IDE completion the other way around. And it means you you entirely control the shape of that thing. And it's just a matter of instantiating that based on whatever you have available. And then it doesn't matter if it's a fake or if, you know, or in this example, it's GitHub and GitLab and Bitbucket.

Michael:

And then the fake is everything is returning the same structured data. And how you compose that can vary, you know, in an infinite number of ways because you're not conforming to any individual provider specification.

Jake:

Yeah. I'm curious how you guys are doing that, Jason. Like, when you have the different providers, is is there some sort of standard response that you guys are formatting it to? Or how does that work? You know, when you say, like, I wanna talk to GitLab or wanna talk to Bitbucket or when I wanna talk to whatever.

Jake:

When you're getting back that data, that's the list of repositories. Yeah. Is that typically just returning an array, or is it returning some sort of shaped object?

Jason:

Right now, we're basically just returning arrays. Mhmm. But we are shaping them to some extent. But, yeah, we're not using

Jake:

Like, inside the different implementations inside the different implementations of the drivers, you're just basically saying, like, whatever comes back, I just want this array of the repos.

Jason:

Yeah. So, yeah, the repository's, method on this GitHub thing goes and gets some, and then we're looping over each one, mapping it to the structure we want, and just return an array of arrays, basically.

Jake:

Mhmm. Mhmm. Yep. And then the other ones, like, the Bitbucket driver would basically do the same thing. It would kinda go get it, and whatever the response is, it's gonna massage it into this shape that you're that you're wanting to see there.

Jake:

Okay. Yep. Okay. Interesting. Yeah.

Jake:

Michael, your suggestion of, like, you know, always having a DTO there, I think it just depends on where you're at in, like, the journey, I guess. Like, if you're if you're really wanting to be positive or if it's you know, for example, like, in this case, if you're just returning an array of, like, the items, like, probably not that critical. But if the shapes of the data that you're dealing with are quite complex, if, you know, if you're needing to grab 20 keys off of this one object that's returned and you need to make sure that they're there or have some error handling in the case that they aren't there or whatever or have, like, a null response, you know what I mean? Those sorts of things, then I think it's worth, it's worth sort of extracting some sort of DTO there and then always returning that regardless of if it's coming from the fake or from your real one.

Michael:

Yeah. I mean, obviously, it depends on your team, your makeup, your you know, where you're at, as you say. I think it's just it's so easy to to create these objects to shape the data. And that way, you don't run into these situations of, like, forgetting a key or trying to reference a key that doesn't exist or having a typo and then, like, these things bubbling up as exceptions because you're accessing keys of arrays that don't exist and things like that. Like, you know when it's an object that it's this object and it's going to have these things in there.

Michael:

And it just like gives you a sane kind of structure to bounce around in the application. Yeah. And and like

Jason:

I said have

Jake:

I was

Jason:

gonna say these only have, like, 4 keys, but Mhmm. With them being, like, the same structures copied across 4 different Mhmm. Providers, there's a pretty good argument already that this could be a DTO.

Jake:

Mhmm. So you're saying with each one of the repositories that's returned from that array, like, each one of those items has, like, 4 keys that you're that you're hoping that they have? Yep. And right now, it's kind of just up to the

Jason:

Yeah.

Jake:

Go ahead. You were you were gonna say I'm guessing you're just gonna just say the different keys and stuff.

Jason:

Yeah. It's got a full name, is private, a name, and then a default branch. Mhmm. Yeah.

Jake:

Yep. So the different yeah. And so it's basically right now, it's just up to the developer who's implementing these to make sure that they're consistent across the board. Right? Which is Yep.

Jake:

I mean, you got high quality team members. Right? So they hopefully, when somebody goes to change one of them, they remember to change all of them. Right? But that's Yeah.

Jake:

That's sort of the danger. Right? Is that if there's some new key that you need off of it, you're gonna have to remember to implement that on all of them. And if you forget one of them, then you're not gonna know until it bites you. You know?

Jake:

Yep.

Michael:

Yeah. So Yeah. Again, comes down to the team where you're at, you know, that it is 4 different implementations that are all the same thing. When you create a new one, you know, you're just going and copy pasting the existing things and doing the mapping, and and you make the assumption that it's there. So, like, it's not critical.

Michael:

But then, you know, depending on your team, you're then in this situation where you're using PhpDoc to annotate the shape of your returned arrays instead of just returning a DTO. So,

Jake:

Right. Right.

Michael:

Like, a million different ways to to implement the same things, and, like, make the decision that that is suitable to you. I just think no matter how simple it is, it's so easy to just create these objects now. And it's and it's it's more resilient, to to return those things than it is to just, like, hope that you don't make a typo. And like, yeah, you write your test, and you make sure that things exist and and whatever else. But I'd I'd like, it's so so easy to do that it's Yeah.

Michael:

You know, you'd have to you'd have to have a a really good reason to not do it. Like, yes, before everything was just erased because everything in PHP was always erased and that was fine. But it's like as I said, because it's so easy to do, there's like no overhead. You don't have to like, we've got auto loading. These things can all just exist and they just work nicely.

Michael:

So, yeah. Do do whatever you want. I'm not I'm not your dad. I'm not I'm not the one that's gonna be rejecting your PRs.

Jake:

Yeah. Right. Right. If I can make one more pitch for the DTO thing on this side, I'll I'll say this is the last thing. When you are doing fakes inside of your tests, a lot of times, like, if you're doing, like, an HTTP fake, for example, you're just returning, like, arbitrary strings of data data.

Jake:

Right? And the danger for me in those is those things can change shape relatively quickly, and it's hard to keep them consistent across the board. Whereas if you use this sort of architecture that we're talking about, what we've done in some instances where I've said I know I've called them more are are more serious apps. And what I mean by that is apps that are dealing with, like, financial information or or, you know, payments or things like that. When we're doing that, what we'll do is we'll return a DTO from each of the different methods on the contract, which is great because you can just say this method always returns this sort of DTO.

Jake:

But then what we'll do is in our tests, we can just say facade, which is like Laravel Cloud, you know, colon colon, should receive or expects, create new server. That's the method. And then we say and return. And then we have that DTO, and we say double colon fake. Right?

Jake:

And so we would we basically have on each of these DTOs, we've we've implemented something that says, you must have a method that's fake on this, which returns essentially like a factory. You know what I mean? Here is a default version of what we would expect this DTO to look like. Mhmm. And, and then anybody who's using it you know what I mean?

Jake:

If you're ever needing to test that in your actual tests, that's where you're gonna get that data from. And and you can, you know, you can then modify it. So you basically have these DTO fakes, which, again, are very much like database factories where you have, like, methods you could call on them to say, like, I would expect this one to be like a failure or I would expect this DTO to be like a whatever. Right? And so, it does give you a sort of another layer of confidence to know that, you know, you're always going to be returning that shaped data in a way that is going to be consumable to all your other stuff.

Jake:

And in your tests, you have a lot of confidence to know that, you know, you're not just returning arbitrary data from, like, a HTTP fake. This is what's gonna come back, and it's gonna be this shape. And so that feels really nice. It feels like it gives you a lot of confidence.

Michael:

We've also done things with static constructors to, like, dtocoloncolondefault. You know, if we have something that wants to return some default state. And then you can just call that if you want to use that for for whatever reason. You know, you can do these things. You can extend them locally, I guess, if you're not using final classes.

Michael:

And, like in your test environment, say like this thing extends this existing DTO, and it will always return, like, failing data or successful data or whatever I was trying to say. So, yeah, there's there's definitely some nice approaches to it. I mean, yeah, with with an array, you can just say, like, whatever returns this static array of data. Yeah. A million ways to skin the cat.

Jake:

Jason, I do have one more pretty critical question on this front for you. So exceptions with these different drivers. I'm curious if in each one of those little, you know, in your contract, you define these are the methods that we're gonna use to interact with this Git provider. Right? If there's an error when we're interacting with one of those, is it just chuck an exception up and let the let the calling code catch it?

Jake:

You know what I mean? Or, you know, because I know Laravel has, like, the I I don't know how you guys are doing it, and you don't necessarily have to tell me. You know, I know that there's probably some things you can't maybe say. Laravel has, like, the HTTP stuff, which you can use, which is just guzzle under the hood. And then you could say, you know, by default, it doesn't throw anything.

Jake:

It'll just you have to check the response to see if it was a failure or not. But that's not always the case. Sometimes if you're using, like, a GitHub integration, SDK sort of thing, it'll chuck an exception regardless of if you want to or not. So I'm just curious as, like, you know, is each one of those returning, like, a custom type of exception that is handled specifically by the calling code? You know, because it's like maybe GitHub throws a different type of exception than Bitbucket than GitLab.

Jake:

You know? How is how are you guys doing error handling in those?

Jason:

Yeah. I didn't write these, so I'm looking at it now. Each of these, providers has, like, a client method on it that sets up the HTTP client. So we're using that HTTP You're using HTTP. Cool.

Jason:

And looks like each one has a tags on a throw method, which returns a, GitHub request exception. So Okay. We're making custom exceptions for each one.

Jake:

Okay. Alright. Sweet. So that's that's helpful to me too because I've always wondered on those as well. Like, do I you know, we've almost played with the idea of, like, creating a DTO error response as well.

Jake:

Like, saying if we're gonna throw or, you know, I guess an exception is is it's not a DTO, but, you know, having a consistent exception that we throw from each one of those. Like, if if it's going to throw, it's going to throw an exception that looks like this, and the exception is going to have this type of message. But, yeah, that's interesting. Okay. Very cool.

Jake:

So so the the you set up a client and then inside of the method itself, you're calling a, you know, you're calling post or you're calling get or you're calling whatever. And then on that, you're tacking on throw. And then if there's an exception, it'll throw a custom GitHub exception or a custom Bitbucket exception or custom whatever exception. And then Yeah. Yeah.

Jake:

Whatever's calling that is probably gonna catch it, display that to the user, maybe report it, something like that. Okay. Interesting. Well, I'm gonna have to I'm gonna have to, pair with either you or Nunos some time and, like, dig around in there. I'm so curious.

Jake:

I'm so curious what this code base looks like. Yeah. I have to, like, I have to sign an NDA and, to contact, you know, the big dogs. I have to contact, Tom and Taylor and be like, hey. Any chance I could take a look at this?

Jake:

Just curious. It's probably gonna be a no, but, might as well try. Right? They told me no when I said it when they when I asked if, they needed a sub for a basketball game. Right?

Jake:

Like, I text Taylor ahead of the time. It's like, hey. If you need to there was like, yeah. No. That's okay.

Jake:

Thanks. And then they ended up asking. So, hey. The worst I can say is no. Right?

Jake:

And then maybe they soften up later and let me take a look at it. I don't know. Well, Jason, it's been, it's been super nice having you on, man. I told you that we wouldn't keep you until after midnight, and I've broken that promise. So the first time is this the first time we've had you on the podcast?

Jason:

It's the first podcast ever.

Jake:

The first hey. I am honored. 1st podcast you ever been on and the host lied to you and said we wouldn't keep you after midnight. It is after midnight right now. So,

Jason:

Eric, I

Jake:

wanna thank

Jason:

you for coming after me for Oh. To get on his podcast forever. So Dude. I got it first.

Jake:

Well, I'm glad that we are the first, and then he can be the second. So, you know, it's fine. We broke the ice. We broke the ice for you. Yeah.

Jake:

But it's been great having you on, man. Thanks for taking some time and and talking with us about this. This is super interesting. And if there's any future developments that you guys make, you know, there's a bunch of smart people on your team over there. So I would be, forever grateful if you guys are coming up with a really cool solutions to this and can share some of that knowledge, on Twitter or just personally.

Jake:

That'd be that'd be great.

Michael:

So It would be it would be nice to see more stuff now that the team has grown the engineering blog to to, like, start talking about some of these interesting challenges where possible. I think that'd be cool.

Jason:

Mhmm. Or

Jake:

put them on Laravel News. I think

Jason:

Glau is kinda like the 3rd iteration of these apps. Like, Forge and Envoy were kind of one version. Vapor was a second, and then, like, Nuno has worked on all of them. Nuno and Joe and Mohammed, they've worked on all of them. So, they're kinda learning from the first couple iterations and bringing in some nice patterns for cloud.

Jason:

So it's been pretty cool to see how they work. Yeah.

Jake:

That is very interesting. Yep. Well, thanks for sharing some of that knowledge, man. I really appreciate it. Jason, if anybody wants to follow you on Twitter, where can they find you at?

Jason:

Yeah. It's, twitter.com/jasonlbags.

Jake:

Awesome. And what, you have anything you're selling? You have a website or anything that we can send people to as well?

Jason:

I guess cloud.laravel.com.

Jake:

There you go. That'll work. Awesome. Michael, what episode are we on again? 163.

Jake:

163. Folks, you can find show notes for this episode at northmeetsouth.audio/163. Hit us up on Twitter at jasonlbeggs, Jacob Bennett, or Michael Dorinda, or North South Audio. And, if you have any questions, feel free to hit us up on there. Rate us up in your pod catcher of choice.

Jake:

5 stars would be amazing. Jason, it was a pleasure, my friend. Thanks so much. Folks, we'll see you in 2 weeks. Peace.

Jake:

Bye.

Creators and Guests

Jake Bennett
Host
Jake Bennett
Christ follower, web dev designer @wilbergroup and @laravelphp fanboi. Co-host of @northsouthaudio and @laravelnews with @michaeldyrynda
Michael Dyrynda
Host
Michael Dyrynda
Dad. @laravelphp Artisan. @LaraconAU organiser. Co-host of @northsouthaudio, @laravelnews, @ripplesfm. Opinions are mine.
Fake drivers with Jason Beggs
Broadcast by