DEV Community

Cover image for You ain't gonna need it
Lina Jelinčić for Bornfight

Posted on

You ain't gonna need it

I love coming back to a feature that was developed a couple of months ago and seeing that extending it is just a walk in a park. When thinking about a solution, I always ask myself the same question: If I were to change this in a month, how hard would it be?

I find it a great way to catch flaws in my solutions very early, but it can also lead to something called overengineering. To put it simple, overengineering is the act of solving a problem that doesn’t exist anywhere besides your mind.

Overengineering leads to bad code

It took me a long time to realize that even though my code is nice, clean and easy to extend, it is still a bad code. It often introduces unnecessary complexity, being harder to maintain and harder to understand by other devs, while taking a longer time to build.

None of it is a bad thing by itself. If you were to code just to get it done, you can get by, but in a month all you would get is messy, spaghetti code that nobody wants to work with in the future. It becomes a bad thing when that price is paid for a feature that you really don’t need.

Do you actually need it?

I’m still fighting this battle myself and the thing that is helping me is a principle of extreme programming (XP) called “You ain’t gonna need it” (or simply, YAGNI). XP co-founder Ron Jeffries has written: "Always implement things when you actually need them, never when you just foresee that you need them.”

Now, whenever I start overthinking and catch myself wondering “But what if X happens” I just stop and write down my what ifs. Also, I think about what would happen if that feature is not implemented. Is the added complexity the price I’m willing to pay just now?

Making that call is not easy

It is important to differ what is needed and what is not, even though it is difficult to do that. If that is a decision you are having problems with, ask your teammates. A fresh look at the problem is something that always helps. If for whatever reason that is not possible, go with a simpler solution. Keep the list of what ifs and if they actually happen, choose the more complex solution.

This approach helped me with decision making paralysis and I believe it also made me a better developer.

How about you? Do you also sometimes overengineer? What are your tips and tricks for deciding what is needed and what is not?

Top comments (21)

Collapse
 
eljayadobe profile image
Eljay-Adobe

Could leave yourself a breadcrumb for work not done, that maybe wasn't needed, maybe will never be needed.

Picking JavaScript today, rather than my usual C++.

// YagniError - something was not done yet because it wasn't needed yet.
// Appears to be needed now.  Happy fun time is over.
class YagniError extends Error {
    constructor(to_do) {
        super("YAGNI: " + to_do)
    }    
}

// print_value - print the word for the number.
// We'll yeet a YagniError for the unhandled cases.
function print_value(value) {
    if (value === 0) console.log("zero")
    else if (value === 1) console.log("one")
    else throw new YagniError("print(value:"+value+")")
}

try {
    print_value(3)
} catch (err) {
    console.error(err.toString())
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
linajelincic profile image
Lina Jelinčić

I like the idea of leaving breadcrumbs! It's self-documenting and pretty straightforward.

Collapse
 
murticalen profile image
Alen Murtić

Great article!
I agree that over-engineering produces a lot of bad code. Especially over-abstraction for code that can have one, and only one, implementation.
Though, one should never reject forward-thinking system design, if there is a clear notion that the functionality would be extended and if it takes comparable amount of time to implement it.

Collapse
 
linajelincic profile image
Lina Jelinčić

I couldn't agree more!
Forward-thinking is one of the most fun and important part of being a software developer. The problem occurs when that clear notion is not so clear and when you find yourself solving problems that don't and might never exist.

Collapse
 
marcmacgonagle profile image
marcmacgonagle

Good post.

One useful suggestion by Martin Fowler is to imagine making the change at a future point. This tends to lead to two things. Firstly, realising that the cost at a future point may not be as bad as first thought. And, secondly, coming up with some small changes that would make the change easier at a future point without adding much complexity.

Collapse
 
linajelincic profile image
Lina Jelinčić

Thank you!
As usual, Martin Fowler is on point. This is really useful how-to.

Collapse
 
aleksandarperc profile image
aleksandarPerc

When I would like to implement something but suspect that now it is not the time.. i write a comment // TODO write a method for... . That helps me feel better and I never do anything from those TODOs :-D

Collapse
 
linajelincic profile image
Lina Jelinčić

I usually write down what ifs in my notes, but I really like idea of documenting it in code!

Collapse
 
aleksandarperc profile image
aleksandarPerc

This VSCode plugin gathers all project TODOs
marketplace.visualstudio.com/items...

Collapse
 
rahoulb profile image
Rahoul Baruah

It's one of the advantages of Test-Driven Development (actually Behaviour Driven Development is what I do, but the idea is similar).

Because you start with an executable specification, all you do is the bare minimum to make the tests pass. I have found that making the tests pass looks like an insurmountable mountain for ages - then suddenly everything works and you're really surprised as how simple the resulting code is. (Although, now you have tests you're also safe to refactor if it does need tidying up)

And because all you're concerned with is making the tests pass, all the YAGNI stuff gets put on the back-burner until it, too, is included in a specification.

Collapse
 
linajelincic profile image
Lina Jelinčić

This is a great approach. You're right, tests ensure that you've done a good enough job and they would also help you enormously when (or if) refactor is needed.

Collapse
 
eelstork profile image
Tea

In a team environment, recommending retros vs what-iffs, let alone bread-crumbs or adding comments. I do inline a comment here and there, and I think the edge case is when writing code that's obviously fragile, because time is lacking to write it well.
Here's why.
The tomorrow you is smarter than the today you.
The tomorrow you understands the problem better.
What-iffs are you talking to yourself; they're a likely waste of your time, and more importantly (team environment) hardly anybody will take the time to figure what you possibly intended; they likely will solve the problem a different way and leave your stubs/breadcrumbs sitting here, feeling unsure what the stubs/comments intended; then what? Bloat.
A variant of what-iffs consists in endorsing as many best practices as possible, without an understanding of the context.
Finally, a thing I often see is developers getting reluctant to delete code produced by misunderstood (or mis-stated) requirements.
Travel light!

Collapse
 
linajelincic profile image
Lina Jelinčić

You made a few excellent points. You are right, just as all code comments, leaving breadcrumbs can result in mess.
And yes, deleting code is hard, especially if you've done something you're proud of. We should all remind ourselves that it's just a code and has one and only purpose: to solve problems, not to boost our egos.

Collapse
 
paxel profile image
paxel

I had to work with code of a guy that tried to solve each future problem. Of course the next problem was not solved and from there on life was hell. Adapting an overdesigned framework immediately leads to crap. change my mind

xkcd.com/974/

Collapse
 
linajelincic profile image
Lina Jelinčić

Oh yeah, overdesigned frameworks are just evil.

Collapse
 
andreidascalu profile image
Andrei Dascalu

Well, you seem to be mashing together a couple of somewhat related things but not in a way that seems to connect logically.
You start with over engineering, which is about code design and implementation and then go about features (not sure if there's a word for it, perhaps over-productization?).
Overengineering, basically engineering things a certain way just for the sake of it, is a bit of weird case. It's weird because there will always be a moment when you're doing things to preempt problems. When you're just beginning a project, you make decisions that solve potential issues you know about from experience which you might hit (but then again, might not - experience is never 100% replicable). While it's natural to happen, people tend to forget that different people come with different experiences and different takes which they will try to apply all at once in that stage.
Point 2, which is more a rule-of-thumb kind of guideline, is to never engineer extensibility before it's needed. Don't design a contract that's currently used once. Design it when you have at least two uses in mind, which are needed right now.
On the second matter, features (which is what Ron Jeffries specifically refers to) are no longer a development decision. Nowadays we have (generally, unless you're a garage startup - though even then ...) product designers that describe features. It's still a great thing to not implement something unless it's needed (and particularly in startups people avoid it like fire when going for an MVP) but it's beyond the domain of pure development. Overengineering a product (in the sense of taking a convoluted approach to feature implementation for the sake of opening up future options) would probably get you skinned alive in a time-pressed startup (but unless you double it with hurried spaghetti code, it won't kill anyone except probably accountants). Overengineering an implementation (architecture, code) is a more immediate development issue that's guaranteed to come haunt you in many different ways.

Collapse
 
linajelincic profile image
Lina Jelinčić

Thanks for this comment, it really made me think. When I talk about overengineering a feature, I don’t talk about defining scope of it. For me, as a developer, overengineering a feature is adding unnecessary complexity to code only because of a fear of the future problems. It’s all about that thin line between building right amount of flexibility into a code and overengineering.

Collapse
 
miguelmj profile image
MiguelMJ

This is completely spot on. I deal with overengineering myself and I'm learning to have a more realistic approach to the solutions I code. One approach I'm trying is that, when I'm not sure if something is going to be really needed, then I put a short time limit to implement it. If it takes too long, I quit and continue with the rest of the code.
I only want to add one thing: you can do overengineering in personal projects to learn. I have a lot of code that I never use, but I try to have it heavily commented and its common that I revisit it to see how I thought that certain problem could be solved.
However, the focus of this code is not quality, but experimentation and learning, so its important to keep it separated from real code!
Great post, Lina :)

Collapse
 
linajelincic profile image
Lina Jelinčić

Thank you!
I like the idea of overengineering personal projects. It really can be a great way to fully grasp some concepts and find out what is going on under the hood. In the worst case scenario, you learn how not to do something, which is also a great thing!

Collapse
 
ajest profile image
Pablo Fumarola

Im agree with your post and the importance using YAGNI, but every new feature is different depending the project you are working on. Let's say that you have to code a new big feature, but you have to deliver it in steps, in order to let the client to see how is it working and put these functionality in the table letting the rest of the team to see how is this working for example. In that case, you can save a lot of time by doing every step in flexible way because you have the certainty that you are going to need it, which doesn't violate YAGNI principle.

Collapse
 
linajelincic profile image
Lina Jelinčić

If there is certainty, absolutely!