Programmer Culture and the God Complex

I recently volunteered at a mock interview night for a great company here in Chicago called i.c.stars (link here), and both of the budding programmers I ‘interviewed’ asked me questions along the lines of ‘do I enjoy being a programmer/developer?’ and ‘what are some things you don’t like about the profession.’ Pretty generic questions in themselves, but it got me thinkin’, and at the end of all this thinkin’ I was left with a rather sour taste in my brain; I love what I do, but there is a seeping underbelly of programmer/developer culture that I hate (and I use that word with due consideration for its meaning) – the God Complex.

At this point I could give a scathing expletive filled rant about having to deal with Tech Jocks, geniuses in various boxes, and ‘rock start developers’ a la Spider Jerusalem (see Transmetropolitan for reference) – but I think we’ll go another way. Let’s have a conversation instead – GASP.

Note – If you don’t know what I’m referring to or have not had any interactions with persons of the previously described natures, then congratulations. You are very, very lucky. Go about your business of hunting gummy bears while riding a unicorn made of chocolate.

Firstly, let’s make a common sense observation – with all the ‘Learn to Code’ and ‘Everyone Should Learn to Code’ dime-a-dozen endorsements we are seeing so much of, wouldn’t you think developers with the aforementioned complex be a bit more humble? Just a little bit more self-aware? Even if the majority of incoming programmers riding the ‘Learn to Code’ waves aren’t good enough yet to take your job, wouldn’t it give you pause? Just some food for thought.

If you want a great read about the state of the ‘Learn to Code’ movements we are currently seeing, look no further than a wonderful article by my good friend Basel Farag.

Anyway, onwards and back to my original point. Let’s debug this problem by first writing out some assertions to check ourselves against. One, the software development community is largely responsible for itself, the comportment of its members, and most importantly its future. Two, if we hold the first assertion as true, then the developer community can enact change from moment to moment. Three, we (as a collective) could then decide to stop accepting the God Complex as a necessary part of our industry.

So how do we do this? Not to hit the nail to hard on the nose, but we need to be ‘raising’ better programmers. We need to put more emphasis on leading others in our everyday lives, and have the continued humility to BE LEAD. If this sounds familiar, it’s because it’s a great nugget I got from a Surge talk by Bryan Cantrill (watch it here).

Walking the walk is far more important than talking the talk in software development. The continued acceptance, passive or otherwise, of the programmer genius complex is entirely our own fault. Positive, passive, and apathetic reinforcement has gotten us to this point. So the next time you are forced to work with a developer like this, spank them. I’m not kidding, give them a good Javascript whoopin’, or a Swift switch across the bum. They’re too far gone for anything else to work. And for those of you mentors out there that see this behavior budding in your mentees, snip it quick.

We are the technical makers and breakers of tomorrow, but don’t get it twisted, that doesn’t make us better or more deserving than anyone else. You are not God’s hipster gift to programming. If you disagree, get the f#*k out, the industry doesn’t need you. A good team is better than a solitary genius everyday of the week, and twice on Sundays.

Encoded Reflections: Code + Design Theory

Designing a great user interface starts off with the best of intentions; make it pleasing to the eye, providing great feedback, and ensuring a delightful interaction. However, when we get to the code it’s often a game of tacking on functionality, extra validation rules, and an ever growing management responsibility. Recently, I’ve been diving into Human-Centered Design, and how it can be applied in a technical context. So in light of LinkedIn now supporting code snippets I thought it’d be a great time to translate theoretical concepts to a real-world example – form pages.

Forms are tricky because there’s usually a lot going on, even in the simplest ones. But the central questions are basically the same in whatever language or platform you’re developing with: How do you let your users know what they CAN do, what they SHOULD do, and what they CAN’T do?

The most interesting thing I’ve found is that the more I think in terms of good design, the more readable my code becomes. This has nothing to do with design patterns or software architecture in particular, but more with writing code that uses language and concepts from usability design; in essence designing the code as a product with the programmer (in this case me, but it could be others in a large team) as the end user.

The example in this post is in Swift 3, and is only a small implementation of design theory as I see it applying to form field validation. First off, let’s get a bit of state management going, and fold in a signifier protocol; then we can talk about how theory is driving the code implementation.

//1
enum FormState{
    case Editable, Disabled, Loading, Interrupted, Complete
}

//2
protocol Signify{
    var currentState: FormState { get set }
    
//3
    func configureSignifiers()
    func updateSignifierState(to newState: FormState)
}

//4
extension Signify{
    mutating func updateSignifierState(to newState: FormState) {
        currentState = newState
        configureSignifiers()
    }
}

  1. First, we declare a simple enum to keep track of all the possible states our form can be in.
  2. Then we declare a protocol that will deal exclusively with how we designate and update the signifiers present in our forms.
  3. In here are two methods that all forms will have to conform too; the first is where you set all configurations for the given states, the second a wrapper for updating the state logic.
  4. Finally, you can declare a default implementation as a protocol extension, or you could alternatively declare it’s implementation directly in the form (as we would do with the configureSignifiers() method).

On first glance this might not seem like anything super cool, but I would draw your attention to what we’ve just done. The FormState enum is now a base definition for the physical constraints we want to impose on the form. On top of that, defining a Complete state means we’ve introduced an forcing/interlocking constraint in the form; it’s now very easy to keep the user from submitting the form while it is incomplete, all that’s needed is only enable the submit button when the form state is marked as Complete.

We can go even further. If we provide a set of good feedback systems for the user, as well as for ourselves as debuggers, we get a great level of usability. For instance, if we wanted to flesh out a custom response type that could be used to help the user understand how to fill out the form, and what they might be doing wrong, while at the same time providing great information for stepping through code, we could.

//1
enum FeedbackResult {
    case TooShort(forContext: String)
    case MissingNumber(forContext: String)
    case MissingSpecial(forContext: String)
    case DontMatch(forContext: String)
    case Valid
   
//2 
    typealias FeedbackTuple = (message: String, valid: Bool)
    
//3
    var info: FeedbackTuple {
        switch self {
            case .TooShort(let context):
                return (message: "Your \(context) is too short", valid: false)
            case .MissingNumber(let context):
                return (message: "Your \(context) needs a number", valid: false)
            case .MissingSpecial(let context):
                return (message: "Your \(context) needs a special character", valid: false)
            case .DontMatch(let context):
                return (message: "Your \(context)'s don't match", valid: false)
            case .Valid:
                return (message: "Go ahead and log in", valid: true)
        }
    }
}
  1. We’ve defined another enum, this time that deals exclusively with form validation feedback. The case list can be easily expanded and managed.
  2. This is a simple type alias declaration of a custom type we want to be able to access. This is where we’re constructing a type that is useful to the user and to our future selves.
  3. The info property is a computed property on the enum, letting us get a custom message and a valid boolean for each case. If we paired this with reactive programming, or simply used the UITextField delegate method that fires when the text is changed (ie. when a user inputs keystrokes), we’ve got a real-time validation system.

But so far, these two enums are not used in any concrete way. What we could then add would be another protocol, this one for string validation using our enums and extending the NSString class for easy access.

//1
protocol Validateable {
    func isValidEmail() -> FeedbackResult
    func isValidPassword() -> FeedbackResult
}

//2
extension String: Validateable {

//3
    func isValidEmail() -> FeedbackResult {
        let context = "email"
        
        if !self.contains("@") {
            return FeedbackResult.MissingSpecial(forContext: context)
        }
        
        return FeedbackResult.Valid
    }
    
    func isValidPassword() -> FeedbackResult {
        let context = "password"
        
        if self.characters.count < 5 {
            return FeedbackResult.TooShort(forContext: context)
        }
        
        return FeedbackResult.Valid
    }
}
  1. A small protocol that evaluates whether a given field input is a valid email or password
  2. Extending the NSString class to conform to this protocol.
  3. Writing the actual implementation of the email and password validation rules. You could use regex here or regular expressions instead of simple string checks.

We’ve now got the building blocks of a complex validation system, complete with feedback for users and developers! There are always ways to make code better, and this example is no exception. But what I’m most excited about is that attempting to translate physical design theory into code not only helped me manage my application better, it made my code easier for me to understand (not to mention helped solidify the actual conceptual learning).

It turns out theory can be quite concrete.

Software Architecture and Human-Centered Design

There’s always something new to learn. Hopefully, there’s a subsequent spark that ignites when you integrate new knowledge and come out the other side with an enhanced set of neural connections. The best ones, I’ve found, come from taking concepts from a neighbouring disciplines (or better yet, disciplines that are completely alien to each other), and applying them in a way that suddenly explains a concept or idea that was…let’s say cloudy.

There’s a great talk by Adrian Kosmaczewski which he aptly titled Being A Developer After 40. Now, I’m not 40, but his advice holds true for any age – which is essentially the conclusion of his talk – age doesn’t matter. In my roundabout way I’m coming to the point of this article, I promise.

While reading Don Norman’s The Design of Everyday Things (one of the many books Kosmaczewki recommends for expanded learning), I came across his 7 stages of action diagram, and how it relates to Human-Centered Design. Briefly, the stages are: Goal, Plan, Specify, Perform, Perceive, Interpret, and Compare.

This hit me like a ton of bricks, not because it was something alien to me, but because this summed up exactly how I wanted a code base to be designed. Why should HCD be relegated to just tangible products? In a digital age, the intangibles need love too.

In the diagram above, we would replace the goal, with the desired functionality. The world is the API, UI, or really anything you are interfacing with. Good software architecture should attempt to address both the bridge of execution and evaluation, but not only in terms of the user and the application but between the programmer and the code base. Let’s take a practical example of hitting an API endpoint.

  1. Goal: Hit the endpoint and retrieve data
  2. Specify: Give all relevant information to your class that is going to perform the call
  3. Perform: Fire the request
  4. Perceive: Evaluate if anything has happened at all
  5. Interpret: Parse out the results for evaluation
  6. Compare: Check if the result is what you were expecting

Steps 1 – 4 are well covered in most courses on developing software; it’s steps 5 and 6 that are often left out, or badly implemented. You shouldn’t have to crawl through a stack trace or step through hundreds (if not thousands) of lines of code with breakpoints to find the source of a bug – there should be a clear channel of communication from the code base BACK to the programmer. This might seem ridiculous because the programmer wrote the code, why would he/she need it to explain itself back to him. BECAUSE IT’S BETTER THAT WAY.

Techniques like Test Driven Development, Unit Testing, Integrated Testing, etc all do this fairly well, but even these tools are often left out in favour or faster development. I would go even further; I dream of a code base that spits out a human-readable snapshot of where something is wrong, what the problem may be, and possible ways to fix it. At this point in time we don’t have IDE’s that are that smart, but we should start thinking about how to add some of our own smarts into our projects. Write helper functions for debugging, use unit tests, check your assertions, anything but leaving out interpretation and comparison.

Software IS a product, which means it has many of the same design needs as a physical product. The user should not be the only one kept in mind when designing delightful usability. Extend this thinking to yourself (or think of other developers who will be working with your code) and see where it takes you.

If you need a place to start, always go to the grammar. Take a page out of the Swift API Guidelines and strive to write grammatically correct code and make use of explicit commenting. The focus should be on designing code base AS interactions between the programmers and the project. This will have big payoffs down the road in less on-boarding hassle, clearer interfaces, and a joyful coding experience.

“Always end with a quote” – thanks American History X – so here’s a good one; architect software code to be less”exasperatingly difficult to use, the tools of a cult of professional engineers and designers who seemed to take a perverse pride in making them as obscure and intimidating as the oracles of ancient Greece” (from Dealers of Lighting).