loading...
Cover image for Practicing YAGNI

Practicing YAGNI

gonedark profile image Jason McCreary Originally published at jason.pureconcepts.net ・4 min read

A while back I spoke at Laracon about Practicing YAGNI. It was an honor to present in front of such a large audience at such a premiere conference. To this day, I continue to receive a lot of feedback and interest in my talk.

To that point, many have asked me to share my slides. As the slides were mostly placeholders for discussion, I felt a blog post would better summarize the talk. Nevertheless, if you want to see the slides, you can watch my talk on StreamACon.


I consider myself a searcher. On a quest to find the Holy Grail of programming practices - that single practice which instantly levels up my skills. While I know this doesn't exist, I do believe in a set of practices. Recently, I found one to be YAGNI.

YAGNI is a principle of eXtreme Programming - something I practice daily at work. YAGNI is an acronym for You Aren't Gonna Need It. It states a programmer should not add functionality until deemed necessary. In theory, this seems straightforward, but few programmers practice it.

Why practicing YAGNI is hard

Before we continue talking about YAGNI, we need to understand the problem it solves. The problem is over engineering. At some point, we started priding ourselves on complexity - obsessed with playing design pattern bingo and building ever more intricate architectures in our head.

XKCD illustrates over engineering well with "The General Problem".

The General Problem

This is funny because it's true. But it begs the question - why can't we just pass the salt?

What ever happened to KISS? What's wrong with an MVP? The answer is nothing. We need to find our way back to simple. YAGNI can help us get there.

How to practice YAGNI

I think Ron Jeffries, one of the co-founders of eXtreme Programming, summarizes practicing YAGNI well:

Implement things when you actually need them, never when you just foresee that you need them.

Nonetheless, the most common contention is timing. We continually write code sooner than we actually need them. This is the over-engineer in us. We confuse foreseeing with needing.

To help distinguish between the two, we can create a time horizon. Kent Beck describes this well during an interview on Full Stack Radio:

…I did a little experiment… what if I deliberately stopped trying to predict the future and limit my design horizon to six months… things went better for me… I was less over engineering. I was making progress sooner. I was less anxious… Things were cleaner, easier to understand… So what about three months? One month? I never reached a limit with that experiment…

In this way, practicing YAGNI becomes a time experiment. One where we keep decreasing our time horizon to help limit the code we write. Ideally until we reach a point where we don't write code until it's actually needed. Not just because we're thinking about it, or want to, or it relates to code we're working on. We wait until the current code requires us to implement new code in order to work.

At first, I'll admit, this will feel like laziness. It's going to seem like you're intentionally avoiding writing code. In a way, this is true. The catch is, the code you're wanting to write isn't ready to be written. By waiting, you prevent all the bad things that happen when you make assumptions.

When not to practice YAGNI

Once you realize the benefits of YAGNI, you're going to try to apply it to everything (another programmer curse). You need to remember with great power, comes great responsibility. YAGNI isn't about saying no. YAGNI is about deferring unnecessary complexity.

As such, there will be times when you should not call YAGNI. Unfortunately, this takes experience. So I will outline a few scenarios to help those getting started.

  • Learning something new: You should take the time when evaluating a new technology. You'll gain the time back later, and mitigate the risk of losing more time by making the wrong decision.
  • Current design decisions based on future needs: YAGNI shouldn't handicap or sabotage our efforts. In these scenarios, make the future-proof design decision, but only implement enough to fulfill the current need. This allows us to limit rework, without completely undermining YAGNI.
  • Abstracting external dependencies: External dependencies add complexity to your project. Inline with the previous examples, taking the time to abstract these dependencies will avoid rework and decrease the complexity.
  • Testing, Security, Scale, and Business Requirements: Sorry, but YAGNI is not a free-pass on writing tests, secure code, considering scale, or business requirements.

What YAGNI means to me

Practicing YAGNI gives me confidence. I am comfortable delaying design decisions because I will be better informed in the future. I trust my ability to pivot quickly because my code is simple, making it easy to refactor and evolve. I write less code, and let's be honest, the best code is no code.


Want more? Follow @gonedark on Twitter to get weekly coding tips, resourceful retweets, and other randomness.

Posted on Jan 17 '18 by:

gonedark profile

Jason McCreary

@gonedark

I build things with my hands. The human behind Shift - https://laravelshift.com, master of Git - https://gettinggit.com, and author of "BaseCode" - https://basecodefieldguide.com

Discussion

markdown guide
 

No code runs faster than no code.
No code has fewer bugs than no code.
No code uses less memory than no code.
No code is easier to understand than no code.

  • Mike Perham
 

Abstracting external dependencies: External dependencies add complexity to your project.

This can very quickly be taken too far. I might add: "Abstracting external dependencies adds complexity to your project."

  • "Let's abstract away our relational database so we can easily switch from MySQL to postgres"
  • "Let's abstract away our ORM so we can more easily switch to another ORM"

These are cases where YAGNI should absolutely be practiced. Eventually you have to base your application on something concrete, i.e. a particular DB engine or programming language; trying to abstract these things away completely is a waste of time.

Furthermore, excessive abstraction can make it hard or impossible to actually use a tool effectively. For example, if your ORM provides pagination but you abstract away the ORM (using "repository pattern" or some such), you now have to reimplement pagination in your application code, or pass everything through, effectively rewriting the ORMs API.

I think YAGNI is the perfect philosophy when asking things like "What if we want to switch from MySQL to MongoDB?" To wit: you are probably never going to want to do that, and if you are, it's probably cheaper overall to just do the rewrites at that point than using some hobbled db abstraction layer that provides crappy SQL support & crappy mongo support but technically "supports both."

Thanks for the food for thought!

 

IMHO, You should keep decoupled your domain layer from the infrastructure layer.

It's not about "What if we want to switch from MySQL to MongoDB?", it's all about of separation of concerns. For example, by applying "Dependency Inversion", it only takes a few minutes (sometimes 1, YMMV) to create a domain interface and then create the implementation in the infrastructure layer, although you know that you aren't going to change the DB next month (Be careful here, because applying this principle everywhere is an example of over engineering, we're in a separation of concerns context).

As Jason McCreary says in the article, external dependencies add useless complexity to our domain and that boilerplate code will give us a lot of headaches when business people will ask us to add some features to the already existing use cases.

 
 

"I write less code, and let's be honest, the best code is no code."

This part reminded me of something one of my sensei's (masters, teachers) use to say to us in Karate:

"There's no better fight than the one that never happened."

 

Loosely related to this, how would you approach this situation Jason:

We decided to use a certain design pattern, but now we realize we need to shift the approach, perhaps add a constraint we did not have before. Maybe we were not using the pattern correctly at first, or we have a new vision on our needs.

Do you try and update all the existing code right away? Do you start writing the new code under the new approach and try to update the old stuff as you go? How do you discuss this with the team and share knowledge about shifting design guidelines?

 

Ideally, you wouldn't have decided on a design pattern upfront. Instead, you would have deferred the decision until you felt the pain and at that point realized the appropriate design pattern.

In the real-world, I understand this isn't always possible. So, given you have chosen a design pattern and have now found it's not the right one, I would swap it out. Nothing is sacred.

If code you have written no longer fits, don't be afraid to replace it throughout the code. If it works mostly and needs adjustments, practice YAGNI and do the simplest thing that could possibly work (you mentioned adding a constraint).

YAGNI is just one principle of extreme programming. Unless accompanied by lean process it can be jarring. So definitely be sure there is a way to share these sweeping design decisions. So long as there is, they shouldn't be feared.

Feel free to get more specific. I love this stuff…

 

I tend to follow a different but related approach: I try to imagine as many future scenarios as possible, and settle on the simplest approach for the current issue that, with only a reasonable amount of changes, would allow me to satisfy those hypothetical scenarios (while not satisfying them right now).

So more briefly, keep it simple, but don't block your future self.

 

While I understand what you mean, you're still playing the same old game. When practicing YAGNI, you don't want to spend your time imagining every possible future scenario. You want to do what works right now. Save the time and mental energy for later.

Said another way, believe your future self is smarter. In a paradoxical way, you aren't even smart enough to block them.

 

I get what you're saying, but imaging myriad possible scenarios and instinctually evaluating them for being blocked, is something that in reality takes on the order of 15 minutes. TDD even is more expensive than that.

 

I'd like to translate the article dev.to/gonedark/practicing-yagni-3n1d into Japanese and publish on our tech blog techracho.bpsinc.jp/ for sharing it. Is it OK for you?

I make sure to indicate the link to original, title, author name in the case.

Best regards,

 
 

I was actually surprised in a good way to see this technique mentioned in the Clean Architecture book, try to keep in mind the future but don't write code for it.

The majors impediment I think I will have when trying to apply this is with coworkers that "don't have time to refactor" and adapt the old code for new features. I hear this excuse every time I speak with a peer dev, so sometimes I take the time and try to think and code ahead 🙃

 

The best advice I’ve heard about YAGNI when talking to devs is to add a Y at the end. You aren’t gonna need it Yet.

Developers want to plan ahead and YAGNI on its own is a little condescending (in an I-know-better-than-you sort of way). Adding “yet” gets the point across that it may be a good idea and it may actually be needed, just not right now. Keep it in mind or write down for when it is actually required.

Oftentimes you can look back in a month or year and realize that the extra functionality wasn’t actually needed but you are prepared for the day it is needed

 

my biggest problem with this is that I only want everyone else to do it!

 

When everyone on the team practice YAGNI, so much of the noise fades away. You truly get to focus on the code. It's great!