34

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?

3
  • 1
    "As abstract and as general" is generally known as the programmer's hubris :) However, because of Murphy's law, the one degree of adaptation you'll need when working on the next task will be one you haven't provided. Commented Jan 4, 2011 at 7:45
  • 1
    One of the best questions ever !
    – explorest
    Commented Jan 28, 2011 at 20:29
  • 1
    You always guess wrong so seep it simple. When you have expanded the simple cases "enough times" you start to see a pattern. Until then, don't.
    – user1249
    Commented Mar 8, 2012 at 14:18

9 Answers 9

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.

6
  • 1
    "Never abstract until you must." - I take it you avoid the use of classes, methods, loops, or if statements? These things are all abstractions. If you think about it, code written in high-level languages is very abstract whether you like it or not. The question isn't whether to abstract. It's which abstractions to use. Commented Dec 31, 2010 at 23:03
  • 10
    @Jason Baker: "I take it you avoid the use of classes, methods, loops, or if statements". That does not seem to be what the question was about. Why make the absurd claim that all programming is impossible if we avoid over-abstracting our designs?
    – S.Lott
    Commented Dec 31, 2010 at 23:21
  • 1
    I'm reminded of the old joke where a man asks a woman if she'll sleep with him for a million dollars. When she says yes, he asks if she'll sleep with him for five dollars. When she says "What kind of woman do you take me for?" the response is "Well, we've already established that you'll sleep with someone for sex. Now we're just haggling over the price." My point is that it's never about a decision to abstract or not. It's about how much to abstract and what abstractions to choose. You can't choose to hold off on abstraction unless you're writing pure assembly. Commented Jan 1, 2011 at 3:26
  • 9
    @Jason Baker: "You can't choose to hold off on abstraction unless you're writing pure assembly" That's trivially false. Assembly is an abstraction over machine language. Which is an abstraction over the hardware circuitry. Please stop reading too much into the answer that wasn't present in question in the first place. The question wasn't about "Abstraction" as a concept or an intellectual tool. It was about abstraction as an OO design technique -- misapplied -- too often. My answer was not that abstraction is impossible. But that "over abstraction" is bad.
    – S.Lott
    Commented Jan 1, 2011 at 5:32
  • 1
    @JasonBaker it is not an unheard reason for sleeping with somebody. Perhaps you were thinking of "money"?
    – user1249
    Commented Mar 8, 2012 at 14:19
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.

3
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.

5
  • 2
    +1 for useful principles, -1 for making it all about money. Commented Jan 3, 2011 at 17:12
  • 4
    @Mason: It's not about money, it's about effort. Money is a practical measure of effort. Efficiency is benefits accrued per effort produced; again, profit in money is a practical measure of benefits accrued. It is simply easier to discuss the abstraction of money than to come up with some way to measure effort, cost and benefit. Would you prefer to measure effort, cost and benefit in some other way? Feel free to make a proposal of a better way. Commented Jan 3, 2011 at 19:28
  • 2
    I agree with @Mason Wheeler's point. I find the solution here is to not involve stakeholders in that level of a project. It obviously depends heavily on the industry, but clients do not usually see the code. Also, all these answers seem to be anti-effort, seems lazy.
    – Orbling
    Commented Jan 4, 2011 at 0:27
  • 2
    @Orbling: I am strongly opposed to unnecessary, expensive, wasteful effort that takes resources away from worthy and important projects. If you are suggesting that this makes me "lazy" then I submit that you are using the word "lazy" in an unusual way. Commented Jan 4, 2011 at 3:06
  • 1
    In my experience, other projects do not tend to go away, so it is usually worth investing as much time as needed in the current project before moving on.
    – Orbling
    Commented Jan 4, 2011 at 5:01
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.

1
  • +1 for making the difference between abstraction and generalization.
    – Joris Meys
    Commented Jan 1, 2011 at 12:25
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.

5
  • 1
    That sounds a lot like my version of the Refactoring Rule: strike two! refactor! Commented Dec 31, 2010 at 22:26
  • @Frank: It probably is your Refactoring Rule, but with the second paragraph added from personal experience. Commented Dec 31, 2010 at 22:29
  • +1: I believe this is also noted fairly early on in The Pragmatic Programmer almost verbatim IIRC. Commented Dec 31, 2010 at 22:34
  • oh definitely. There's nothing to abstract with only one example: as soon as you have a second example, you can see what is common (shared) and what not, and you can abstract away! Commented Dec 31, 2010 at 22:55
  • A sample of two is in my opinion too small to generalize for infinity. In other words, much much too early.
    – user1249
    Commented Mar 8, 2012 at 14:24
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.

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.

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.

2
  • Why the dislike of perfectionism? (I did not vote you down by the way)
    – Orbling
    Commented Jan 4, 2011 at 0:29
  • Not aiming for perfection works for me. Why? Because I know my programs will never be perfect. There is no standard definition of perfection, so I just choose to provide something that works well.
    – Pablo
    Commented Jan 6, 2011 at 19:54
-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"

3
  • If you could make all the assumptions here, you wouldn't have a problem. Short and easy to maintain are mutually exclusive. If you know something will be reused, the general solution would address that.
    – JeffO
    Commented Jan 3, 2011 at 22:16
  • @Jeff I'm not quite sure I understand your comment..
    – Darknight
    Commented Jan 3, 2011 at 22:39
  • You took the "Solve it Stupid" out of context.
    – Nayrb
    Commented Jan 3, 2011 at 23:19

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