39

While writing the code or during design do you try to generalize the problem at the first instance itself or try to solve that very specific problem.

I am asking this because trying to generalize the problem tends to complicate the things (which may not be necessary) and on the other hand it will be very difficult to extend the specific solution if there is a change in the requirement.

I guess the solution is to find the middle path which is easier said than done. How do you tackle this type of problem ? If you start generalizing it at what point of time you know that this much of generalization is sufficient?

3

9 Answers 9

64

Too often when you try to design for the future, your predictions about future needs turn out to be wrong. It's usually better to refactor when you actually know how the needs have changed than to overdesign your system on day one. At the same time, don't shoot yourself in the foot, either. There's certainly a middle ground, and knowing where that is is more art than science.

To boil it down to one rule of thumb: less is more.

2
20

Are you familiar with Agile? One of the big principles of Agile is YAGNI. I find that's the best way to approach things.

"You aren't gonna need it" ...is a principle of extreme programming (XP) that states a programmer should not add functionality until deemed necessary. Ron Jeffries writes, "Always implement things when you actually need them, never when you just foresee that you need them."

...YAGNI is a principle behind the XP practice of "do the simplest thing that could possibly work" (DTSTTCPW). It is meant to be used in combination with several other practices, such as continuous refactoring, continuous automated unit testing and continuous integration. Used without continuous refactoring, it could lead to messy code and massive rework. Continuous refactoring in turn relies on automated unit tests as a safety net (to detect unforeseen bugs) and continuous integration to prevent wider integration problems...

YAGNI is not universally accepted as a valid principle, even in combination with the supporting practices. The need for combining it with the supporting practices, rather than using it standalone, is part of the original definition of XP...

5
  • 3
    While I agree with YAGNI more or less, I can't find it in the agile principles: agilemanifesto.org/principles.html Mar 24, 2009 at 18:16
  • "Simplicity--the art of maximizing the amount of work not done--is essential," would apply to YAGNI and some other agile practices.
    – tvanfosson
    Mar 24, 2009 at 18:27
  • 1
    While it doesn't specifically say "YAGNI" in the manifesto, I think that they are very much in line with each other.
    – Matt Grande
    Mar 24, 2009 at 18:42
  • 2
    @Jens and @Matt, YAGNI, is in Agile by means of XP being bundled as an "agile" methodology. As mentioned in the Wikipedia article the YAGNI principle was developed by Ron Jeffries as a part of the XP core practises.
    – Rob Wells
    Jun 26, 2009 at 13:38
  • 1
    It may be true that YAGNI is developers' idiom, but TDD is the one that applies this dilemma pretty nicely. In the step where it says that you should only write enough code to pass the test and no more. And TDD is part of agile. Mar 28, 2016 at 5:52
13

This is probably one of the most difficult parts of software development because you need to walk the line between "YAGNI" and "PYIAC" (Paint Yourself Into A Corner).

It's pretty easy to say "don't write a feature unless you need it". The hard part is designing your code so that you can easily add features later when you do need them.

The key is to be able to design an extensible architecture where you don't write any more code than what you currently need. The ability to do this well really comes from a whole lot of experience (and pain).

7

I spend some time upfront thinking about the general direction of the design - not too much, but enough to basically sketch out a high-level overview. I then follow a story-based agile methodology using TDD to develop solutions for individual stories. As I'm implementing via TDD I keep my high-level overview in mind and either (a) direct my particular implementations to follow the high-level overview or (b) refactor (and improve) my high-level understanding/direction based on what I learn during testing/implementation.

I think it's a mistake to do no planning upfront, but it's probably a bigger one to do too much. As much as possible I like to let me experience guide me in the big picture and then let the design grow organically along the lines I've laid out in my mind for how the application is going to develop. Using TDD I find that the design itself is forced into better design principles (decoupled, single responsibility, etc) and is more malleable with respect to changes than if I try to pre-conceive the whole and fit the development into it.

3

A good design accomodates future changes and is definitely worth going for. Consider the UNIX operating system and its "everything is a file philosophy". That design decision was taken not to meet some immediate need but with a view to future requirements. One shudders to think what an operating system based on an "agile" design would look like.

2

What you're trying to deal with has to do with reuse (i.e. the generalization of a problem that you're dealing with now so you can reuse the work (code) in the future). I've said it before and I'll link to it again.

I think I've heard other people say something to the effect of:

I solve the problem the first time. When I repeat my solution the first time, I note it. When I repeat it again, I refactor.

2

Design for "now + 1". That means, solve the immediate problem at hand, and build in enough functionality so that next time they ask for a change, you've already got it half way (or more) done and have the choice of either a) solving it immediately and refactoring later, or b) solving "now + 1" again (with the "now" half done)

This does depend project, and, in short, experience will teach you what the "+1" is.

1

The philosophy of YAGNI, You Ain't Gonna Need It, can be summarized with (from the article):

According to those who advocate the YAGNI approach, the temptation to write code that is not necessary at the moment, but might be in the future, has the following disadvantages:

  • The time spent is taken from adding, testing or improving necessary functionality.
  • The new features must be debugged, documented, and supported.
  • Any new feature imposes constraints on what can be done in the future, so an unnecessary feature now may prevent implementing a necessary feature later.
  • Until the feature is actually needed, it is difficult to fully define what it should do and to test it. If the new feature is not properly defined and tested, it may not work right, even if it eventually is needed.
  • It leads to code bloat; the software becomes larger and more complicated.
  • Unless there are specifications and some kind of revision control, the feature may not be known to programmers who could make use of it.
  • Adding the new feature may suggest other new features. If these new features are implemented as well, this may result in a snowball effect towards creeping featurism.
0

I'm a big believer of designing for the problem at hand and not blowing out your design by trying to guess all the cases that you have to cater for because "someday we might need it".

Basically, given a list of specific requirements, design against that, however, this does not mean that you shouldn't:

  • make aspects of your design configurable rather than hard coding those aspects in your solution. Either via parameters passed at runtime or via an external config read at startup (or after HUP'ing).
  • lace your code with magic numbers,
  • avoid having a look to see if there is something already written that you can reuse, maybe after adapting the existing solution to provide an approach that is suitable for the existing situation as well as for the new requirement(s).

The main problem with designing for "possible futures" is that you are always just guessing. Possibly making educated guesses, but "when push comes to shove" it's still just a series of guesses.

By doing this you also have the very real possibility of squeezing your solution to fit the general case(s) rather than solve the specific problem at hand as defined by your known requirement(s).

What's that saying? "When all you have is a hammer, everything begins to look like a nail."

I wish I had a pound for every time I've heard someone say, "But it's a solution that's more adaptable for those general cases we might see in the future."

Not the answer you're looking for? Browse other questions tagged or ask your own question.