When it comes to writing software, two of the most popular schools of thought are:
- YAGNI (or “you aren’t gonna need it”),
- and Generalization (or maybe “premature generalization”).
They aren’t necessarily exact opposites, but for where I’m going with this, it’s worth treating them as such.
In my experience, projects boil down to projects for customers or products that you’re building to sell. This isn’t to say we don’t build products for customers, so maybe it’s just easier to say:
- “Hey, I’m building this for someone else,”
- or, “Yeah, I’m building this for me [for profit].”
Here’s the thing: I often find that when it comes to building things for other people, it’s easier to want to go about Generalization for their code and YAGNI for our code.
So which one is right? Or is there even a right one?
We’re Not Gonna Need It (Or Are We?)
Before getting too much into this, let me provide a definition for each of the above terms off of which I’m working at least for the context of this post.
Yes, they can vary from book-to-book or blog-to-blog, but I’m going to go with the following:
I’m using this definition from this site:
Even if you’re totally, totally, totally sure that you’ll need a feature later on, don’t implement it now. Usually, it’ll turn out either a) you don’t need it after all, or b) what you actually need is quite different from what you foresaw needing earlier.
Honestly, I’ve read some material that says the opposite of YAGNI is “planning” which I think is pretty funny (although it may not be completey wrong 🤷🏻♂️). But I’m obviously not going with that for this post.
Instead, I’m going with, as you can tell, generalization:
[Generalization] happens when one sets out to “solve the general case” for a specific requirement. While general solutions are often “better” by some measure, the act of deriving them carries risk, particularly when the requirements for a general solution are imagined.
How Does This Play Out?
Working On Their Projects
Imagine you’re building a solution for a client and you have a well-defined set of requirements, though you know from experience what you’re building is going to need to:
- be amendable to change,
- support the addition of future features.
When this happens, it’s far easier to want to generalize the codebase so that you can support future features even though you have no guarantee that the codebase in question is going to need more than it already has.
Regardless, assuming that our client is going to want things from us in the future, doesn’t it mean that we should be building our code in such a way that we can easily introduce new features, extend it further, and so on.
We have no guarantee that the codebase in question is going to need more than it already has.
Why then do we feel the propensity to want to generalize the codebase?
Working On Our Projects
Conversely, imagine that you’re building a project for yourself. Since most of the people reading this site are WordPress developers, let’s imagine that we’re building a WordPress plugin.
In building it, we know exactly what we want it to do, and we’re going to be the primary authors of it. That is:
- we have the requirements,
- we know better than anyone the problem we’re trying to solve,
- and we hold enough domain knowledge to know what’s needed to ship the product.
In this case, we should inherent know what we’re going to need. But why is it common to maintain feelings of “I should build it just in case I want to add new things later.”
But if we maintain the most domain knowledge of any stakeholder of the project, shouldn’t we be able to make that call on our own?
And, thus, as we move through the development cycle we should be able to make the call if we’re gonna need something or not?
Musings On Development Styles
I don’t know if I have a solid conclusion to all of the above. Instead, this is a smattering of thoughts I’ve had, weakly organized, and shared primarily to demonstrate the various aspects that come with building software.
To summarize this as succinctly as possible:
I lean in the direction of wanting to maintain a healthy dose of YAGNI regardless of who’s building it, but that I fight the urge for Generalization.
But, you know, maybe there’s a middle ground that lies with proper object-oriented programming.
If we know what we want to build and what said thing would require, we know that we can define certain through the use of interfaces and abstract classes and then capitalize on that when the time comes.
But if that’s the conclusion to which all of this comes – that good object-oriented programming solves that various urges we have for writing code – then are we back at square one?
Do we need interfaces? Abstract classes? Why not just create the classes that we need and organize them in ways that make sense conceptually?
Here we go again, though. But not in this post.