What’s a software architect?

After a discussion on the twitters with Kellabyte and Iris Classon about software architects, I thought I’d summarise my position. Feel welcome to disagree.

What does a software architect do?

A software architect is there to identify risks that affect the technical implementation of the software product, and address those risks. Preferably before they stop or impede the development of the product.

That could mean doing tests to investigate the feasibility or attributes of a proposed solution. It could mean evangelising the developers to the clients or managers to avoid those people interrupting the development work. It could mean giving a junior developer a tutorial on a certain technology-or getting that developer to tutor the rest of the team on the thing that person is an expert on.

What a software architect doesn’t do

A software architect doesn’t micromanage the developers who work with them. The architect doesn’t rule by memos and UML diagrams. The architect doesn’t prognisticate on things they have no experience of. Perhaps confusingly, the role of software architect bears very little resemblance to the profession after which it’s named. If you want analogies with civil engineering, all developers are like architects. If you want to see the software analogue to the builder, that’s work done by the compiler and IDE.

Architects don’t make decisions where none is necessary. They don’t ignore or belittle suggestions that come from people who aren’t architects.

In one sentence

A software architect is there to make it easier for developers to develop.

On free apps

This post is sort-of a follow-on to @daveaddey’s post on the average app; although in reality it’s a follow-on to the response that comes out every time a post on app store revenue is written.

Events go like this:

  1. Some statistic about app store revenue.
  2. “Your numbers include free apps. You shouldn’t include free apps.

Yes you should. The revenue that comes from the app store is indeed shared across all apps, free and paid. Free apps contribute significantly to the long-tail price distribution of apps on the store, and the consumer perception that apps shouldn’t cost much. Some of them generate revenue through in-app purchase: revenue that probably is counted in Apple’s “we’ve given $5bn to developers” number. Some of them will generate revenue through iAd: it’s not clear whether that’s included in the $5bn but it’s certainly money paid by Apple to developers. Similarly, it’s not clear whether money paid through Newsstand subscriptions (again, in free apps) counts: it probably does. Some of them have not always and will not always be free; again these apps make money directly from Apple.

A lot of free apps come from companies that do other things, but feel a marketing need to be on the app store:Amazon, O2, facebook and others go down this route. In these cases there is absolutely no money to be made from the app directly, though there are possibly many sorts of collateral benefits. It costs Amazon money to write the Windowshop app, but they bank on users buying more things from them than if the apps doesn’t exist.

On the other hand, some free apps are just written by developers who want to put a free app on the store so that it can act as their portfolio when they try to get work as iOS app developers.

So yes, other business models exist. But when talking about money made from the app store, you have to include all of those products that don’t make money on the app store. Ignoring the odd outlier is fine statistics. Ignoring large quantities of data that make your conclusions look bad is not science; it’s witchcraft.

Inheritance is old and busted

Back when I started reading about Object-Oriented Programming (which was when Java was new, I was using Delphi and maybe the ArcGIS scripting language, which also had OO features) the entire hotness was inheritance. Class hierarchies as complicated as biological taxonomies were diagrammed, implemented and justified. Authors explained that while both a Dog and a Cat could makeNoise(), they’d do it in different ways. They also got their knickers in a knot over whether a Square is-a Rectangle or the other way around. If you believe in successive specialisation then the Square is-a (special kind of) Rectangle. If you believe in the Liskov Substitution Principle then it cannot be. If you don’t care then you’d only define a Rectangle in the first place.

Slightly earlier than this time it was noticed that you sometimes want to separate the idea of “this object does these things” from “this object is something of this type”. C++ programmers were inheriting from purely virtual classes. Objective-C gave us protocols to conform to; Java borrowed this idea and called the protocols interfaces.

Both inheritance and conformance (which is what I’ll call protocol adoption, to avoid the confusion between Objective-C and Java over what it means to “implement” an “interface”) are blunt and unwieldy instruments, however. In practice, I use them in a few specific places:

  • I most often inherit from a “base class”. My Python object is-an object, my Java object is-an (java.lang.)object, my ObjC object is-a NSObject.
  • I sometimes inherit from deeper classes because something requires me to build a specialised version of that class. For example, UIKit requires that my view controllers are specialised versions of UIViewController, and that my views are specialised versions of those views.
  • I usually adopt a protocol or abstract interface in order to implement some design pattern. For example I might adopt a data source or delegate protocol. I might provide an implementation of a strategy or state or visitor.
  • I pretty much avoid even with bargepoles the “marker interfaces” that pervade pre-annotation Java, like Cloneable. I even hate NSCopying as a way of designing a protocol.

It’s time we added specialised syntax on top of inheritance to account for these cases particularly. For a start, any language that doesn’t assume you want its usual version of Object-ness when you create a class without saying anything about it is broken. Yes, I’m looking at you Objective-C. Yes, I know about NSProxy and I’m old enough to remember Object. I still say that not wanting an NSObject is rare enough that you should have to shout about it. I’ve never intentionally created a new Objective-C root class – but that’s the easiest thing the syntax permits.

Think of it like pointers. Yes, you can use pointers in ObjC and C++. Yes, arrays are built using pointers—as are object references. No you don’t need to use pointer syntax to build arrays and object references – we’ve got higher-level syntax. Just like we’ve got higher-level syntax for getting at properties or at subscripted entries in Objective-C.

So yes, you can create a delegate using protocols. But why not have syntax saying “this class’s delegates must do this” and “this is a delegate of that class”? Why not say “this is a special version of that thing”? Why not say “this group of classes implement a state machine?”

Inheritance is old and busted.

On Null Objects

I’ve said before, NSNull is an anti-pattern. It’s nice that we have the nil object, which allows us to have a stand-in for any object that doesn’t do anything. Unfortunately, it’s not a universal stand-in. You can’t add nil to a collection. You can add +[NSNull null] to a collection, but you can’t use that as a placeholder for other objects.

In either of these cases, you end up needing to warty your code. Either when you are building the collection, you must test for and handle trying to add nil. Or when you are reading the collection, you must test for and handle trying to use NSNull.

What would be nice is if we could easily define empty implementations of protocols. These would combine the two null implementations described above, along with more awesomeness:

  • Like nil, they provide default implementations of any method in the protocol.
  • Like NSNull, they can be used in collection classes.
  • Unlike both, they conform to the protocol for which you created them.

Then you can use these instances with aplomb, without your client code needing to work around their existence.

You’d use them like this:

        id <NSCacheDelegate> emptyCacheDelegate = [NullProtocol nullFor: @protocol(NSCacheDelegate)];
        NSArray *delegates = @[emptyCacheDelegate];

The nullFor: method would look to see whether you’ve previously created a Null Object for the specified protocol. Because they all do the same thing, it could use Flyweight instances.

+ (id)nullFor: (Protocol *)protocol
{
    NSString *className = [NSString stringWithFormat: @"Null%s", protocol_getName(protocol)];
    Class NullClass = NSClassFromString(className);
    NullProtocol *nullObject = objc_getAssociatedObject(NullClass, "instance");

If you don’t have one, then the method would create a new class pair, add this protocol to the class and instantiate it.

    if (nullObject == nil)
    {
        NullClass = objc_allocateClassPair(self, [className UTF8String], 0);
        class_addProtocol(NullClass, protocol);
        objc_registerClassPair(NullClass);
        nullObject = [NullClass new];
        nullObject->_protocol = protocol;
        objc_setAssociatedObject(NullClass, "instance", nullObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return nullObject;
}

The object could then use the standard message-forwarding technique to let nil handle all the messages from the protocol.

BOOL isNullMethodDescription(struct objc_method_description description)
{
    return (description.name == NULL && description.types == NULL);
}

- (struct objc_method_description)methodDescriptionForSelector: (SEL)aSelector inProtocol: (Protocol *)protocol
{
    //required methods
    struct objc_method_description description = protocol_getMethodDescription(protocol, aSelector, YES, YES);
    if (isNullMethodDescription(description))
    {
        //optional methods
        description = protocol_getMethodDescription(protocol, aSelector, NO, YES);
    }
    //look in the super-protocols
    if (isNullMethodDescription(description))
    {
        unsigned int protocolCount = 0;
        Protocol * __unsafe_unretained *protocols = protocol_copyProtocolList(protocol, &protocolCount);
        if (protocols == NULL)
        {
            return description;
        }
        unsigned int protocolCursor = 0;
        for (protocolCursor = 0; protocolCursor < protocolCount; protocolCursor++)
        {
            Protocol *thisProtocol = protocols[protocolCursor];
            description = [self methodDescriptionForSelector: aSelector inProtocol: thisProtocol];
            if (!isNullMethodDescription(description)) break;
        }
        free(protocols);
    }
    return description;
}

- (struct objc_method_description)methodDescriptionForSelector: (SEL)aSelector
{
    return [self methodDescriptionForSelector: aSelector inProtocol: _protocol];
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
    struct objc_method_description description = [self methodDescriptionForSelector: aSelector];
    return !(isNullMethodDescription(description));
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *superResult = [super methodSignatureForSelector: aSelector];
    if (superResult) return superResult;
    struct objc_method_description description = [self methodDescriptionForSelector: aSelector];
    return [NSMethodSignature signatureWithObjCTypes: description.types];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    [anInvocation invokeWithTarget: nil];
}

Oh wait, that all exists.

Illuminative-C

In addition to being a mildly accomplished software engineer, I’ve done some studying and armchair research in the field of ancient languages and palaeography. What happens if we smoosh those fields together?

In a very slight way, art historian and fellow Oxenafordisc Dr. Janina Ramirez did that in her series on Illuminations: the Private Lives of Medieval Kings (erm, Kings and Ælfgifu). In the series she showed off many manuscripts in the British Library collection, but when she went out in the field she took an iPad. It turns out that the BL isn’t too hot on letting you run around with their thousand-year-old kidskin.

You already know my opinion on our digital heritage. This puts it into stark relief: in one hundred years’ time, barring some epic fire in London (those never happen), the BL and its collection will still be there. Will it still be possible to even launch the iPad app she was using? I very much doubt it.

How about if we put the same effort into storing our source code as the scriptoria did into storing their indentures and gospels? Well, I sharpened a goose feather and had a go at just that (warning: very much draft document impending).

Example 2-1 from PCAS

What you see up there is the first sample code in Professional Cocoa Application Security – Listing 2-1. Ignore the fact that you don’t recognise all the letter shapes: things have changed over the centuries. There were a few contortions required to get the source code to work in manuscript form: let me show you them.

First is that in the hand in which I wrote the source, some of the characters needed for Objective-C source code don’t exist. Like ‘v’. I used the fact that u and v are actually the same letter to get around that. Punctuation was harder: I went for roughly accurate rendering, with a single misplaced comma to suggest that the scribe didn’t really understand punctuation.

When it came to comments, I decided they have the same meaning as the gloss in the Lindisfarne Gospels – rendering the difficult language required by the church^Wcompiler into plain English. I therefore roughly scratched them in smaller text with different ink, letting them flow around the code as if they’d been written later. I also put in a few Old English spellings – though again not consistently[*].

The return value posed some difficulty, because we didn’t borrow 0 from the Middle East until a few centuries after the time this script is mimicking. I realised that if a scribe were to illuminate any part of a C function, it’d probably be the return value because that’s the consistent and – from the perspective of the rest of the code – important part. Thus the 0 is highly decorated, with six legs in the fashion of a bug :-).

Bugs hark back to the days of illuminated manuscripts anyway. Any good scribe would know that a mistake in the text was the fault of Titivillus, not of the scribe. Just as those bugs aren’t my fault. Honest.

[*] Next time you want to get angry at a teenager, remember that the work “ask” was once “acsian” with the s on the end, and think about which one of you is bastardising our language.

On community

This is a post that had been boiling for a while; I talked a little about the topic when I was in Appsterdam earlier this year, and had a few more thoughts which were completely supplanted and rearranged by watching iOSDevUK. I threw away my earlier draft; you’re about to read something different. Where you see “we”, “us” or “our community” you should probably take it to mean Cocoa programmers, though read on to find out why “us” doesn’t always make sense.

Acknowledgements

So many people have contributed to this, by saying things that I agree with, by saying things that I disagree with, by organising conferences, or in other ways. I’ve tried to cite where appropriate but I’ve probably missed someone somewhere. Sorry :-(.

Introduction

This article is more the presentation of a problem and some thoughts about it than an attempt to argue in favour of a particular solution. I’ll investigate what it means to be in “the Cocoa programming community”, beginning with whether or not Apple is in a community of its own devising. I’ll ask whether there’s room for more collaboration in the community, and whether the community of Cocoa programmers encompasses all Cocoa programmers. Finally, I’ll notice that these are questions as yet unanswered, and explore what the solutions and non-solutions might be.

On Apple and the community

This is the bit that I’d done most work on already, as it was the topic of my Appsterdam talk. The summary of that talk is pretty much the same as Dave’s working-with-Apple pro tip in his iOSDevUK talk. As his was more succinct, I’ll use that version:

Apple is people too. Don’t be a dick.

(I’m a fan of people not being dicks.)

The thing is that as Scotty said, the community wins when all of its members win. But he also said that Apple isn’t in the community, so don’t they obviate themselves from this relationship?

Well, no. If we look at the community that most of the people reading this post – and that most of the people at iOSDevUK – consider themselves a member of, it’s the community of iOS app makers. It happens that all of these people depend on the same thing: on iOS. Being nice to Apple and helping them just makes good business sense. If you’re not helping Apple to win, they might decide to help you lose.

On a related subject: for Apple to win, it’s not necessary for anyone else to lose. In fact, I’m not the first person to say this. I’m stealing from a man who was, at the time this quote was coined, freshly CEO after having been a management consultant at Apple:

We have to let go of this notion that for Apple to win, Microsoft has to lose. We have to embrace a notion that for Apple to win, Apple has to do a really good job. And if others are going to help us that’s great, because we need all the help we can get, and if we screw up and we don’t do a good job, it’s not somebody else’s fault, it’s our fault.

So Microsoft, Windows 8 and Windows Phone 8 don’t have to lose. Google and Android don’t have to lose. Enterprise Java programmers don’t have to lose. Your competitors don’t have to lose. The team in Apple that make that thing that just crashed don’t have to lose.

On that last note, Apple is the biggest company in the world and you’re supplying one or a handful of 600,000 or so different replaceable components that helps them make a trivial fraction of their income. So if the choice you give them is “do what I need or I’ll stop working with you”, they’ll pick option 2. “Fix Radar or GTFO”? It’s cheaper and easier for Apple to GTFO.

That’s not to say the best strategy is always to do whatever Apple want. Well, actually it probably is in the short term, but Apple is real people and real people benefit from constructive feedback too.

Just who is “them”, anyway?

Around the time that I started to be a proper software writing person, there was a strong division in Mac development. The side I was in (and I was young, opinionated, easily led, and was definitely in this faction) was the Yellow Box. We knew that the correct way to write software for the Mac was to use the Foundation and AppKit APIs via the Objective-C or Java languages.

We also knew that the other people, the Blue Boxers who were using libraries compatible with Mac OS 8 and the C or C++ languages, were grey-bearded dinosaurs who didn’t get it.

This sounds crazy now, right? Should I also point out that I wrote a Carbon app, just to make it sound a little crazier?

That’s because it is crazy. Somehow those of us who had chosen a different programming language knew that we were better at writing software; much better than those clowns who just made the most successful office suite ever, the most successful picture editing app ever, or the most successful video player ever. Because we’d taken advice on how to write software from a company that was 90 days away from bankruptcy and had proven incapable of executing on software development, we were awesome and the people who were making the shittons of money on the most popular software of all time were clueless idiots.

But what about the people who were writing Mac software with WXWindows (which included myself), or RealBASIC, or the PerlObjCBridge (which also included me)? Where did those fit in this dichotomy? Or the people over on Windows (me again) or Solaris (yup, me here)?

The definition of “us” and “them” is meaningless. It needs to be, in order to remain fluid enough that a new “them” can always be found. Looking through my little corner of history, I can see a few other distinctions that have come and gone over time. CodeWarrior vs Project Builder. Mach-O vs CFM. iPhone vs Android. Windows vs Mac. UNIX vs VMS. BSD vs System V. KDE vs GNOME. Java vs Objective-C. Browser vs native. BitKeeper vs Monotone. Dots vs brackets.

Let’s look in more detail at the Windows vs Mac distinction. If you cast your mind back, you’ll recall that around 2000 it was much easier to make money on Windows. People who were in the Mac camp made hand-waving references to technical superiority, or better user interfaces, or breaking the Microsoft hegemony, or not needing to be super-rich. Many of those Mac developers are now iPhone developers. In the iOS vs Android distinction, iOS developers readily point to the larger amount of money that’s available in making iOS apps…wait.

O(community)

The community contribution fraction

As Scotty said, an important role in a community is that of the reader/consumer/learner, the people who take and use the information that’s shared through the community. Indeed in any community this is likely to be the largest share of the community’s population; the people who produce and share the information are also making use of it too.

The thing is, that means that there are many people who are making use of those great ideas, synthesising them, and making even new and better ideas. And we’re not finding out about them. Essentially there is more knowledge than there is opportunity to share knowledge.

It’d be great to have some way to make it super-easy for everyone who was involved in “the community” to contribute, even if it’s just to add a single thought or idea to the pool. As Scotty said, there’s no way you can force people to contribute, and that’s not even desirable as it’s a great way to put people off talking to you ever again.

So you can’t hold a gun up to people and force them to tell you a fact about Objective-C. You can ensure everyone knows what forms of contribution take place; perhaps they’ll find something that’s easier than they thought or something they’ll enjoy. Perhaps they’ll give it a go, and enjoy it.

Face to face

Conferences are definitely not that simple way for everybody to contribute. Conferences are great, though as I’ve said before there aren’t enough seats for them to have a wide direct impact on the community. Tech conferences will never be a base for broad participation, both due to finite size (even WWDC comprises less than one percent of registered developers on the platforms) and limited scope for contribution – particularly the bias toward contributors with “prior”.

One “fix” to scale up the conference is to run the conference all year long. This allows people who don’t like the idea of being trapped in a convention with the same 200 people for a week the option to dip in and out as they see fit. It gives far more opportunity for contribution – because there are many more occasions on which contribution is needed. On the other hand, part of the point of a conference is that the attendees are all at the same place at the same time, so there’s definitely some trade off to be had.

Conferences and Appsterdams alike lead to face-to-face collaboration; the most awesomest flavour of collaboration there is. In return, they require (like Cocoaheads, NSCoder or whatever you call your pub/café meet) that you have the ability to get to the venue. This can call for anything from a walk down the street via a couple of ten-hour flights to relocating yourself and your family.

Smaller-scale chances for face to face interaction exist: one-on-few training courses and one-on-one mentoring and apprenticeships. These are nearly, but not quite, one-way flows of information and ideas from the trainer or sensei to the students or proteges. There are opportunities to make mentoring a small part of your professional life so it doesn’t seem to require a huge time investment.

Training courses, on the other hand, do. Investment by the trainer, who must develop a course, teach it, respond to feedback, react to technology changes and so on. Investment by the trainees, who must spend an amount of time and money attending the course, then doing any follow-up exercises or exams. They’re great ways to quickly get up to speed with a technology by immersing yourselves in them, but no-one is ever going to answer the question “how can I easily contribute to my community?” with “run a training course”.

Teaching at a distance

A lower barrier to entry is found by decoupling the information from the person presenting the information. For as long as there has been tech there have been tech books; it’s easy (if you have $10-$50) to have a book automatically delivered to your house or reader and start absorbing its facts. For published books, there’s a high probability that the content has been proofread and technically reviewed and therefore says something a bit accurate in a recognisable language.

On the other hand, there are very few “timeless” books about technology. Publisher schedules introduce some delay between finishing a manuscript and having something to sell, further reducing any potential shelf life. If you’re in the world of Apple development and planning to say anything about, for example, Objective-C or Xcode, you’re looking at a book that will last a couple of months before being out of date.

Writing a book, then, takes a long time which already might be a blocker to contribution for a lot of people. There’s also the limitation on who will even be invited to contribute: the finite number of publishers out there will preferentially select for established community members and people who have demonstrated an ability to write. It’s easier to market books that way.

The way to avoid all of that hassle is to write a blog (hello!). You get to write things without having to be selected by some commissioning editor. Conversely, you aren’t slowed down by the hassles of having people help you make the thing you write better, either—unless you choose to seek that help.

You then need to find somebody to read your blog. This is hard.

Stats for this blog: most pages have only ever been read a couple of hundred times.

If someone else already has an audience, you can take advantage of that. Jeff Atwood previously wrote about using stack overflow as a blog, where you’d get great reach because they bring their audience. Of course, another thing you can do on stack overflow is answer questions from other people: so that quick answer you contribute is actually solving someone’s problem.

This is, in my opinion, the hallowed middle ground between books (slow, static, hard to get into, with a wide reach) and blogs (fast, reactive, easy to pick up, hard to get discovered). Self-publishing a book is a lot like spending ages writing a long blog post. On the other hand, contributing to a community resource like a Q and A site or a wiki means only writing the bit of the book that you’re best placed to contribute. It also means sharing the work of ensuring correctness and value among the whole contributor base.

Our community / People with ideas ≪ 1

Whatever your definition of “the community”: the iOS developer community, the object-oriented programming community, the developer community—there are many more people who aren’t in that community. But they still have things to say that could be interesting and help us see what we do in different ways.

I’m not so sure that there are people out there doing what we do who don’t even passively engage with the rest of the community. Maybe there are, maybe there are lots. But I’m sure most people have at least read a book, or done a search that ended up at a mailing list post or blog entry. Very few people will never have used community-supplied resources; although it’s possible that there are programmers out there who’ve learned everything they know from first party documentation.

What I am sure of is that if you’re an Objective-C developer building mobile apps and you only listen to other Objective-C developers building mobile apps, you’re missing out on the information and ideas you could be taking from everyone else. Dave Addey told us to go and visit museums and art galleries to get inspiration, but that’s not all there is to it. Talk to someone doing Objective-C in a different context. Talk to someone doing Java, or Clojure. Talk to business people, or artists, or musicians. Break out of the echo chamber, and find out whether what other people are doing could be applied to what you’re doing.

Conclusions

As promised, there aren’t really any conclusions here. It’s more a collection of my own thoughts dumped out from brain to MarsEdit in order to let me make sense of them, and to stop me having to think about them at bedtime.

What’s clear is that there are a load of different ways for people to contribute to a community. Consumption of other people’s thoughts, advice and ideas is itself a very beneficial service as it’s how new ideas get synthesised, how new practices are formed and how the community collectively improves its output. It would be even better if what those people were doing were also made available and shared with the rest of us, to achieve an exponential growth in experience and advancement across the whole community.

But that’s not guaranteed to happen. The best thing to do is not to try driving people to contribute, but to give them so many opportunities to do so that, at some point, someone in the community will be in the position that sharing something is really easy and they choose to do so.

Other techniques to improve the number of ideas you get from the community are to be less adversarial in your definition of community, and more broad in your inclusion. The “community of people making iOS apps with Objective-C” is small, the “community of people making things” is universal.

Password checking with CommonCrypto

I previously described a system for storing and checking credentials on Mac OS and iOS based on using many rounds of a hashing function to generate a key from the password. Time has moved on, and Apple has extended the CommonCrypto library to provide a simple, standard and supported way of doing this. If this is still a problem you need to solve, you should look at doing it this way instead of following the earlier post.

We’re going to use a key-stretching function called PBKDF2 to make an encryption key of a standard length. This key probably will be much longer than the user’s password. However, it can’t possibly be any more random: it must be deterministically derived from that password so we’re stuck with the same amount of randomness that this password contains. A good key-stretching function should “smooth out” the randomness from the initial password, so that you can’t guess anything about the password given the function’s output.

We’re then going to use this key to calculate an HMAC of some known data. An HMAC is a bit like a digital signature, in that it depends both on the key used and the input data. We’ll store the HMAC but not the password and not the key derived from the password. Whenever the password is needed in the future, it must be provided by a user and cannot be derived from any of the data in the app.

The idea, then, is that when you set a new password, the app calculates this key, uses that to calculate an HMAC and stores the HMAC. When you try to use the app, you present a password, from which the app generates a key and an HMAC of the same data. If this HMAC matches the one that was previously calculated, the same keys were used, which (hopefully) means that the same password was supplied.

The faster you scream, the slower we go.

One of the problems that a key-stretching function must address is that computers are really, really fast. Normally computers being really, really fast is a benefit, but the faster it is to compute all of the above stuff the more guesses an attacker can try at the password in some amount of time. We therefore want to make this function slow enough that brute force attacks are limited, but not so slow that people get frustrated with the app’s performance.

PBKDF2 has a tuneable parameter – the number of rounds of a hashing function it uses internally to stretch the key. With CommonCrypto you can ask for a number of rounds that will result in the function taking (approximately) a certain amount of time to work on a password of a certain length.

Requirements for the rounds parameter

  • The number of rounds used when checking a password should be the same as the number used when generating the stored HMAC, otherwise the keys generated won’t match.
  • The above means that you need to choose a single value to use across app installs and hardware – unless you’re happy with losing access to all user data when a customer upgrades their iPad.
  • You will want to revise this parameter upwards as faster hardware becomes available. This conflicts with the first requirement: you’ll need a fallback mechanism to try the same password with different “versions” of your rounds parameter so that you can upgrade users’ credentials with your app.

With those in mind, you can construct an algorithm to choose a tuning parameter based on this call:

        const uint32_t oneSecond = 1000;
         rounds = CCCalibratePBKDF(kCCPBKDF2,
                                   predictedPasswordLength,
                                   predictedSaltLength,
                                   kCCPRFHmacAlgSHA256,
                                   kCCKeySizeAES128,
                                   oneSecond);

You can probably know what length of salt you’ll use: salt should be a block of random data that you supply, that is used as additional input to the key-stretching function. If two different users supply the same password, the salt stops the function from generating the same output. You probably can’t know what length of password users will use, but you can guess based on experience, data and knowledge of any password strength rules incorporated into your app.

Notice that you should derive this rounds property on the target hardware. The number of rounds that take a second to run through on your brand new iMac will take significantly longer on your customer’s iPhone 3GS.

Generating the HMAC data

Both storing and checking passwords use the same internal function:

- (NSData *)authenticationDataForPassword: (NSString *)password salt: (NSData *)salt rounds: (uint) rounds
{
    const NSString *plainData = @"Fuzzy Aliens";
    uint8_t key[kCCKeySizeAES128] = {0};
    int keyDerivationResult = CCKeyDerivationPBKDF(kCCPBKDF2,
                                                   [password UTF8String],
                                                   [password lengthOfBytesUsingEncoding: NSUTF8StringEncoding],
                                                   [salt bytes],
                                                   [salt length],
                                                   kCCPRFHmacAlgSHA256,
                                                   rounds,
                                                   key,
                                                   kCCKeySizeAES128);
    if (keyDerivationResult == kCCParamError) {
        //you shouldn't get here with the parameters as above
        return nil;
    }
    uint8_t hmac[CC_SHA256_DIGEST_LENGTH] = {0};
    CCHmac(kCCHmacAlgSHA256,
           key,
           kCCKeySizeAES128,
           [plainData UTF8String],
           [plainData lengthOfBytesUsingEncoding: NSUTF8StringEncoding],
           hmac);
    NSData *hmacData = [NSData dataWithBytes: hmac length: CC_SHA256_DIGEST_LENGTH];
    return hmacData;
}

Storing credentials for a new password simply involves generating a salt, computing the authentication data then writing it somewhere:

- (void)setPassword: (NSString *)password
{
    //generate a random salt…

    //do any checking (on complexity, or whether the passwords entered in two fields match)…

    NSData *hmacData = [self authenticationDataForPassword: password salt: salt rounds: [self roundsForKeyDerivation]];
    //store the HMAC and the salt, perhaps by concatenating them and putting them in the keychain…

}

Then testing the credentials involves applying the generation function to the password guess, and comparing the result with what you previously stored:

- (BOOL)checkPassword: (NSString *)password
{
    //recover the HMAC and the salt from wherever you stored them…

    NSData *guessedHmac = [self authenticationDataForPassword: password salt: salt rounds: [self roundsForKeyDerivation]];
    return [guessedHmac isEqualToData: hmacData];
}

Conclusion

If you can avoid storing a password in your app, even in the keychain, you should; there’s then a much reduced chance that the password can be recovered from the app by an attacker. Deriving a key from the password using PBKDF2 then testing whether you can use that key to obtain a known cryptographic result obviates the need to store the password itself. Mac OS X and iOS provide an easy way to use PBKDF2 in the CommonCrypto library.

The key derived from the password could even be used to protect the content in the app, so none of the documents are available without the password being presented. Doing this offers additional confidentiality over simply using the password for access control. Building a useful protocol around this key requires key wrapping, the subject of a future post.

Sound bites considered harmful

Knuth said:

premature optimization is the root of all evil.

Only, what he actually said was:

There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified. It is often a mistake to make a priori judgments about what parts of a program are really critical, since the universal experience of programmers who have been using measurement tools has been that their intuitive guesses fail. After working with such tools for seven years, I’ve become convinced that all compilers written from now on should be designed to provide all programmers with feedback indicating what parts of their programs are costing the most; indeed, this feedback should be supplied automatically unless it has been specifically turned off.

In that context, the quote takes on a very different meaning.

How people learn

Don’t you hate those times when you go to a talk or article that says “you should be doing this”, but then doesn’t explain how to do that?

I just wrote one. In “Coding. Standards.” I explained that what software engineers should do is to learn and analyse from all their experiences and interactions. But how do you do that? How does learning work?

The model of learning that I know best is the Kolb learning cycle, which I was taught when I was in management training. Yes – the reason I wear my hair this long is to stop it going pointy. David Kolb published this model in the 1980s, it’s been augmented and adapted since then but is still approximately as written. Having said that, some neuroscientists dispute the validity of this and other models of learning.

Kolb says that there are four distinct processes in learning:

  • Concrete Experience: actually doing a thing.
  • Reflective Observation: analysing how you (or someone else) did a thing.
  • Abstract Conceptualisation: building a model of how things should be done.
  • Active Experimentation: just playing with the plasticine and seeing what comes out.

Not everybody goes through all of the items in the cycle, but most people start out somewhere and progress through at least a couple of the points, probably in the order presented (acknowledging that as a cycle it should be, well, cyclic). The four examples he gave corresponded to four “corners” of the cycle:

  • Diverging: concentrates on concrete experience and reflective observation. Tries something out, and decides how it could be adapted.
  • Assimilating: focuses on observation and conceptualisation. Sees how different examples might fit a common pattern.
  • Converging: conceptualisation and experimentation. Try to build a mental model from theory and then put it into practice.
  • Accommodating: experimentation and experience. Hands-on trying things out and deciding which ones work.

This gives you a starting point for working out how you learn, and what materials best support your style: there are many blog posts and conference talks based on personal experience, which fit the “reflective observation” category. Technical books tend to be more abstract, and fit conceptual learning. Training courses often bridge conceptualisation and experimentation.

It also gives you a way to understand why some people don’t like your good-faith attempts to help them. “RTFM” works for those people who want to build a conceptual model, but not for everybody else. Similarly, those people saying “please give me the codes” may be people who learn by abstracting from examples. You can’t hate people just for learning in a different way.

Coding. Standards.

I just realised that this month marks the 10th anniversary of my first payment for writing software (on, of all the weird things to be writing software on in 2002, a NeXTstation)! What have I learned from those ten years? What advice would I give to someone who wants to do this stuff for at least 10 years?

Programming is the easy bit.

Well, comparatively. There are hard bits in programming, and every few years a new paradigm comes along that means you have to unlearn whatever it was you were doing and learn something else to do anyway. So learning programming is never done, but still programming is easier than:

  • estimating. The one project I’ve worked on that finished on its planned completion date only did so by accident.
  • getting any kind of agreement out of two or more people.
  • accepting that the other person isn’t a dick, but has different goals and problems than you.
  • objectively evaluating your own work.
  • objectively assessing someone else’s evaluation of your work.
  • stopping programming when you’re done.

You always need to be learning.

You can’t compete on price in the software market, because there’s always some student somewhere who’s willing to do the same work for free. It was Mike who first taught me that. You have to compete on quality, which means you need to strive to improve your own quality. Because other people are too, so you need to run just to stand still.

There are various ways to learn, and they’re not mutually exclusive. A combination of books/articles, experimentation, and discussion with peers is valuable. If your town has an Appsterdam or a CocoaHeads, get along and say hello.

You probably don’t want to be doing this in 10 years.

I was actually a UNIX programmer a decade ago (well, I was mainly a student). Then I was a barman, then sysadmin, then a Linux server application programmer, then a Mac app programmer, then a contract Mac programmer, then a Java app programmer, then a security consultant, then an iOS app programmer. There’s only a small probability that I’ll be an iOS app programmer in 10 years.

This world moves really quickly. Ten years ago, the iPod was a new and relatively risky proposition. Macs used PowerPC CPUs. Windows XP was the new hotness, and .NET was just about to appear – meanwhile Mac OS X was a sluggish amalgam of NeXT, Java and legacy code. Java, by the way, was run by a now-defunct company called Sun Microsystems, which was trying to work out how to survive the dot-com crash.

Speaking of the dot-com crash, it seems highly likely that within the next decade we’ll see the dot-app crash. App downloads are worth $0.18¢ each, but an app costs $200k – apparently it’s hard work. That means you’ve got to either get yourself into the long tail value-wise (i.e. have a very good app that people will pay for), or you’ve got to find a million users for version 1.0.

For everyone else, the market isn’t worth staying in long-term. The market will bore of brochureware apps, only a few high-value brands will be able to support unprofitable vanity apps, and VCs will realise that throwing their money after an app with no profit strategy is the same as throwing their money after a website with no profit strategy.

It’s likely that at least one of the companies that’s big in the current software world – Microsoft, Apple, Oracle, Google and the like – will be big in the software world of 2022. It’s also likely that there’ll be some new comers that change things completely: Facebook and Twitter didn’t exist ten years ago, and neither did Android, Inc. Sometimes companies that seem to be in an interminable tailspin – like Apple – turn themselves around and become successful.

Learn more than one thing

This is related, in part, to what came above: the thing you’re using right now may not exist, or may be hard to get work in, in a few years’ time. On the other hand, some things seem to outlive the cockroaches: C – and by extension, languages that can link somewhat seamlessly with C like C++, Fortran and so on – have been going on forever. It can be hard to predict which of these camps your favourite tech sits in, so learning more than one thing keeps you employable.

More than that, if your technology of choice comes from a single supplier (e.g. Microsoft, Apple, Embarcadero) then diversification just makes good business sense. This particularly applies in the age of the app store where that sole supplier can also be your sole vendor – you don’t want to sign your entire business’s value over to one other company.

Learning another thing makes you better at the first thing

This is another reason why diversifying your technology portfolio is beneficial. Many of the changes I’ve made recently in the way I write object-oriented software come from talking to Clojure programmers.

The more different things you know, the more connections you’ll be able to make between them. The more you’ll be able to critically analyse one technology, beyond what the vendor tells you. And the more you’ll be able to understand other new things and incorporate them into your Weltanschauung.

Conclusion

My summary could be “learn whatever you can: you never know which bits you need”. Or it could be “don’t rely on your supplier to solve all of your problems”.

I think it’s actually going to be: analyse everything. Reflect on your work: what went well? What didn’t? Could you have done things better? If you don’t think you could have, then you’re probably wrong: what would you need to know to identify the bit that actually could’ve gone better?

But know when to stop, too. Analysis paralysis is as much of a problem as going in blind. At some point, you need to suck it up and move on. Trading these two things against each other is the real difficulty in software engineering.