Joel: Welcome to No Compromises. A peek into the mind of two old web devs who have seen some things. This is Joel.
Aaron: And this is Aaron.
Joel: I always beat the drum, like in tips and different things, about making sure your local development matches production.
This is something... I feel strongly about it because I feel like I've bumped into maybe dozens of ways that it has bit me when they're not the same.
And I'm talking like, even the patch version of PHP being different, file system, case sensitivity, you name it. I've been there.
So, Aaron, I know you feel the same way.
Aaron: I would say that before we kind of locked down, having everything be the same in all the different environments.
I would say before we made that decision, I remember running into scenarios like you just mentioned, at least one time a year, if not longer.
Joel: Right, yeah.
Aaron: So that adds up over the course of old cranky man career.
Joel: Absolutely, yes. So, this isn't like, "Oh," you know, "theoretically you should do this."
It's like, no, I've literally been burned in bad ways in production, and I just can't take it anywhere.
So, all this is to say, today's topic, we had another area where I see regularly people deviating how they handle local versus production.
And I just wanted to see if it kind of falls into that same bucket for you.
I'm going to let you kick it off, because I think this topic was your idea. But I was just sort of setting up some background for us here.
Aaron: Yeah, I think that's a good point. So, I was looking at the service provider for something. I'm going to pick one of the tooling in Laravel.
Let's say Telescope. And in there, there's a Gate functionality set up, and they may change it as we go through different versions.
So, I'm not going to refer to the specific method names, but there's sort of like a gate functionality in there.
And it was kind of set up that says, like, "Return true if you want someone to access this based off their user account. Return false if you don't."
And by default, it's kind of built that it checks the user's email against an empty array, which is always going to be false. Okay.
I don't like that. Because to me, that tells me that no one should be able to access Telescope, right?
Joel: Mm-hmm (affirmative).
Aaron: I mean, if the gate says who can access Telescope, and then it's configured that no one can. How come locally, when I go to /Telescope, I can see it?
Joel: Oh, okay. All right.
Aaron: And the reason is based off of their code further up in the process, there's a service provider that basically checks...
That says, "I'm going to allow you access to this if," you know, "you're not local," or "if you are local," or "this gate matches."
And that drives me nuts because I'm like, first of all, I don't like that it's different in local versus production. But second of all, here's the worst part.
I don't know how I can actually test my logic that is actually working, that protects my production access. So, it's literally set up in a way that I can't...
I guess maybe I can force it to the testing environment and write a test there. I'd have to look, but I don't know if it's... whatever.
But I should be able to be restricted from accessed locally the same as I would be in production. And it wasn't clear.
You have to look at the documentation, it's not even like in the comments of that. So, I don't understand the rationale or reasoning for that.
That's kind of beyond my point. But it is an example of something that is not the same locally as it is in production.
Joel: Right, yeah. And this is a good one, and it's not just Telescope. Like, you mentioned Telescope, but I think Horizon... Like, a lot of first-party Laravel...
I don't know how to phrase it, but I think maybe Pulse is another one. Like, they're sort of meant to be used in a certain way.
Like, bolted on to... It's not application functionality. I don't know if I'm... But it's like, there's this category of packages that follow the same pattern.
And you're right. If you just look at the AppServiceProvider, because I brought it up while you were talking.
The only hint that there's this special local-only behavior is, I think, the comment on the doc block says, "In non-local environments."
So, you have to read between the lines a little bit and be like, "Oh, so it's different in local. Okay, but what does local mean?"
And it actually means specifically APP_ENV equals the word local. Like, it's very specific to that.
And I don't know about test either, but I think you're right. It might treat that production-ish. But, yeah, that's kind of a separate concern.
Like, how it's sort of... hidden is too strong a word, but it's not obvious behavior.
But it is a pretty radical departure between local and production. And you talked about the gate logic.
I suppose... I'm just trying to think what the counterargument would be here.
Like, if your gate logic truly was, "This one person's email address is whitelisted." You know, you could maybe say, "Wow, what could go wrong there?"
But in a real app, it's probably more sophisticated, maybe checking your roles.
Aaron: Yeah, probably roles or IP or a combination of those two things.
Joel: Right.
Aaron: And whenever there's any sort of logical combinations of stuff, I want to test it. Like I said, I just happened to think while I was talking.
That maybe that doesn't... You could write a test. Like, a unit test against that. But again, I don't like that.
That I would have a unit test that proves something works in a way that I can't actually exercise locally. Doesn't make any sense to me.
I've seen them at the other direction, where it's like, I can use something locally, and I can write tests.
But I maybe can't hit the production version of that, so I actually don't ever know if it works. But I've not seen the reverse, that is confusing to me.
Joel: Yes. And I agree with you. Like, I want to be able to drive the app and kind of experience it the way a real user is going to experience it.
So, just trying to anticipate what people might be thinking. First of all, the reason this is probably the default is it does reduce developer friction, right?
So when I'm running this locally and I'm logging in, maybe with like a test account or some seeded account.
Like, I don't have to keep going to my service provider and adding the email address and doing all the setup stuff. So, I understand that approach.
But to me, all of the developer ease in the world immediately is invalidated the first time a bug hits production. Especially a security-related bug.
So, I just tend to really... It's not that I don't like developer ease.
But to me, it's such a lower priority than ensuring my system works correctly in a security context. Do you agree with my framing of that?
Aaron: Yeah. I even bought into that even further. Like, I understood it and liked it when I first saw Telescope, when it was more of a manually registered thing.
But now it automatically registers, unless you say, "Don't discover," I believe. So to me, that really was sort of, I don't know, it's just hard to...
Like, I could install something. So I saw that, yeah, I could access it right away.
Which is nice, but it was weird that I could access it right away before it was automatically available to me. So I think that that's the disconnection.
If it was always automatically available to me, it would have made more sense to me that I could always access it immediately when I installed it.
But it was like that was a separation that time in Laravel, I think. But, you know, you brought it up.
You said, like, "This is a thing that maybe we don't really like." Do you remember what I did to try to get around that? Did you even like what I did?
Joel: I vaguely remember what you did, but remind me.
Aaron: Oh. Basically, when I saw this the first couple of times, I was okay with it, but it just kept bothering me. And I said, "You know, I just don't like this."
"I want to see what it's like." And I wasn't using an email address. I was using roles and stuff like that, so I wanted to test that.
So what I ended up having to do is go up inside of the actual service provider provided by Laravel's package, see how they were calling my gate functionality.
And sadly, I had to go and copy that function that was calling that into mine and change it. So it was like, it would do three or four different things.
As well as calling that gate. And I didn't like that, but it was the only way I could come up with.
Is like, I had to say I would copy this exact same method into there and then just remove the one check for this local. And then everything worked as I expected it.
Joel: Yeah, I think that's jogging a memory for me. And I think in later versions of either Horizon or Laravel, they did even narrow that down.
So, it's like one function called gate or something, now that you just have to override that and get rid of that extra check it was doing for local.
We basically don't make that call.
Aaron: It was that. And there was also something where I probably should stop talking, because I may not even remember the details.
But I thought there was something else in maybe Telescope, or something.
Where it was like, it would make decisions on blacklisting or whitelisting, or including something based off the environment as well.
And I was like, "I don't want that based off the environment." I want it always to function the same way, and I will configure things as I want them.
And you know what, I will see locally, but the problem is before it goes to production.
Right.
Because if I'm logging my password or something like that, I want to see that locally, and I'll stop that there.
Joel: Yeah, I agree. And you're right. These blend together because they're all slightly different but also mostly the same. So anyways, that's very...
Anybody listening to this and wanting to take action. When you look at the actual code, it'll be apparent where the point is to override this.
And I would just say maybe to wrap up. Like, if you're listening to this and you think this is old man says, "Get off my lawn," or whatever.
You know what, you can believe that you'll run into these issues.
Or maybe you will just lead a charmed life and never once run into one of the things that I've run into over and over and over.
But trust me, Aaron and I are both saying this comes from hard-earned experience, and maybe listen to us.
Aaron: Joel was just complaining about his AirPods. Something about putting one in and the second one in, I don't know, sometimes they work. I don't really...
I wasn't really paying attention. But-
Joel: Okay, thank you.
Aaron: And he said, "Do you ever have that happen to you?" And I wasn't paying attention because, no, I don't use my AirPods like you do.
Joel: You put them on your nose, or what do you do?
Aaron: No, I only use one at a time. So, because I use them when I'm outside walking, I want to see what's going on, or I want to hear what's going on in the world.
And we had this little weird conversation, like, you know, back and forth. Like, "Well, I don't need to..." And I was like, I wasn't mad, but I was like...
And I wasn't judging, Joel.
Joel: Okay.
Aaron: But I was like, "Ah, maybe a little bit." Because for me, it was about safety. If I can hear people, you know, without both AirPods and whatnot.
So, I don't know. I judge Joel a little bit there.
But then I told him, "You know, I got to apologize to you for something else." Because I've been judging Joel a lot lately, and I don't know why.
Joel: Oh, boy.
Aaron: I'm going to tell you the other thing I need to apologize for.
Joel: Yeah, I don't like this.
Aaron: And I think it's because I was jealous. And it was actually a really great idea, and I didn't like that I didn't think of it.
Joel: Oh.
Aaron: The other day, we went out to have lunch, and we both ordered food. And I think Joel's food was way better than mine.
And the reason I think it was way better is it was just amazing, little bites of food of all different types of stuff.
And I ordered one entree. And Joel had ordered two appetizers. And I remember thinking like, "Joel, what's wrong with you? Why are you ordering two appetizers?"
And he's like, "Oh, it's what I want." I'm like, "But is that going to be enough food?" And I just had all these different judgmental...
It didn't feel judgmental, but it felt like trying to tell him that my way is better. Have one appetizer, maybe, and then have the main meal.
And then he's sitting over here having his appetizer, two appetizers. I was a little upset. I was a little upset because they came out before my meal
And so, he got his whole meal, whatever.
Joel: Another hack.
Aaron: But if we look back at the whole meal. I had some leftover in my meal, I wasn't really happy with it. Joel just demolished his.
Was so happy the entire time, and made me jealous. So, "Oh, no. You won, and I apologize, Joel."
I was wrong. You know, you don't have to just take our word for it. There's other people that can tell you how they've been bit in production too.
And where would you find those people, Joel?
Joel: Well, I'll tell you what, Aaron. There's some nice, smart devs in the Mastering Laravel community.
And if you'd like to join, head over to masteringlaravel.io and click on community. We'd love to have you.