Archive for August 2011
Lots of other people have already covered Steve Jobs’s resignation from Apple in tasteful, circumspect ways.
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.
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.
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:
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:
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!