35

As a programmer, I find myself in the dilemma where I want make my program as abstract and as general as possible.

Doing so usually would allow me to reuse my code and have a more general solution for a problem that might (or might not) come up again.

Then this voice in my head says, just solve the problem dummy its that easy! Why spend more time than you have to?

We all have indeed faced this question where Abstraction is on your right shoulder and Solve-it-stupid sits on the left.

Which to listen to and how often? What is your strategy for this? Should you abstract everything?

yannis
  • 39,647
Nayrb
  • 2,514
  • 1
  • 19
  • 22

9 Answers9

28

Which to listen to and how often?

Never abstract until you must.

In Java, for example, you must use interfaces. They're an abstraction.

In Python you don't have interfaces, you have Duck Typing, and you don't need towering levels of abstraction. So you just don't.

What is your strategy for this?

Don't abstract until you've written it three times.

Once is -- well -- once. Just solve it and move on.

Twice is the indication that there may be a pattern here. Or there may not. It can just be coincidence.

Thrice is the start of a pattern. Now it transcends coincidence. You can now abstract successfully.

Should you abstract everything?

No.

Indeed, you should never abstract anything until you have absolute proof that you're doing the right kind of abstraction. Without the "Rule of Three Repetitions", you'll write stuff that's uselessly abstracted in a way that's unhelpful.

Doing so usually would allow me to reuse my code

This is an assumption that's often false. Abstraction may not help at all. It can be done badly. Therefore, don't do it until you must.

S.Lott
  • 45,522
  • 6
  • 93
  • 155
21

Ah, YAGNI. The most abused concept of programming.

There's a difference between making your code generic and doing extra work. Should you spend extra time to make your code loosely coupled and easily adapted to do other things? Absolutely. Should you spend time implementing unneeded functionality? No. Should you spend time making your code work with other code that hasn't been implented yet? No.

As the saying goes "Do the simplest thing that could possibly work". The thing is that people always confuse simple with easy. Simplicity takes work. But it is worth the effort.

Can you go overboard? Of course. But my experience is that few companies need more of a "get it done now" attitude than they already have. Most companies need more people who will think things through ahead of time. Otherwise, they always end up having a self-sustaining problem where no one ever has time for anything, everyone is always in a rush, and no one gets anything done.

Think about it this way: chances are good that your code is going to be used again somehow. But you're not going to correctly predict that way. So make your code clean, abstract, and loosely coupled. But don't try to make your code work with any specific pieces of functionality that don't exist yet. Even if you know it will exist in the future.

Jason Baker
  • 9,653
12

A syllogism:

  1. Generality is expensive.

  2. You are spending other people's money.

  3. Therefore the expense of generality must be justified to the stakeholders.

Ask yourself if you are really solving the more general problem in order to save the stakeholders money later on, or if you just find it an intellectual challenge to make an unnecessarily general solution.

If generality is determined to be desirable then it should be designed and tested like any other feature. If you can't write tests that demonstrate how the generality you've implemented solves a problem required by the specification then don't bother doing it! A feature that meets no design criteria and cannot be tested is a feature that no one can rely upon.

And finally, there is no such thing as "as general as possible". Suppose you write software in C#, just for the sake of argument. Are you going to make every class implement an interface? Every class an abstract base class with every method an abstract method? That's pretty general, but it is nowhere near "as general as possible". That just allows people to change the implementation of any method through subclassing. What if they want to change the implementation of a method without subclassing? You could make every method actually a property of delegate type, with a setter so that people could change every method to something else. But what if someone wants to add more methods? Now every object has to be expandable.

At this point you should abandon C# and embrace JavaScript. But you still haven't gotten anywhere near general enough; what if someone wants to change how member lookup works for this particular object? Maybe you should write everything in Python instead.

Increasing generality often means abandoning predictability and massively increasing test costs to ensure that the implemented generality actually succeeds in meeting a real user need. Are those costs justified by their benefits to the stakeholders who are paying for it? Perhaps they are; that's up to you to discuss with the stakeholders. My stakeholders are not at all willing for me to abandon static typing in pursuit of an entirely unnecessary and extremely expensive level of generality, but perhaps yours are.

Eric Lippert
  • 46,558
5

Just to be clear, making something generalized and implementing an abstraction are two different thing entirely.

For example, consider a function that copies memory.

The function is an abstraction that hides how the the 4 bytes are copied.

int copy4Bytes(char * pSrc, char * pDest)

A generalization would be making this function copy any number of bytes.

int copyBytes(char * pSrc, char * pDest, int numBytesToCopy)

The abstraction lends itself to reuse, while the generalization just makes the abstraction useful in more cases.

More specifically related to your question, Abstraction is not only useful from a code reuse perspective. Often times if done correctly it will make your code more readable and maintainable. Using the example above, what is easier to read and understand if you are skimming through code copyBytes() or a for loop iterating through an array moving data one index at a time? Abstraction can provide a sort a self documentation that in my opinion makes the code easier to work with.

As my own rule of thumb, if I can come up with a good function name that describes exactly what I intend a piece of code to do then I write a function for it regardless of whether or not I think I will use it again.

Pemdas
  • 5,395
  • 3
  • 23
  • 41
4

A good general rule for this kind of thing is Zero, One, Infinity. That is, if you need what you're writing more than once, you can assume you'll need it even more times and generalize. This rule implies that you don't bother with abstraction the first time you write something.

Another good reason for this rule is that the first time you write the code, you won't necessarily know what to abstract because you only have one example. Murphy's Law implies that if you write abstract code the first time, the second example will have differences that you didn't anticipate.

2

Eric Lippert points out three things that I think apply in his article future proofing a design. I think if you follow it you will be in good shape.

First: Premature generality is expensive.

Second: Represent in your model only those things which are always in the problem domain and whose class relationships are unchanging.

Third: Keep your policies away from your mechanisms.

Glorfindel
  • 3,167
Conrad Frix
  • 4,165
  • 2
  • 20
  • 34
1

It depends on why you are coding, the purpose of your project. If the value of your code is that is solves a concrete problem, then your want to get that done and move on to the next problem. If there are quick and easy things that you can do to make things easier for future users of the code (including yourself) then by all means, make reasonable accommodations.

On the other hand, there are cases where the code you are writing serves a more generic purpose. For example, when you are writing a library for other programmers, who will be using it in a wide variety of applications. The potential users are unknown and you cannot ask them exactly what they want. But you want to make your library broadly useful. In such cases I'd spend more time trying to support the general solution.

Rob Weir
  • 756
0

I'm a big fan of the K.I.S.S. principle.

I focus on providing what I'm asked to do, and not on what is the best solution. I had to let go of perfectionism (and O.C.D.), because it made me miserable.

Pablo
  • 827
-1

where Abstraction is on your right shoulder and Solve-it-stupid sits on the left.

I don't agree with "solve it stupid", I thinks it can be more "solve it smart".

What's smarter:

  • Writing a complex generalised solution that has could support multiple cases
  • Writing short and effecient code that solves the problem at hand and is easy to maintain, and which can be extended in the future IF it is required.

The default choice should be the second one. Unless you can demonstrate a need for generational.

The generalised solution should only be when you known that it is something that will be used in multiple different projects/cases.

Usually I find generalised solutions are best served as "Core Library Code"

Darknight
  • 12,159