I just thought of this, as I sat contemplating an issue I’m having at work. The issue, without getting too much into detail, is about the best practices for designing a certain pluggable component of a larger framework. As with much of the OO C-descendant world, the component is defined by an interface that someone implements, and the question has arisen, “How do we tell somebody to write one of these?”
The problem is, really, a few things. These apply in a very general sense, so you’ll have to forgive my letting the details escape.
First off, the interface is really abstracted. It’s one of these things where you, say, think of all the many ways you would want this piece to supply information to the rest of the world. Then, you make generalizations on top of that. Then some more, and more, and finally you are left with one method.
Now, I’m not saying that boiling the functionality of a piece of, well, whatever, down to a function is an entirely horrible thing. I’m sure there are many cases in which that would be useful and all, but, in this case, the manner in which the piece responds to and supplies information to the rest of the world really cannot be abstracted to “well, it does something. yeah.”
The problem we are having is that there is a certain way we want people to write components, or at least we think there is a certain way it should happen. By should, I mean the sensible default, as in you really could do it in a much different way, but we think this way is the best combination of efficiency/simplicity/asynchronicity/etc. The problem is that, in reference to the interface, it is entirely non-obvious.
I’m pretty sure that in Joel Spolksy’s book on UI design, he describes how things should be obvious to use, in a couple of ways. In terms of interfaces, they should look like people expect, and the paradigms should make their use obvious. This is really sensible design in the most general case of designing, whatever that is. I do remember an example of a camera that he gives, explaining that a certain camera was constructed in such a way that it begged for your left thumb to be placed just like this, which had the wonderful side-effect of keeping your left hand out of the way of the picture (I wish Canon had been thinking of this when they designed my PowerShot A530).
This type of advice applies to designing interfaces as well. When I look at an interface, it should be immediately obvious just what the intended use case is. Specifically in the case of computer science, it should be a combination of how this piece interacts with the world (and how I would interact with it), and also how I would write this piece. In our case, this is where I am struggling with the best-practices stuff.
You see, there should be no such thing as a best-practices document. Really. I mean it.
Take this example: most linked list implementations, the most obvious for me (at this late hour) being C#’s LinkedList, don’t have an index operator.
But why? It’s not like it is impossible to find element i. It isn’t horrible, and it can be computed. So why not?
The problem here is related to the idea of IEnumerable or Iterable or whatever-you-call-it and for-each type loops: in most cases, people just want to go through a collection from beginning to end. In fact, most people use a list or array or some other type data structure when really they don’t need random access. You may not realize it, but you can end up paying a lot for that when you don’t need it.
So why not provide random access for linked lists? Because people would use it, that’s why. And because it doesn’t have the same semantic meaning as it does for an array-type list.
If C#’s LinkedList had an operator[], I guarantee that somebody would put it in a for-loop. And why would they think otherwise? Why not? It looks and acts like the same thing on a list? Should I not use that one?
And so we create a best-practices document, that says, “Well, this method is really bad, so you shouldn’t use it. I mean, it was convenient for us at this juncture in time, but you should really steer clear.”
But you know what says “don’t use this” better than any document?
Not including it at all.
And that’s the message here. It should be obvious, just by looking at a thing, how you should use it. When I look at LinkedList, I see that it is enumerable and I see that I cannot get random access. I know that I could write it myself, but I see how intolerably slow that would end up being. If I need random access, I find a different data structure to provide that.
The point is that each and every interface you write as a programmer or as a designer should be meticulously created to be, more than anything else, obvious. If you add extra methods on there for convenience of a special case in a special time, you lost the right to tell anybody else not to use them. In fact, you never, never, never have the right to create an interface but then say, “well, but don’t use this part. Please.” “Deprecated” isn’t a magical word that gets you out of the doghouse when you failed design school.
I mean, we all do make mistakes. I can think of things I’ve written in haste, for my own use , more rationalizations, etc., that aren’t well designed. But it doesn’t matter, right? Nobody else is gonna see it or use it or whatever.
You can’t always guarantee that, though, especially if you are writing framework code that will be used by millions. You don’t have the luxury of “I’ll just fix it later”, and nor should you. The second corollary here is that you should always take the time to do it right the first time, just as you should always take the time to do it the first time.
Code isn’t something you shit onto a computer. It is something you create, like a craftsman creates a beautiful piece of furniture, or a dreamer creates an articulate piece of poetry. You shouldn’t treat it like anything else.
I don’t know where this came from, but if you come up with a good attribution, please, let me know:
I once had a chance to speak to a blacksmith as he carried on his daily work. A horseshoe here, some other, sharper acoutraments there. At one point during the day, I watched as he slaved over a small, curved piece of metal, intended to be placed on the undercarriage of a wagon.
I said to him, “Why do you take so much time on a piece the goes underneath? Does it matter if it isn’t exactly straight, exactly flat, pristinely made? Nobody will ever see it!”
He smiled sadly towards me, saying only, “But I will know it is there.”