YAGNI-C as a Practical Application of YAGNI

YAGNI-C as a Practical Application of YAGNI

By Sergey Ignatchenko

Overload, 21(117):12-14, October 2013


YAGNI can seem vague. Sergey Ignatchenko offers a more precise definition.

ALGOL 68 was the first (and possibly one of the last) major language for which a full formal definition was made before it was implemented.
~ C.H.A. Koster

... as a tool for the reliable creation of sophisticated programs, the language [ALGOL 68] was a failure ...
~ C.A.R. Hoare

Disclaimer: as usual, the opinions within this article are those of ‘No Bugs’ Bunny, and do not necessarily coincide with the opinions of the translator or the Overload editor. Please also keep in mind that translation difficulties from Lapine (like those described in [ Loganberry04 ]) might have prevented providing an exact translation. In addition, both the translators and Overload expressly disclaim all responsibility from any action or inaction resulting from reading this article.

The YAGNI (‘You aren’t gonna need it’) principle is well-known in the agile world, going back to XP (as in ‘eXtreme Programming’, not ‘Windows XP’) in the end of 1990s. Unfortunately, this concept is too open to interpretation, which causes lots of confusion and heated debates both in industry [ Fowler04 ] [ Devijver08 ] [ Litzenberger11 ] and in academia [ Boehm02 ].

This article describes a practical approach to YAGNI, which has been tried in practical agile projects (one of which has had releases to millions of customers every 2–4 weeks). For the purposes of this article, we’ll name it YAGNI-C (as ‘YAGNI-Clarified’). While not being universal, we hope that YAGNI-C might be useful in quite a wide range of projects. Oh, and if somebody is about to say “Hey, we’ve been doing exactly the same things for years” – of course, YAGNI-C is not something really new; the problem is that such practices (which we think are best practices) are not often described, and therefore cannot be widely used.

The very beginning

The story starts when we’re about to start our new agile project to make our super-duper app. The first question is – are we going to have some architecture? In general, it depends, but let’s consider projects where architecture is essential, so the answer is ‘yes’. The second question is – within the architecture chosen, are we going to have our own set of libraries (let’s name them ‘infrastructure libraries’) which needs to be common for a significant part of project? Again, in general, it depends, but let’s assume that in our project (for example, due to the project size/complexity) we’ve decided to have such a set of infrastructure libraries (it may be just a glue, or something more substantial – it doesn’t matter too much, the key thing is that these libraries are supporting a big part of the whole project). Now, let’s assume that APIs to these libraries are designed and supported by one or more people, let’s name them ‘library API maintainers’ (with a hope that there is at least one of the architects involved in this group). Now let’s try to define some principles and procedures of how ‘library API maintainers’ should approach YAGNI within our YAGNI-C model.

Thinking, not implementing

The First Principle in YAGNI-C is that ‘thinking ahead is good, implementing ahead is bad’. While the second part of the First Principle is actually YAGNI in its pure form, the first part of the First Principle may need a bit more of explanation. There are numerous complaints out there (see, for example, [ Fowler04 ]) that YAGNI is (or at least can be) misused to the point where any thinking about architecture is prohibited, and the project becomes a mess of ad hoc tactical decisions. The other side of the spectrum (library which does everything in sight) is also well-known to have led to disasters (ALGOL 68, DCE RPC, and especially X.500 are good examples of the over-designed systems which were so complicated that nobody was able to implement them properly). The First Principle above aims to strike the balance between these two undesirable extremes, and in practice it seems to work reasonably well (while in some cases, preliminary proof-of-concept prototypes may be needed before First Principle can be applied, starting from post-prototype development seems to work pretty well).

One other way to look at the First Principle is to rephrase it as ‘as long as you can think about design without starting to implement it – you’re fine, anything beyond that is over-design’. Essentially it restricts the amount of ‘thinking ahead’ to the amount of information which fits into the heads of the ‘library API maintainers’, which is subject to the cognitive limitations of the human brain, so it is fairly limited. In fact, the First Principle is much closer to the ‘very lean’ end of spectrum, while still allowing a certain amount of thinking ahead.

Specific cases

The Second Principle of YAGNI-C is ‘if nobody in the team can describe a very specific use case for a problem – the problem doesn’t exist’ (and whatever doesn’t exist doesn’t need to be solved). This Second Principle is of extreme importance for the whole process to function. What it allows is the transfer of discussion from the space of “Hey, why are we not using XYZ?” and “Why we don’t support paradigm ABC?” (which are subjects which can easily take months to deliberate on) to the space of very specific use cases, applicable to the current project, and while decisions might be not so obvious, at least it can be reasoned about not from the Swiftian big- and little-endians 1 point of view, but from the point of view where at least some logic can be applied.

Prohibit misuses

The Third Principle of YAGNI-C is ‘If in doubt how it should behave – prohibit it’. If, as a library implementer, you don’t have specification on a certain behaviour (for example, answering “what will happen if x.g() will be called before x.f() ” is not specified, and you yourself have doubts about what will happen in this case) – you should prohibit such behaviour (for example, inserting an assertion, but other means are also possible).

This Third Principle is essentially a manifestation of agile principle known as ‘deferring commitment’. In practice, whenever a library is released, people start using it in all kinds of ways, including those ways which were never intended. Prohibiting unintended uses (effectively deferring commitment of library writers regarding these uses) is good for at least two reasons: first, it makes code more reliable (making sure that caller really understands what is going on), and second, it allows implementation details to be hidden as deep as possible, reducing chances that library modifications which don’t change the specification can break the client code.

How it works

Within YAGNI-C, the process of infrastructure API design is as follows.

  1. First, library API maintainers design a very minimalistic API.
  2. Then, people from the rest of the project start to come and say, “Hey, your library doesn’t support this call, please add it.” According to the second principle, each such request MUST be accompanied with a specific use case – “WHY this call is necessary?”
  3. This is a point where library API maintainers perform analysis, deciding if a new call (class/...) should be added to the library API. As practice shows for good library design, about 30% of requests from step 2 above are turned down as “You’re solving the wrong problem, what you really need for your use case is...”, another 50% are turned down as “This can be done using existing API as follows:...”, and remaining 20% lead to extending the APIs. And as ‘library API maintainers’ did think about potential requests (see the First Principle), implementing additional calls is normally not that a big deal.
  4. Rinse and repeat from step 2.

It should be noted that YAGNI-C is substantially iterative, and therefore can’t possibly work in strictly-waterfall development environments.

Example

Let’s take a look and see on a specific example, how YAGNI-C might work in practice. Of course, this example is inherently extremely limited and oversimplified, but it still provides a good illustration of the concepts involved.

Let’s assume that Alice is a library API maintainer, and one of the classes she develops, is a class File (Listing 1).

class File {
  FILE* f;
  public:
  File( const char* filename ) {
    f = fopen( filename, ... ); }
  size_t read( char* buf, size_t bufsize ) {
    /* ... */ }
  void write( const char* buf, size_t bufsize ) {
    /* ... */ }
  ~File() { fclose( f ); }};
			
Listing 1

This class, as written, has a problem: it has an implicit copy constructor, which, combined with the nature of ~File() , will cause all kinds of problems. Now, Alice faces a question: what to do about it? One thought quickly crosses her mind: to add something like Listing 2 but she quickly realizes that as there is no requirement to copy class File , this is a feature which would violate our First Principle. Now, to comply with both the First Principle and the Third Principle, she writes Listing 3.

File( const File& ff )
{
  f = fdopen( dup( fileno( ff.f ) ) );
}
File& operator =( const File& ff )
{
  fclose(f);
  f = fdopen( dup( fileno( ff.f ) ) );
}
			
Listing 2
private: //copying/assigning of File
         //objects is prohibited
File( const File& );
File& operator=( const File& );
//in C++11, File( const File& ) = delete;
// and File& operator =( const File& ) = delete;
// can be used instead
			
Listing 3

This construct prevents other classes from calling the File copy constructor/assignment operator. This implementation is consistent with all the principles stated above.

Some time later, Bob comes to Alice and complains, “Hey, why don’t you support a copy constructor for File ?”. Given such a request, it is impossible to judge if it has merits or not, as it is not clear exactly what problem Bob faces; formally, this request violates the Second Principle and is therefore denied. As a next step, Bob elaborates:

The following piece of code doesn’t compile: void f( File ff ) { /* ... */ }

Now request is specific enough, but it immediately becomes obvious (at least to Alice) that Bob has just forgot to put & in the function declaration, so his problem can be solved without introducing a copy constructor for the class File .

At some point, Charlie comes to Alice and complains:

Why is the assignment operator prohibited for class File ? I want to copy a file and am trying to write:

    File f1( filename1 );
    File f2( filename2 );
    f1 = f2;//compiler error”

Once again, when a specific use case is provided, it is obvious that adding an assignment operator to File would be quite the wrong thing to do to solve Charlie’s problem, so Alice explains to Charlie how he can implement file copying for File (or she may decide to add static File::copy() for this purpose).

Summary

YAGNI-C is an attempt to clarify YAGNI so it becomes less vague and easier to follow. While YAGNI-C (though not under this name) has been successfully used for agile projects, it is certainly not a silver bullet, so you’ll still think to think if it is beneficial for your project. It should not be considered as a new approach to development (we know many teams which follow these or very similar approaches), but as a new way to describe existing best practices.

References

[Boehm02] Boehm, B. , ‘Get ready for agile methods, with care’ Computer , vol 35

[Devijver08] Steven Devijver, 1The YAGNI Argument (You Ain’t Gonna Need It)’ http://architects.dzone.com/news/yagni-argument-you-aint-gonna-

[Fowler04] ‘Is Design Dead?’ Martin Fowler, http://martinfowler.com/articles/designDead.html

[Litzenberger11] Dwayne C. Litzenberger, ‘The Price of YAGNI’ https://www.dlitz.net/blog/2011/02/the-price-of-yagni/

[Loganberry04] David ‘Loganberry‘, ‘Frithaes! – an Introduction to Colloquial Lapine!’ http://bitsnbobstones.watershipdown.org/lapine/overview.html

Acknowledgement

Cartoon by Sergey Gordeev from Gordeev Animation Graphics, Prague.

  1. Not to be confused with Intel/DEC little- and big-endians





Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.