Since you used the word "delegation", I thought I'd mention the specific sense in which that word is used in Objective-C (and now in Swift).
So in Objective-C (which is actually more "Smalltalk" than "Objective"), inheritance, while possible, is not the recommended way to build extensible objects. Instead the idea is for an object to "delegate" some decisions to another, unrelated object. There is no subclassing relationship, the word "delegate" here is used in quite the literal dictionary sense.
Too many this and thats, so let me take an example.
Suppose you have a list of elements you want to display on the screen. iOS provides a framework class called UITableView for this purpose. As the name suggests, it is a "Table" that displays a list of elements.
Now, suppose you want to display a list of recent Hacker News posts. In many other mainstream languages, at this point the standard way to reuse the framework class would be to create our own subclass and override a few methods.
So for example, we could have a
HackerNewsTableView: UITableView {
override item(index i: Int) -> TableViewCell {
// return the row to display the post at index
...
}
Instead, the standard idiom in iOS programming is to have a separate "delegate" object implement the item method. i.e. We don't override the UITableView, but instead becomes it's delegate. Then when the table needs needs the row at some index i, it asks it'll ask us.
This is a small and fairly straightforward thing, but it fundamentally results in a very different sort of programming style (which I personally think is under-appreciated outside of the iOS programming world, and maybe even within it).
At no point does the original object know about who is its delegate, what type or class hierarchy it belongs to. In fact, it is common for a single "controller" object to be the delegate of many objects.
And the core concept has nothing to do with iOS or Objective-C really. It can be done in any language that supports interfaces (they're called protocols in Objective-C, though in the general programming world the more common term is an interface, e.g. a Java interface).
When you write a class, say Foo, you also write an associated interface, say FooDelegate. Then whoever wants to extend Foo can just implement the methods in FooDelegate, and at runtime set itself as the delegate of an instance of Foo. When foo needs something, it'll ask its delegate.
This might seem backward/more work to you at first glance, but in practice it ends up being much simpler. And conceptually it is much closer to the Smalltalk philosophy, of there just being objects talking to each other.
Foo ==> hey FooDelegate, do this for me ==> FooDelegate
Apologies in case you already know this. But I felt that in your article, you focused on two ways of doing delegation: inheritance and composition. But there is a third way, "delegation" itself. It seems too simple, but it works, it is the core organizing principle of iOS apps (or at least used to be, that's changing with the new SwiftUI framework).
I don't have anything substantial to say for this blog post, sorry, except maybe a generic "scratch your own itch" and "continue scratching them until you find one that other people too have" sort of a thing. I guess you already know that, and you're looking for or thinking aloud about more specific advice.
But anyways, after reading this post I clicked around and read your other post. I found that useful, so wanted to say thanks! I especially found it useful how you separated market-fit from founder-fit, I'd not quite cleanly separated them that way before.
Also, while I'm sure it was very frustrating, but I wouldn't count it as failure: if there was a straight path to "success" (whatever that means to someone), then everyone would be taking it. Keep building, and best of luck!
Hello there! Nice blog. One small suggestion: I didn't quite like the picture of the screaming man you used it the post - it was, um, a bit jarring and distracted from the content.
Interesting! For those of who might skip reading the source -- The authors ran various code quality tools on the source of the code quality tools themselves. The results were not great.
> These tools display a number of code smells despite the fact that their main goal is to help developers get rid of the extra effort needed to find code smells. Therefore, these implements could be perceived as having questionable quality, and as a result, inadequate trustworthiness.
>
> Nevertheless, all of the detection tools did have an accuracy-rate of at least 50%, with three out of the total seven tools exhibiting an accuracy-rate as high as 70% according to our tests.
I have the same anecdotal experience. It'll be interesting if someone can point to a concrete study about it, though if I had to guess this is less about software engineering and more about human psychology.
These are some factors I can think of:
Rhythm: In one project that I joined towards the tail end, just before release, things were looking quite bad. The bulk of the work was done, but there were too many open threads left and the launch date kept getting pushed back, and back. One thing that we as a team decided that I think really helped was bind ourselves to a fixed bi-weekly release cycle. Product or management didn't ask us, we just decided it ourselves. Twice a week, at fixed times, the release train would leave. The overall effect was something akin to how someone who is hyperventilating can get back to a more relaxed state by breathing to a rhythm.
Statelessness: Every release is provides a mental closure. Sure, there are TODOs, and things that you realize over sleep that are semi-buggy or could be done better. But there is something out there. I'd die more peacefully if I were to get hit by a bus after making a release than in the midst of a cycle :)
Iteration speed: This is perhaps the reason specifically pertaining to programming. We're more productive with shorter feedback cycles. Let us think about an environment where doing a code change, making a build, and then seeing the effect of that change takes, let us say, 2 minutes. A different environment where this cycle is 2 seconds is a whole different ballgame. It is just much more productive. This same principle applies to shipping, just on a higher level.
Fear. Or lack of it. Bulky releases come with fear. Software is hard, there are just too many things that can interact to cause unanticipated effects. The shorter the release cycle, the less the fear of the unexpected stuff happening. And when unexpected stuff does happen, it is easier to zero in on the cause.
Of course, there are downsides too. "Release fatigue" could be one. Diluting the importance of the release could be another - a sentence would look pretty tedious if every. space. was. replaced. by. a. fullstop. I personally try to do it as often and rhythmically as I can get away with.
I don't know why people are downvoting you. I think you're right. I agree with his general point (I expanded a bit on that here [1]), and I also agree that people often overlook that general point since it is so convenient to do so. But otherwise, the specific examples he wrote (and the other assertions he made) seem to me to be incorrect.
But, as you said, he is a good story teller. Like Taleb. Getting the moral across is more important, even if sometimes the wolf eats little red riding hood in malformed renderings.
So in Objective-C (which is actually more "Smalltalk" than "Objective"), inheritance, while possible, is not the recommended way to build extensible objects. Instead the idea is for an object to "delegate" some decisions to another, unrelated object. There is no subclassing relationship, the word "delegate" here is used in quite the literal dictionary sense.
Too many this and thats, so let me take an example.
Suppose you have a list of elements you want to display on the screen. iOS provides a framework class called UITableView for this purpose. As the name suggests, it is a "Table" that displays a list of elements.
Now, suppose you want to display a list of recent Hacker News posts. In many other mainstream languages, at this point the standard way to reuse the framework class would be to create our own subclass and override a few methods.
So for example, we could have a
Instead, the standard idiom in iOS programming is to have a separate "delegate" object implement the item method. i.e. We don't override the UITableView, but instead becomes it's delegate. Then when the table needs needs the row at some index i, it asks it'll ask us.
This is a small and fairly straightforward thing, but it fundamentally results in a very different sort of programming style (which I personally think is under-appreciated outside of the iOS programming world, and maybe even within it).
At no point does the original object know about who is its delegate, what type or class hierarchy it belongs to. In fact, it is common for a single "controller" object to be the delegate of many objects.
And the core concept has nothing to do with iOS or Objective-C really. It can be done in any language that supports interfaces (they're called protocols in Objective-C, though in the general programming world the more common term is an interface, e.g. a Java interface).
When you write a class, say Foo, you also write an associated interface, say FooDelegate. Then whoever wants to extend Foo can just implement the methods in FooDelegate, and at runtime set itself as the delegate of an instance of Foo. When foo needs something, it'll ask its delegate.
This might seem backward/more work to you at first glance, but in practice it ends up being much simpler. And conceptually it is much closer to the Smalltalk philosophy, of there just being objects talking to each other.
Apologies in case you already know this. But I felt that in your article, you focused on two ways of doing delegation: inheritance and composition. But there is a third way, "delegation" itself. It seems too simple, but it works, it is the core organizing principle of iOS apps (or at least used to be, that's changing with the new SwiftUI framework).