When should you use final classes or private properties?

Joel Clermont (00:01):
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:16):
Aaron, I know you're so far above the cesspool of Twitter, like you're just immune to following it and knowing what's going on. But I'm going to share with you something because it's what we want to talk about anyways today. Which is, there was some pretty strong opinions on the use of final classes in PHP. You had put this down as a possible topic that you want to talk about, but I just thought I was all in on it because it's something that I think people in the Laravel and PHP community are thinking about right now. Those of us that are on Twitter and just getting dirty in the cesspool of Twitter. But I'm just going to kind of open it up in a general question and I sort of know your opinion already, but what is your take on the use of final classes?

Aaron Saray (01:08):
Okay. I think my opinion has slightly evolved over the years again. I can say that I particularly have an opinion of final classes in open source projects. I don't have really any experience working with compiled binaries or closed source stuff that I can't get access to their source code. I work mainly in open source where I can see all the things. I mean, I may not dig into them but I could see them and I could interact with them and read them. So I think when you do open source work, you give up the right to be able to tell people how to implement your code. You have rights, like licensing and how they can use your code, but implementation is slightly different than use cases. Use cases could be like, "I'm using this to make money," or, "I'm not contributing to it," or whatever. But implementation is, "How do I actually write the code inside of my code? How do I use your library inside of my library?" Because of that, because I don't think you have the right to tell someone how to use your code, I don't favor final classes. I have not ran into a situation yet where having a class final, so not being able to extend it or whatever, was a good thing for me.
Now, I know there are some arguments. Like, "Well, I make classes final because they shouldn't be extended and people make too many extensions," of all that kind of stuff or whatever. That's an architecture question. And why are you the wise one? I can tell you that my opinion of what things should be done and how they should be done has changed over the years. So if you believe that mine has, that means that yours will too. That means that we know what we know at the time that we know it, but that isn't necessarily always what we're going to feel and think moving on in the next couple years when you learn different things, when you have different experiences. And because of all that, I'd rather keep things more open, giving people more flexibility to use my code in different ways than to make it final and not be able to do something with it.

Joel Clermont (03:12):
Okay. Just to clarify in my own mind. You're thinking of a final class, for example, in a package that's being released. You're not talking about, like, within a web application? Or is it the same principle?

Aaron Saray (03:26):
Anywhere.

Joel Clermont (03:27):
But you find it especially egregious in a open source package that somebody's publishing for others to use?

Aaron Saray (03:33):
Yeah, I think it makes stuff like mocking a little bit harder too. Sometimes I want to run your code all the way through, other times I want to mock it out. And when it's a final class, it's a little bit harder to mock.

Joel Clermont (03:43):
Okay, all right. You've taken a stand, you've drawn the line in the sand. I see where you're coming down on this. Let me just flip it a little bit. Is there any reason, valid reason, you can think of making something final? Or is it just an always bad thing?

Aaron Saray (03:59):
Well, I'm a little reluctant to say always bad, but I haven't heard any reasons yet where I think hold water. So if you have something to share.

Joel Clermont (04:08):
Okay. There's one that came up. Because as with so many things, I really don't have a strong opinion on this. I personally don't use final classes, but I don't think I would... I guess I wouldn't be mad seeing one until it actually impacted me. I know there's ways around it. Like, PHP with reflection, you can do whatever you want but it is annoying. Anyways, the one argument that somebody brought up, and actually as I'm about to say this I know it's not going to work with you, but I'm going to share it anyways is, a tool like PHPStan, especially at the stricter levels, will say, "Hey, you're type hinting this class." That class could be extended and it makes things more difficult to type check. However, if you mark it final, it's sort of saying, no, it's always definitely going to be this thing and it relaxes. I don't have a concrete example, but I saw this argument and it made sense as I was reading it. But tell me why you don't like that example, Aaron.

Aaron Saray (05:06):
Well, if that thing that's type hinted that can be extended... At least in PHP, there are a number of rules yet with the language constructs that require there to be exact same method signatures and stuff of something you extend. So whatever is public and whatever is available on that class or that instance is still going to be... I guess you could make some of those methods protected or private. I can't remember off the top of my head what would happen. But again, there's a whole host of other problems there that you have versus trying to tell someone, "Well, don't do this to my code."

Joel Clermont (05:47):
Right. And I know a couple times I've done something and in a code review you're like, "Joel, why did you do that?" And I gave an answer like, "Oh, I was doing it to fix this error in the IDE," or, "Do something for the tool," or, "Make a test easier." And you're like, "No, don't change application code you know, to make tooling happy basically." Like, we've had this discussion many times so I thought that's the argument you're going to go for here.

Aaron Saray (06:11):
No. To expand further, I can see where someone might extend a class and then maybe I think you could make a public method protected. But why are you then doing that? I mean, why? You know, what are you getting in your class?

Joel Clermont (06:27):
Yeah, I don't think it's that somebody actually is doing that, but it's PHPStan knows that's a possibility. Again, it's only at the very high levels. It might be eight or nine or something. It'll yell about that and say, "Hey, this could be extended," and that complaint goes away if you mark the class final. That wasn't the only one argument I saw recently I found really compelling from a technical standpoint. One that's maybe not a technical issue and more of a people issue is, what do you think about this idea that if I'm publishing something in open source that I don't owe you something? I guess as the user of the package. Whereas if I don't mark something final and I want to change it, now it's like a support burden or something... It's potentially more people yelling at me about this free thing that I'm giving them. Whereas if I mark it final, you can work around it. But I am making it very clear this is not going to be supported if you want to extend it and change it, and then I change it out from under you. Does that hold any water with you?

Aaron Saray (07:34):
I think what you're talking about more is application of interfaces, not something being final. Because I don't see how marking a class as final, if it has any public properties or anything like that, can make it not be changeable in future result or on things. I mean, you can still change a final class on the next release of the software. I think it's more so people who don't want to implement and stay with an interface that is contracting out to the general public that, "I'm going to have these things available, maybe later on I don't want to have those." That's a different conversation but making a class final doesn't really control that in my opinion.

Joel Clermont (08:07):
No, it doesn't. Like, you still have the whole issue of semantic versioning and things like that. But I think it's a signal if I'm the author of a package and this is an implementation class really meant for internal use, one way to make that obvious is by marking it final. Like, "You can't extend this and tack methods onto it or do other things to it."

Aaron Saray (08:28):
Well, in the same methodology... I mean, I guess there's two things. There's a sort of implicit contract that we make as open source developers that when we release something open source, we try to make it usable for other people. Otherwise you do what I do, which is just write about it on your blog, you know?

Joel Clermont (08:46):
Yeah.

Aaron Saray (08:46):
I want to teach people but I don't want to make packages for all this stuff so I just write about it and then they can take it and synthesize it. And I also think when you say, "I don't owe you anything or whatever, and I can change my stuff." Well, you can, you definitely can. I mean, there's nothing in the language of PHP that makes us use semantic versioning, it's a construct that we all agreed to do. Same thing as like, if I really want to dig into your source code and use an internal class that you don't want me to, what does that matter? Let me use it. I don't have to upgrade my package so if you publish things and say, "These are the things that are usable."
And you put your documentation, "Here's the method you should call." If I use internal stuff that you didn't publish and say, that is something that I should be using to accomplish the goals, well then that's the thing that you take on yourself. And I can just say, "I'm sorry," as a package owner, "it's great that you had good experience with that on that version. That's not something I intended to use." You can continue to use it that way, I'm glad it solved your business problems for you at the time but that's not a thing I'm guaranteeing. That's not part of the public interface that I described.

Joel Clermont (09:50):
Or you could just come up with really obnoxious antagonistic class names. So if somebody is extending it, you could name it, Don'tUseThisOrElseItWillBreak.php, I don't know. Okay, I see your point. Again, I still don't feel very strongly about it but I appreciate your take on this. The other thing that kind of goes along with this that I know we were talking about before we started recording, was private properties within the class. Does this kind of fall under that same umbrella as a final class?

Aaron Saray (10:23):
Yeah, it really does for me is, I understand the idea of encapsulation in a class and what's available outside of it. So I'll use protected properties, public and protected, but I don't really find a lot of value in private. Every once in a while when I know something is just a placeholder for myself for between two functions or something, I don't know, it's usually then a sign that you're doing something wrong anyway, but I'll use a private. But most of the time it's protected because I'm saying, "I intend for these to be in this class, but if you have a different use case for this class you don't get to modify, you don't get access to my stuff publicly because I didn't make it for that. But if you want to extend my class, you now have access to everything and you can do what you want." And my only liability is the code I wrote, not what you extend and what you use with it.

Joel Clermont (11:11):
Okay. I guess those two things definitely go hand in hand then.

Aaron Saray (11:15):
I should clarify, I'm absolutely looking for examples of why I'm wrong.

Joel Clermont (11:21):
Oh.

Aaron Saray (11:21):
Well, because this is one thing that really confuses me is, why people take the other side. The only thing I can think of, and I'm sure this is wrong, but some sort of hubris talking about how your code should never be altered or whatever. That's not what open source is. You can go on Twitter and you can message Joel and he'll tell me.

Joel Clermont (11:38):
Oh boy. That's right. Yeah, you're not out there listening, but I will relay that message.
Recently, I was going through a drive-through at a restaurant and it was kind of a long line. So there was likely already some frustration from the guy working the window because it's kind of hectic. But I get up there and he tells me how much it costs, I give him my credit card and he takes the card and he sticks it in the machine and he's just looking at it. And he's getting more impatient, and he's getting frustrated. And then all of a sudden he says, "Timed out." "What do you mean timed out? Why does a credit card need to take a time out?" "I need a time out." He wasn't yelling. I said it maybe a little more forcefully, his was more muttering. But then I just thought as a computer person, I know what timeout means, right? Some sort of communication has to happen and if it doesn't happen within a timeframe, it's a timeout. But to a non-technical person, that's kind of odd terminology. And in that frustrated state, he was not having it. But I just thought, "Is there a better message to give this poor guy at the drive-through window?"

Aaron Saray (12:55):
Well, it's like then when we try to do that stuff, we say silly things like, "Please try again later." It's like, that's not helpful.

Joel Clermont (13:03):
How long is later?

Aaron Saray (13:05):
Yeah. All I know is if you're in a drive-through line that's really long, best thing you can do is if you have a performance car activate launch control, or just on your regular car, just start revving it. People love that.

Joel Clermont (13:19):
No.

Aaron Saray (13:22):
If you disagree with us so much that you want us to work on your project so you have an example of code that you hate and disagree with-

Joel Clermont (13:30):
We can help. Head over to nocompromises.io and book a call.

No Compromises, LLC