Christ, What an Asshole

Lots of other people have already covered Steve Jobs’s resignation from Apple in tasteful, circumspect ways.

Really, go read them.

My contribution? Well….

There was a joke going around for a while about how every New Yorker cartoon was funnier if its caption was replaced with “Christ, What an Asshole.”

We all know it: Steve Jobs is a jerk.

I mean that in a lot of ways, but the most important is: he has strong opinions and he doesn’t care what you think.

He doesn’t care what I think. He doesn’t care about stepping on toes. He doesn’t care about making friends. With him at the helm, Apple has ruthlessly exploited its partners, its third-party developers, the media, and anyone else it has leverage over. The only reason it hasn’t attacked its rivals more is because it doesn’t care what they think, either. You either live with it or you don’t.

As an Apple employee, I was delighted to have this awesome weapon pointed outward. It was less pleasant to realize I was surrounded by it. From what I saw, Apple has spent years busily discovering, hiring, and fast-tracking the most razor-sharp, take-no-prisoners assholes it could find throughout its ranks. John Gruber phrases it as, “The company is a fractal design.” I would say: it’s assholes all the way down.

That’s why I’m not worried about my former employer.

Putting It to the Test

I’ve been thinking about automated tests recently, and I’ve come up with a number of observations based on my experience.

I’ve rarely if ever seen unit tests break.
In theory, the tests exist so that when you add functionality to a class or a group of classes in a way that changes the code paths of existing functionality, you’re sure the existing functionality still works. In practice, I’ve seen that either those classes are never touched again, or they’re only added to, not modified, or the modifications are simple enough that they just don’t lead to breakage.

Rewrites don’t bring the tests along.
In my experience, when you’re doing a rewrite of your GUI application code, for, let’s say, a new major release, you’re generally changing the logic in the new code enough that the automated tests don’t come along. So any exhaustive, pre-existing tests you have need to be thrown out. For example, if you’re moving your model to Core Data for version 2 of your app, much of your existing model classes tests are now outdated.

I’ve only had one case where a rewrite kept the same logic, and that was for a recent side project that was more of an example than anything else. And in that case, I wrote the exhaustive tests right before doing the rewrite. That worked really nicely, but, if anything, it’s an argument for putting off writing such exhaustive tests until you know you need them for something specific.

Regression tests tend to run into sorting issues.
I’ve written extensive regression tests for some projects, and I’ve tended to run into problems with sorting. NSArray’s sorting APIs are unstable. So I’ve often had to write extra logic into the production code to provide reproducibility that it doesn’t actually need, for the sake of testing.

Whenever I talk to any colleagues about automated testing, there’s always a wistful quality to it; yes, we don’t have automated tests or we only have a few, but we should be testing everything. We’ll get to it when we have time. You’re not a real developer unless you’ve done it or at least feel inadequate about it.

But in my, possibly limited experience (I did, after all, work on the same app for almost 6 years), I just don’t see them as the panacea that others do.

A Bridge Too Far

I was looking through my Gang of Four design patterns book recently, a book I last cracked open many years ago. Maybe it’s the experience I’ve had since then, but one pattern jumped out at me in a way that I don’t remember happening before: the Bridge pattern.

And not in a good way.

The pattern purportedly heads off this kind of class hierarchy, where the abstract public classes are in blue, and the concrete classes are in black:

Regular Abstraction Layer

The idea above being that for every public, abstract class, like Window and its subclass CircleWindow, you’ll need a full array of concrete classes hanging off of it, like OSXWindow and OSXCircleWindow. That can lead to a proliferation of classes, even in the two-level deep hierarchy shown above.

The Bridge pattern makes it unnecessary to have any more than a single array of concrete classes:

Body Abstraction Layer

But it does this by making a very brittle assumption: the only concrete method implementations that will ever be needed anywhere in the system can be put into that base implementation class. (Here, WindowImp.)

The example given in the book is drawing. The concrete classes implement DrawText() and DrawLine(), and everything else can be derived from that—geometric shapes, icon borders, etc.

I have no faith in that. There isn’t a single complex architecture I’ve worked on—and why would you use this for very simple architectures?—where you can comfortably put everything you need in a base class like that. You will always need weird, subclass-specific logic, and tossing such things into a base class leads to confusion, lack of proper separation, and maintenance headaches.

“Oh,” you say, “well then, make an Imp class hierarchy and….” Dammit, multiple hierarchies are what this pattern was supposed to solve!

10 Unanswered Questions About the New Yahoo Mail

Recently, I got an email from Yahoo saying that I would be required to upgrade to the new Yahoo Mail in about a month.

This led me to a series of questions, which, alas, are not answered on the Overview or Mail Help pages I was directed to.

1. Why do I have to agree to the upgrade? If Yahoo can upgrade its mail to be better, why not just do it without requiring me to do anything? Wouldn’t that be better?

OK, so that’s three questions. And actually, there is an answer here. It says, “Due to the new and exciting features within Yahoo! Mail”. But…which features? As I’ll be mentioning below, many of the “features” you talk about are actually just improvements to the service, not features per se, which you shouldn’t need my permission to roll out. So: why? And will I get a straight answer to this question anywhere on your website?

2. Why does your Overview page prominently feature useless Flash videos that don’t tell me anything? Aren’t you trying a little too hard here?

3. Under the “Awesome Features” tab of the Overview page, you mention faster email, better spam protection, and unlimited storage. All for the good, but why did I have to wait until now for these improvements? And as part of this mysterious “upgrade”? I’ve had an email account with you for 10 years, for crying out loud. Is this the first time you’ve made any of these things better? Or are you really just throwing those in to sweeten the “upgrade” I haven’t agreed to yet?

4. Why does the new web email interface, which you tout heavily on the Overview page, look almost as ugly as the current interface? (Now with more purple!) Why do your video tutorials take great pains to crop out the areas of the interface where ads normally reside? Even if the new interface were bee-yootiful, if it had the vast number of ugly ads that festoon the old interface, it would be an unpleasant experience regardless.

5. If these new web abilities of your email are so great, why can’t you get the HTML of your own Overview page right? Here’s how it acts in Safari 5.0.5 if you make the window too narrow:

Before:
Space below selected 'Faster Email' tab has purple background and promotional contents.
After:
Space below selected 'Faster Email' tab is just blank and gray, with just the edge of a purple box all the way to the right.

And here’s a bit more tomfoolery I found:
White promotional text on top of other white promotional text, a big mess, on a purple background. The only clear text is at the end, which says, 'Everybody's happy.'

6. You mention a new Mobile interface, but never quite get around to mentioning on the Overview page that this is a web interface, not a native interface. What’s going to happen with the native iOS Yahoo mail? Anything? If not, can’t I just keep using that rather than agreeing to this “upgrade”? You mention waaaaay at the bottom of your “Emailing: The Basics” help page that, “If you upgrade to Yahoo Mail Beta, your experience using Yahoo Mail on your mobile device will not be affected”. But (a) that doesn’t answer my question, and (b) why the heck is a question like that on a “The Basics” page which is mostly about the nuts and bolts of using the email interface? Are you maybe trying to hide it?

7. Why does your help page say, “If you aren’t ready to try it out, no problem!” but the email I received say that I only have a month before I have to upgrade?

8. When I was clicking around trying to find information about iOS, I found your page about what operating systems you support, last updated in October 2010. You know what it says? “Mac OSX “Tiger” and “Leopard” (10.4 & 10.5)”. For a service called “New Yahoo Mail”, wouldn’t you have expected someone to go through all the support pages and update them with the most up-to-date information? Doesn’t this say something about your attention to detail? And even if nobody did that, wasn’t Mac OS X 10.6 “Snow Leopard” actually released a year earlier than the last time this page was updated? Or are you actually saying you don’t officially support Snow Leopard? (Let alone Mac OS X 10.7 “Lion”!)

9. Hey, what’s all this in your Mail Beta FAQ about “relevant ads”? Well, OK, you do answer that question in the FAQ itself; it sounds like one of the major things about the new Yahoo Mail is that it scans your email content, like Google, in order to show you ads that are, yes, relevant. That’s really one of the “features”, isn’t it? Part of the “new and exciting features” you mention above? But somehow can only think to mention at all, let alone in any detail, in your text-only, sans-exciting-videos-or-graphics FAQ? That you probably hope will be tl;dr for most people? Hey, at least for now, you let people opt out of it. That’s something.

10. Why do I still use Yahoo Mail?

Backwater

In John Siracusa’s and Dan Benjamin’s now-old podcast episode “The Bridges of Siracusa County”, Siracusa talks about how Perl has been an incubator of language innovation, not despite being a backwater, but because of it. Since it wasn’t straightjacketed by the requirements of an important platform, its users could experiment freely.

Let me tell you a bit about what I saw at Apple.

There were at least three places I knew of—in areas you might not expect—where being a relative backwater in the grand scheme of Apple’s priorities helped make a project both better and more enjoyable for the engineers involved.

Let’s tackle the first one: because the projects weren’t anyone’s priority, they didn’t have to be flashy or aligned with unrelated interests or subject to political whims. The engineers could, y’know, just do what they saw fit. Now, I’ve drunk enough Apple Kool-Aid to believe the engineers shouldn’t solely be in charge of a project. There still need to be real designers involved, among others. But the engineers who work day in and day out on a project are often the ones with the best insight on the important bugs, the little annoyances, the things that the actual users want. And given their freedom, they can fix and provide those things.

(The lower-tier engineers are generally not the ones to go to for sweeping, breathtaking new directions. And since Apple’s management skews towards that, you can see why they don’t listen to those engineers very much.)

And the second: because the engineers were allowed to exercise a little judgment and do their own thing, they had more fun. Important Apple projects can be extended death marches, where you both work insanely hard and really aren’t given much creative freedom. Maybe that’s the only way to get the kind of quality Apple provides consistently.

But, y’know, what fun is that?

Harsh Language

Apple’s in danger! Because they don’t have a modern programming language and runtime, unlike their competitors. And even if they introduced one tomorrow, getting their developers to switch over would be difficult and take many years, putting them even further behind.

That’s John Siracusa’s thesis (paraphrased) in his latest Hypercritical podcast. He mentions Microsoft’s C# and .NET transition, a decade long and still with a significant amount of non-adoption.

I don’t know about that. But it occurred to me that Apple’s transition, when and if it happens, will be far swifter, due to the App Stores.

Microsoft has to convince its developers to switch. With rational arguments! On iOS, at least, Apple can simply require it as a prerequisite of approval. Apple’s unequal treatment of their developers has led to a situation where none of the aspects of such a demand would even be new.

Onerous, sometimes arbitrary approval requirements? Check. Retroactive new requirements that, if not met, will get you yanked from the store after a certain date? Check. Little expectation of influence or bargaining power on the part of its applicants? Check check check!

It amazes me how well-positioned Apple is to force such a transition. I imagine they could go from language/runtime initial introduction to required status for iOS App Store inclusion in less than, say, two years. As long as the iOS App Store remains lucrative, developers have both the financial incentive and the mental conditioning to go along with it.

And does anyone doubt that the Mac App Store will have a similar stranglehold, in effect, on app purchases within 1-2 OS revisions?

DeleGate

One of the first patterns you encounter when you’re learning Cocoa is the way Apple’s view classes extend their functionality with delegates.

If Apple does it with their view classes, it must be a great idea for your view classes too, right?

The answer you’re looking for here is “no”.

Let’s play a game of 20 2 questions, shall we?

1. Is your view class (a) a general-purpose class, intended to be used by future clients in ways you can’t anticipate? Or is it (b) a very specific class used in a very specific, single way?

2. Do you have (a) two or more classes serving as delegates for your view? Or is the delegate (b) always the same class, used in exactly the same way, to the point where you’re not even bothering with respondsToSelector: checks?

If the answers to both questions are (b), then you know what using the delegate pattern buys you for connecting your view class to its “delegate”? That’s right, nothing. And you know what it causes for the people who come in later to maintain your class? A big headache, right again!

For one, they have to spend the time investigating what classes actually serve as the delegate of your view class. They may need to run the app, get it into a variety of hard-to-reproduce states, just to be sure. Certainly, this might not be that big of a deal for one class. But in a large codebase where this “pattern” has been used repeatedly, it can bog things down.

For two, it means they can’t entirely rely on the automated refactoring tools when they want to make changes to the delegate classes. Because delegates are of type id, right? That’s the pattern. So that bogs things down, too.

Let me spell it out: if you have a view subclass (or any class, really) that needs functionality from an associated class, add a typed reference to that associated class. Don’t add an untyped “delegate” reference.

By all that is holy (donuts, CDs, bowling balls, etc.), don’t create your own DeleGate scandal.

And stay tuned for my next post, where I rant for twenty pages about bindings misuse.

Hob-Knobbing with Interface Builder

It’s pretty tempting to take a good Cocoa tool—say, Cocoa Bindings—and proceed to use it everywhere, regardless of whether it’s really the best way to solve your problem.

My recent example of this involved Interface Builder.

I wanted to add custom guides to a resizable view, like in this not-for-real example:

Blue square with round green "knobs" on all four corners and at the mid-point of all four borders

Interface Builder is a good Cocoa tool, right? So I used it to set up the little green “knobs”.

Then, I wanted to fiddle with their size.

Hm.

I could, in the above example, manually go in and change the size and location of 8 views in my nib. A disheartening prospect, but Interface Builder is a good tool, right?

But you have to remember why it’s a good tool.

There isn’t really any difference in difficulty between changing a window’s size in Interface Builder and changing it in code. In both cases, you make a change, save, compile, and run.

But Interface Builder is better because you get a mini-preview of the change right there. You can fiddle with it a lot, get it just right, without any compile-run cycle at all. Same with changing a color, or rearranging subviews.

That’s why it’s a good tool. It makes it easy to keep fiddling with the details until you get them just right. If it doesn’t help you with that, then don’t use it.

Once I got that message through my head, I went back to my source code files and wrote an implementation that created and placed the knob views programmatically, based on a single value: knob diameter. (Make your own joke here.) Change that value, compile and run, and see the results instantly. While it cost me an hour or two to make it work like that, after that, it only took me a few minutes of fiddling to come up with a size I liked. And, more importantly, I can go back later and change it again with the same ease. Say, after I’ve run it past a real designer.

Sorry, Interface Builder.

More Than Token Problems

One of my favorite typos from when I worked at Apple was “NSTokenRage”. Rage…over tokens? I don’t know what that was about, but I’ve certainly had my share of puzzlement, frustration, and yes, rage, over NSTokenField, and I have the bugs to prove it.

So let’s dish.

I’ve found three big problems with it.

Tabbing Out

First, tabbing out doesn’t invoke the same token validation behavior as typing in a tokenizing character within the field, such as a comma or a return.

In the latter case, the delegate’s tokenField:shouldAddObjects:atIndex: method is called, and so the delegate can choose whether to tokenize the user’s input or not. In the former case, however, it is not called, nor is any other standard delegate method. Tokenization of the user’s input just…happens.

Here are screenshots from the sample app I submitted with the bug. In the token field, duplicate tokens are not allowed, and if you type in duplicate text, it should disappear instead of being tokenized. Here’s an example, where I’m about to enter the duplicate text “b”:

If I then type return, focus stays in the token field, and the duplicate token is rejected and disappears:

But if I type tab, which changes focus to the text field below the token field, the duplicate token is erroneously created:

The sample app also includes a workaround.

Each token does a round trip from the string the user initially types, to a “represented object” for that string once that string becomes a token, to a display string for the represented object used when drawing the token.

The string → represented object data is provided by the delegate method tokenField:representedObjectForEditingString:, and the represented object → display string data is provided by the delegate method tokenField:displayStringForRepresentedObject:. The workaround is for the first method to return a represented object indicating that the string is invalid (I used NSNull), and for the second method to return nil for the display string for such an invalid object. While this doesn’t keep the invalid represented object from being added to the list, it does prevent it from being displayed.

The downside of this approach is that we get entries like these in Console for the application:

2011-03-13 17:20:47.668 Token Workaround[2819:903] : Failed to query display string for a represented object . Ignoring…

The bug for this issue is rdar://9000060.

Unheralded Delete

A related problem is that there is no delegate method that is called when the user deletes a token, no tokenField:willDeleteObjects:.

Without this, it becomes harder to enforce a no-duplicates rule. The workaround I used was to implement the NSControl delegate method controlTextDidChange:. It’s a lower-level access point, but at least it’s guaranteed to catch all editing changes.

The bug for this issue is rdar://9000191.

Keystroke Validation

The last major issue I saw was an inability to do keystroke validation by the standard Cocoa technique of subclassing NSFormatter.

Apple’s docs list three methods you need to override in a subclass:

  • stringForObjectValue:
  • getObjectValue:forString:errorDescription:
  • attributedStringForObjectValue:withDefaultAttributes:

Only the last actually has to do with keystroke validation. The first two do the same sort of round-tripping from display string ↔ represented object that we saw in NSTokenField itself.

The trouble is, NSTokenField has its own internal attributed string format that is impossible (or seems like a bad idea to me, anyway) to try to replicate. It would be nice if there were a way to just implement the third method, and tell the formatter that, hey, the existing formatting is just fine, thanks. But as far as I know, there isn’t.

I got around this myself by implementing a custom field editor and overriding a private method that is called to actually set the text. I’d much rather have a better way.

The bug for this issue is rdar://9092238.

There’s also the more minor issue that it seems to be impossible to set the right arrow key as a tokenizing character (rdar://8849747).

And that’s all I have to say about NSTokenRageField.