Two seasoned salty programming veterans talk best practices based on years of working with Laravel SaaS teams.
Joel Clermont (00:00):
Welcome to No Compromises, a peek into the mind of two old web devs who have seen some things. This is Joel.
Aaron Saray (00:08):
And this is Aaron.
Joel Clermont (00:15):
Yesterday we were chatting on Slack about some questions, philosophical questions, I had about factories. And I thought, you know what before we argue too long over Slack maybe this would be a good podcast topic. I'll set it up a little bit to get everybody up to speed. The question I asked was something like, "Should you allow a factory to create a model that could not be created through the application?" And what I mean by that is if there's business logic, like validation logic, in place that says if field X is present field Y must also be present. It's not enforced in the database, so yes, you literally can create it, but should you? Should you allow a factory state that is something that just really doesn't represent what would ever be created in the app? And you said, Aaron?
Aaron Saray (01:07):
Well, I can tell you. First of all, whenever you come to me on a Sunday night and say, "Here's a philosophical question," I immediately was defensive because I'm like, "I know Joel doesn't ask philosophical questions on a Sunday night." It's usually I'm asking this question but really it's a concrete question because I'm working on a project and if I answer the wrong way Joel will do a bunch of code and I have to review it the next day.
Joel Clermont (01:29):
No, I'm setting you up so that when you get the code review, like the poll request on Monday you've already answered it and you can't change your mind at that point.
Aaron Saray (01:38):
Yeah, I was already defensive. I'm like, "Ah, I don't know." Well, first of all, I took the question to I mean, should factory create another model? Like, if you're creating a company and all companies have users or something, should you create a user to go with it? But what you're saying too makes sense. Like, if there's a number of different fields that may depend on each other but they can't be enforced in the database because there's a number of different states so no particular state is the right state, should the model be able to be created in a way that is different from whatever validation says? Before I go and answer that, I'll tell you what I think the most dangerous phrase that a programmer can ever utter is.
Joel Clermont (02:22):
Okay.
Aaron Saray (02:23):
"That'll never happen."
Joel Clermont (02:24):
That will never happen. Okay, you're saying what? Why that in this context?
Aaron Saray (02:30):
Yeah, that's kind of where my mind goes right away with this whole idea of like, "Well should we allow the factory to create something in a state that would never happen?" And that's kind of how I go in here. I'm like, Yeah, well it won't happen now or it won't happen if you haven't had any bugs or all these different things. So I tend to lean towards the side of allowing factories to create models in states that could never happen Just to see what would happen to my code when there's things that should never happen. Because when things should never happen, I should really be handling those in a somewhat graceful way and not just 500 all over the place if I can help it.
Joel Clermont (03:12):
Yeah. I mean, you could end up with really super defensive code base which isn't always great either, right?
Aaron Saray (03:17):
True.
Joel Clermont (03:17):
I'll give you a concrete example. If this particular event always has to be at a venue unless in this one weird case it doesn't, like just always having to check for that, I think could end up being kind of messy. But I also get your point and-
Aaron Saray (03:35):
Well, I mean that's what I tell the business people we work with. And they say, "Well, that'll never happen." I'm like, "If it happens once I have to code for it."
Joel Clermont (03:43):
True, yeah.
Aaron Saray (03:43):
I mean, your example there it'd be more... I think the example is more so like, can I ever enter an address without a state, for example? Or, well, all addresses have states or whatever your locality is. You know, the things that support your higher-level addressing. Is that field required? Well, maybe it's not a required field or something because it's nullable and all those different... I think there's something in there that kind of triggers me. Now, I'll argue with myself here real quick.
Joel Clermont (04:19):
Please.
Aaron Saray (04:19):
I haven't given my answer yet on what I do.
Joel Clermont (04:22):
I'm well aware of that, Aaron.
Aaron Saray (04:23):
I just say there's this thing I consider. Before I answer, I want to know what the right answer is so you answer.
Joel Clermont (04:32):
Well, I can tell you where my head was going when I asked the question. And it really had to do with... I was writing tests for something and the tests were failing as I was building it out. I'm like, "That's weird. Why is this failing?" And the way it was failing was it was looking for a relationship that should always be there. So in this case, I'll just build upon the example just to make it a little more concrete. If there's an event, it always has a venue unless the event is set to private, right? So our events have two visibilities, public or private and it has to be one of those. So it's like, well, when it's public there has to be a venue relationship, venue id, and when it's private there's not because it's in somebody's house or something.
I was getting an error because the test I was writing is that, well, private events should never show up in this context. And they were but it was thrown to 500 because it was trying to get a venue off of something that didn't exist. It just got me thinking like, reflexively went to the factory, I'm like, "Okay. Well, I'll create a state called private event that doesn't have it and then I'll update the other state called public event to make sure it does have it." But then I'm like, "There's a lot of stuff in here that we're not accounting for." There's a lot of other subtle, if you will, business rules that are only enforced at the application layer that do affect what fields are on the mode. And that's where I'm like, "Oh man, should I do all of this to make it only reflect what could be created currently through the UI?"
And just as an aside, I get your point about business rules changing and so I'm kind of putting that off to the side because I think that's a separate discussion. You know I'm not like, "Oh, this will never happen," because we just don't think it will happen and then somebody will need it to happen. That that's a reality, I get that. But in this case, we know what the business rules currently are. Should the factory states line up with those different sets of business rules? And I think, yes. I think in a perfect world that the answer is yes.
Aaron Saray (06:31):
Well, I will say I agree with just the phrase that you just said but I don't agree with what you think you said.
Joel Clermont (06:38):
Please explain me to myself.
Aaron Saray (06:42):
Well, you just said, "Should the factory states line up with whatever?" And I think that makes sense. I want to have states that line up with the business rules, things that would've happened. If it's private, it has this, if it's a public state it might have this with this... The sort of state method you can call on a factory.
Joel Clermont (07:01):
But not the default state, that's where you're...
Aaron Saray (07:03):
Yeah, the default is I want only the things that are required to actually write the model.
Joel Clermont (07:09):
Required use of database.
Aaron Saray (07:12):
Yeah. And the reason is... I mean, it's bit me a couple times where I set the default values for something and it just wasn't what I expected or someone else made the default thing and they're like, "Well, default is always private." I'm like, "Why would you have private as default when there's like 1 out of 1 it's private, the rest are public? Why not public?"
Aaron Saray (07:32):
Right.
Aaron Saray (07:32):
And they're like, "Well, private is the least amount of work required to build it so maybe that should be the default." So you can get to that sort of thing. And there's something that you shared with me once, which was sometimes on those default states that are something, that reflects something you might make a state method on the factory that does nothing and you just might say so that it's more readable.
Joel Clermont (07:56):
Just to name it, yeah.
Aaron Saray (07:57):
Yeah, I kind of follow that whole process. There's this part of me, and I think this is kind of a aside but I think it's important for all developers to think about. I think this is what my opinion is but I'm not willing to die on that hill, as we always say. The more I explain it to you the more I'm unsure if I'm right but I still think I'm right. I'm willing to be wrong.
Joel Clermont (08:29):
Well, that's very honest of you, Aaron. I appreciate you sharing that. But I think what you just said kind of crystallized it for me. Which is, the default state should do as little work as possible. And reason being, especially with relationships, there's times where you need like 20 of a model and you're truly never going to use the related value so why spin up all those additional? It's a little bit of a micro optimization but you know what? I've never complained about my tests being too fast. I still like that mindset we have, the default should be the bare minimum fields to allow it to persist to the database. But then the name states, yeah, if you're calling something a public show it really should have all the things that in the application would represent a public show. If that means it has to have a venue, then that state should have a venue. I like that, I think it's kind of like the best of both worlds.
Aaron Saray (09:25):
Yeah, I'm glad I thought of it.
Joel Clermont (09:27):
Yeah, me too.
Joel Clermont (09:35):
Last night I went out to eat at a restaurant, which may not sound very notable but when you have four kids like I do it doesn't happen a lot. And I should clarify, it was just me and my wife, it wasn't... Not all six of us. So anyways I'm looking at the menu and I think I'm a reasonably adventurous eater. Like I'll try a bunch of different things. I wouldn't go so far as to say say I'm a foodie but I do like different things. I guess what I'm saying is unusual ingredients or things wouldn't throw me off. I'm looking at the menu and I'm just noticing nothing can just be like a normal ingredient, right? It's not like the on the menu they had Brussels sprouts and it came with a green chili crema and roasted pepitas. All this stuff, peppadew peppers. And I'm like, "I don't like having to google what things are when I'm deciding whether to eat it." But like the pepitas, I knew those were pumpkin seeds but why don't you just say pumpkin seeds? Like, you know what I'm saying? And this was not like a four-star restaurant, this was just kind of like a normal restaurant and it was trying to be-
Aaron Saray (10:52):
But that's the name of what they are.
Joel Clermont (10:54):
Yeah, I know. But you can get like... Peppadew peppers, they could just say they're like red peppers because that's what it is. I had to look it up but it's like a particular brand that's kind of pickled and it's in a jar and it comes with a certain sauce.
Aaron Saray (11:09):
They're describing the type it is. That's like if you said, "Well, it says apples," and someone's like, "Well, it's a Granny Smith apple." You're like, "Oh, so it's going to be sweet, tart, and make me hate myself. Got you." You know?
Joel Clermont (11:20):
What?
Aaron Saray (11:20):
Well, I'm just saying they're just describing the type of thing. When you said the type of peppers, well if you said red peppers, I'm assuming like raw bell peppers that are red, not some pickled ones. You know, maybe I don't like pickled stuff. I'm like, "Oh, I hate vinegar," or whatever, you know? I don't think you should be so cranky, you old man.
Joel Clermont (11:42):
So you're defending this and you're saying they're actually giving me more information and I should just live with the fact that I'm not going to recognize every ingredient and I'll either roll the dice and order it and eat it and see what it is or I'll have to like get out my phone and like, "What is this?" and look it up?
Aaron Saray (11:59):
It's not their fault that you don't know things like adjectives.
Joel Clermont (12:03):
Not adjectives. It's just like, here's... Maybe this says more about me.
Aaron Saray (12:08):
No, it'd be like if someone says, "Oh, I got a new car." You'd be like, "Oh cool." No, you'd be like, "Oh, what kind?" "I got a Toyota."
Joel Clermont (12:19):
No, it'd be like when you told me...
Aaron Saray (12:20):
"Oh. What is a Toyota?" I have to look up what Toyota is.
Joel Clermont (12:22):
No.
Aaron Saray (12:22):
I didn't even know what a Toyota. Like, that's the same thing.
Joel Clermont (12:25):
No, it'd be like if I'm shopping for a car and they tell me like what brand of cattle the leather seats are made from. It's like, yes, that is imparting more information but is it really relevant? And like especially if it's a uncommon term just tell me what it is and then if you want to also add on details fine.
Aaron Saray (12:42):
Not common to you but you went to a place that specializes in food so those are common things to them.
Joel Clermont (12:48):
Okay. All right, so you're on the side of a big menu. I get it, that's fine.
Aaron Saray (12:55):
We put a lot of work into these podcasts believe it or not, and we appreciate that you're listening and we hope that other programmers have a chance to listen to. Perhaps you can share us on Twitter.
Joel Clermont (13:05):
If you'd like to direct them to our site. It's show.nocompromises.io and we support all the podcast players in the world.