YAGNI revisited



I’m starting a new blog post series about the most valuable principles in software development. Not that I think you might not know them, but I rather want to share my personal experience and thoughts on that topic. The order in which I put those principles reflects their significance relative to each other, as it appears to be in my opinion.

That is quite a large subject and I’m going to dilute it with articles on other topics, so it might take a while.

Okay, let’s start.

Yagni: the principle

The abbreviation YAGNI stands for “You aren’t gonna need it” and basically means that you shouldn’t do anything that is not needed right now, because chances are you end up investing money in a feature that no one actually needs. Not only will you loose time spent on its development, but you will also increase the total cost of owning of you code base, because the more features a software contains, the more effort it takes to maintain it.

If I was asked what is the single most important thing that differs a good developer from a great one, I would answer that it is embracing and following the Yagni principle. I strongly believe that it is vital for a developer to fully understand this principle and, more importantly, possess the skill of following it.

Nothing can save you more time than not writing code. Nothing can boost your productivity as much as following the Yagni principle.

Thinking Yagni

I consider following the Yagni principle a separate skill. Just as other development skills, that skill can be trained. It also requires practice, just as any other skill.

Every software developer makes lots of little decisions in their day-to-day work. It’s important to incorporate “Yagni thinking” in this routine.

“I have implemented the AddCommentToPhoto method. I guess I also need to create an AddCommentToAlbum function. Although we don’t need it right now, I bet we’ll need it in future if we decide to allow users to comment their photo albums.”

“I’ve finished the CanonizeName method on the Album class. I think I’m better off extracting this method in a utility class because, in future, other classes might need it too.”

These are examples of thinking ahead, which is opposite to “Yagni thinking”. You might have noticed that in both cases, there’s a reference to some “future”. In most cases, when you find somebody making such statements, it is a strong sign they don’t follow the Yagni principle.

We, as software developers, are good in making generalizations and thinking ahead. While it helps us greatly in our day-to-day job, such trait has a downside. That is, we tend to think that some amount of work that we make up-front can save us a lot more effort in future. The reality is, it almost never does.

This may seem controversial, but in practice, laying the foundation for a functionality that we don’t need right now and might need in future almost never pays off. We end up just wasting our time.

Every time you are about to make a decision, ask yourself: is the method/class/feature I’m going to implement is really required right now? Or do you just assume it will be at some point in future? If the answer to the first question is no, be sure to forsake it.

It might seem easy to do, but the truth is it usually isn’t. We are used to creating reusable code. We do that even when we don’t have to, just in case. I remember my own mental shift being really tough. It required me to re-evaluate a lot of the coding habits I had at that moment.

Eventually, it pays off. It is amazing how that shift helps increase the overall productivity. Not only does it speed up the development, but it also allows for keeping the code base simple and thus more maintainable.

Thinking ahead

In some cases, thinking ahead can lead you in situations you don’t usually want to be in. I’d like to point them out.

  • Analysis paralysis. When you think of a new feature, you can be easily overwhelmed by the details involved in its development. The situation gets even worse when you try to take into account all the possible future implications of the decisions you make. In an extreme case, it can completely paralyze the development process.
  • Framework on top of a framework. Another extreme situation thinking ahead may lead you in is building your own framework.

Several years ago, I joined a project aimed to automate some business activities. By that moment, it had a rich functionality that allowed the users to configure almost any process they needed. The only problem with that solution was that the users didn’t want to do that.

The project ended up with a single pre-defined configuration that hardly ever changed later on. Every time someone needed to change a process, they asked developers to do that. Developers then changed the configuration and redeployed the whole application.

No one used the brand new highly configurable framework except the developers themselves. The project was implemented with a huge overhead just to enable capabilities that programmers could achieve by modifying the source code in the first place.

Following the Yagni principle helps in such cases a lot. Moreover, it often becomes life-and-death for the project success.

Yagni on the business level

Yagni principle is applicable not only to developers. To achieve success, it is important to also apply it on the business level. This strongly correlates with the notion of building the right thing: when you focus on the current users’ needs and don’t try to anticipate what they might want in future.

Projects that adhere to Yagni principle on all levels have much better chances to succeed. Moreover, following it on the business level pays off the most, because it helps save a tremendous amount of work.

Although business decisions are often deemed to be an area software developers can’t have an influence on, it is not always the case. Even if it’s so, don’t let it stop you from trying.

If you see that a feature that is going to be implemented is not needed right now, state it. Tell business people that they are better off not implementing it. There are two major points I use in such conversations:

  • We don’t know what the real requirements are. If we implement the feature now, odds are we’ll need to change the implementation in future and thus waste a lot of efforts we put up-front.
  • Adding functionality always adds maintainability costs. Every feature increases the time and effort required to implement all subsequent features, especially if that feature interacts with other existing features (i.e. is cross-cutting). Adding the feature later, when the need for it becomes obvious, helps speed up the development of the other functionality.

Following the Yagni principle: self-test

Do you follow the Yagni principle? If yes, how accurately do you do that? Here’s a simple test that can help you evaluate that. Don’t take it too seriously, thought.

Let’s say you need to create a function that calculates the sum of 2 and 3. How would you implement it? Scroll down to see the answer.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Here’s how most developers would do that:

public int Sum(int x, int y)

{

    return x + y;

}

Do you see a problem here? There is a premature generalization in this code: instead of a method that calculates the sum of 2 and 3 which was requested by the problem definition, we implemented a function that returns a sum of any two numbers.

Here’s the version that follows the Yagni principle:

public int SumTwoAndThree()

{

    return 2 + 3;

}

Of course, it’s a toy example, but it shows how deep in our minds resides the habit of making generalizations

Conclusion

Nothing helps gain efficiency as much as following the Yagni principle. It helps avoid a whole set of problems that relate to overengineering.

Although I fully advocate you adhere to it, there are some situations in which this principle is not fully applicable. Namely, if you develop a 3rd party library, you need to think at least a little bit ahead of the current users’ needs as future changes can potentially break too much of existing code using this library.

Besides that exception, following the Yagni principle is a must-have practice in software development.

Other articles in the series

Share




  • Renars Dilevka

    In my opinion, YAGNI principle will work and apply well only if you know how to to implement plan B, when you need. It is called being flexible in your mind and your work you do. All of given examples inside are related to context asked. They are not good examples, album is related to one photo, at least design system that when you will need it, you will apply without rewriting the link between photos and albums. Utilities always are needed, that is why it is utility, so think about where else it is possible to use this method. Wrong understanding of YAGNI really implies, we are just “code monkeys”, and do not think in a big picture. True professional line is when you can do both and you know consequences, not to just follow trends, or buzzwords.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thank you for your comment.

      I understand your point and I agree that we need to keep the big picture in mind while developing software. At the same time I don’t think it contradicts the YAGNI principle. You can think in a big picture and implement only functionality you need right now.

      >”So writing abstraction even the class with one method is worth your time”.

      Introducing an abstraction for only a single implementation is an example of thinking up-front. The thing is, to clearly understand what the abstraction should be, you need to test it at least with 2 or 3 implementations. We don’t create abstractions, we *discover* them, and you can’t discover an abstraction when you don’t have enough evidence that it fits the current code structure.

      The whole point of YAGNI is keeping the code base as maintainable as possible. Addition of abstractions always introduce complexity. Also, you can’t know what abstractions you will need later, so the best way to deal with it is to postpone such decisions to as late point in future as possible.

      I know that seems questionable (not saying more), but I do believe that this principle is the one that allows us to boost productivity the most (both in the short term and in the long run).

      • Renars Dilevka

        Yes, I guess this is about the moment, when you know you can find the balance, when you know where both approaches will end. But there are pitfalls on both ends, either adding over-engineering and complexity, either just do not care at all. And you know if i see the person, whom i know, i recognize his efforts towards good and solid software and i realize he can do both, i trust him with his decision. Contrary if i see the person who does not care, it does not matter he applies YAGNI or not, because he does not care, he will screw it up, and he will not learn for next time. So we need some common sense, yes apply YAGNI, but do not break other DRY (Don’t Repeat Yourself), S.O.L.I.D principles.

        So knowing your domain, and potential risks is the way to think about it. Because you need or do not about YAGNI or any principle is the same argument what causes to debate do you need to apply TDD or not. Right tool for the job in appropriate time.

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          I couldn’t agree more.

          Indeed, YAGNI, if not applied in conjunction with other principles and best practices, can be used to justify bad design and spaghetti code. And you are 100% right that YAGNI is not an excuse for mess in the source code.

          • Renars Dilevka

            Really like your blog, Vladimir! Keep up the good work 🙂

          • http://enterprisecraftsmanship.com/ Vladimir Khorikov

            Thank you! 🙂

  • David Raab

    While i agree to everything you explained. I cannot agree to your examples that you give here or in some other articles. I would say those are examples of a wrong used YAGNI.

    The point of YAGNI is to make someone aware that everything he does takes some amount of time. If i would explain YAGNI in one sentence it would be: “Save time through not implementing stuff you don’t need”.

    But the important idea behind YAGNI is Save time. While you seems to focus on stuff you don’t need

    The difference between boths are important and you see them in your examples. For example how much time did you save of writting your SumTwoAndThree() function instead of a generic Sum() function? The answer is: None at all.

    But if you didn’t save any time at all. You could just choose the more generic one that is more flexible. In the long run it automatically will save you some time. Even if you will never ever use the function again in your code and just use the function a single-time. You didn’t saved time in the first place, so it doesn’t matter which solution you choose. Well it doesn’t matter which solution you choose considering YAGNI, but there can be others principles like KISS so you choose another implementation. But YAGNI is surely about saving time through not implementing stuff you don’t need.

    In your example you need to add two numbers. So write a generic solution. The generic version takes the same amount of time. And surely, you needed to add two numbers so there is nothing wrong about a function that exactly does that.

    Another point is also that YAGNI is not about saving some seconds or some minutes of time. In some posts about interfaces before you argue that in some in-house software you don’t need to return the most generic thing. So instead of returning an IList it is okay to return List instead. And you argued with YAGNI. Once again. Writing IList instead of List as a return type saves you no time at all. YAGNI has nothing to do with it.

    The focus on YAGNI is to save time. Not to forbid flexibility. Your example also makes no sense. Because if it is okay to implement a function that does return 2+3 then you also could just write 5 wherever you need it your source-code directly. Your whole function that you have written would be basically a violation of your own YAGNI rule, at least how you explained it. Because you don’t need the whole function at all.

    The point of YAGNI is really to save time through not writing things that you don’t need at all. So it is about saving time through not doing things at all. In most of your examples you absolutely needed the stuff you showed and you removed the flexibility of your code, without saving time at all. And that is not YAGNI. You clearly needed the stuff. If you remove the flexibility and you don’t save time it has nothing to do with YAGNI. Then here YAGNI is just an excuse for bad code.

    YAGNI would be only applicable when you start implementing flexibility that you don’t need and you need hours or days to get those flexibility. So the focus is really on saving time.

    If you code like you suggest then you make things more worse. Because you don’t implement flexibility. Not implementing it didn’t you saved time in the first place. And when you later need it, you once again has to revisit your code, later implement the flexibility and probably change a lot of code and introduce new bugs. So what you really likely do is increasing the development time, not reducing it. And even if you later see that you never needed the flexibility in the first place, you still don’t won anything because you never saved time in the first place.

    So your examples are more a bad way of how YAGNI should not be applied at all.

    So once again. I think what you explained is correct, and i also think YAGNi is important. But i would say the way how you use YAGNI in your examples is just wrong and has nothing to do with YAGNI. It is the same thing when people using KISS and start making things more simple than they are really are.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thank you for such a full-scale comment!

      First of all, the example with addition of two numbers is a toy example, I used it just to show how deep in our minds resides the temptation to make generalizations. Of course, if I need to sum 2 and 3, I just use 5 instead of writing SumTwoAndThree 🙂

      In place of this problem, you can think of a more complex one. For example, when you need to solve a system of equations with 2 variables, and you decide to create a method that does it for an arbitrary number of variables. That would be a decent amount of work, which you don’t need right now.

      >”The focus on YAGNI is to save time. Not to forbid flexibility.”

      Flexibility and simplicity can’t be combined together. The more flexibility you add, the more complex your solution becomes. One of the purposes (I would say, the most important one) of the YAGNI principle is to reduce complexity at a cost of removing *unnecessary* flexibility. It’s not obvious with the example I put at the end of the post but consider the one I brought above.

      • David Raab

        Yes i knew that it was a toy example. But the important thing was not whether it is a toy example or not. The important thing was to make it clear when YAGNI is appliable. And in general if you don’t save much time then YAGNI is not appliable. If you should implement a function that does an equation and it costs you less than a minute for example to implement it, you can’t really argue with YAGNI. YAGNI is really about to focus on what to solve in a big application. If you start for example developing shopping logic in a web application because you think it might be needed, but nobody needs it now, that is what YAGNI is about. To focus on the things that are important.

        And in fact. If you need an equation between two things than YAGNI doesn’t really apply, because you need it in the first place.

        That flexibility and simplicity cannot be applied. Well i didn’t really said that, but when you mention it. I think flexibility and simplicity goes hand in hand. And you see that already in code.

        Just look at List, IEnumerable, Task, IObservable and so on. All of them are flexible solution. IEnumerable with all the LINQ Operations defined are not even flexible, you can basically transform everything in a flexibly and simple way and it is also declarative. Its flexibly and simple at the same time.

        Same goes for IObservable that you can see in the Rx library and all the Stream operations. And with Task we even have Asynchronous/Parallel code. In C# with the async/await keywords we even can write asynchronous code that is as easy as synchronous code.

        F# goes even further and provides a general generic solution with computation expressions to allow generic solutions. and you see that with async {} and seq {} that it is a generalized solution that can make so much things even more simple.

        Scott Wlaschin also has a great post about removing cyclic dependencies. http://fsharpforfunandprofit.com/posts/removing-cyclic-dependencies/

        The code in the end is more flexibly and even more simple and easier to understand. And how did he do it? He primarily generalize things and creates generic types.

        In my opinion we as humans are really bad to find generalizations. What we usually do is just add just more arguments to functions/classes. But that is not a proper generalization. In fact it is exactly the opposite of a generalization.

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          I’d argue about your first point. Even if the more flexible solution doesn’t require much time to be implemented, it still has a maintenance cost. The more flexible you make your solution, the more cost you introduce.

          Also, I don’t think IEnumerable, Task, IObservable, and F# are good examples here. The flexibility (and as a result, complexity) they introduce is justifiable because it allows us to solve the underliying problem in a much easier way than we’d be able otherwise. Moreover, F# gives us generic parameters by default, so we don’t even have to do anything for that.

          Returning to the example with system of equations with 2 variables, even if creating a function that solves this problem for arbitrary number of variables doesn’t cost you much, I still think you shouldn’t do that. Doing that would make code less obvious to understand and more complex to maintain.

          • David Raab

            I wouldn’t say that a more flexible solution automatically increases the cost is true. It is only true if you don’t need the flexibility. If the flexibility you programed helps you, you get much more code-reuse, you write less code. And your code gets easier to understand.

            You say that IEnumerable, Task, IObservable are justified because they solve the underlying problem much easier. And that is the direction about what i would talk. What did we do before this generalized solutions? We used our foreach loops, with lots of if/else if/else, switch statements and a lot of other things. We used Threads with manual locking and so. And when we always just would exactly do the minimum to just solve the problem like you describe with your idea if YAGNI, we would still do the same.

            We only got IEnumerable, Task or IObservable because we tried to generalized the problem and created a generalized solution for it. But with your idea you would never create something like that, because you would always say that you never would generalize the problem in the first place.

            For the equations with arbitrary arguments. You don’t really need to write that, because when you have a function that solve the problem with 2 variables, you automatically can use List.fold or LINQs Aggregate Method to get a function that works on a an arbitrary lists. Just as an example to see how generalization can help you do new work.

            And i can’t really agree that it would make code less obvious or understandable. If you need that functionality nothing is more understandable than a function that has abstracted the logic away out of your sight. If you don’t create such a function, and you still need the logic, you will probably end up with duplicated logic in your code somewhere.

            So in general i would say that generalized code will always be more maintainable, easier to read, probably even be easier to test, debug. But i don’t say that it is always the fastest or easiest solution to write.

            I don’t think we can have both. Either the code is easy and fast written and probably hard to maintain, or you generalize a solution and you get better maintainability. Sometimes it takes a lot of time to get to such a solution sometimes the amount of work is justifiable.

            But you cannot generalize it and say it is always a bad idea.

          • http://enterprisecraftsmanship.com/ Vladimir Khorikov

            Interesting points, thanks. I think it boils down to concrete code examples. It’s hard to say where generalization would be appropriate without seeing a full picture.

          • daykjs

            And the winner of this discussion iiiiis….

            ….
            ….

            …..
            …..

            DAVID!