Thursday, September 30, 2010

JavaScript: The Way Of The Future

JavaScript Reference, JavaScript Guide, JavaScript API, JS API, JS Guide, JS Reference, Learn JS, JS Documentation JavaScript has been around for ages, but it's coming into it's own with powerful, highly productive tools. The latest generation browsers, ECMAScript5, HTML5/CSS3, PhoneGap and Titanium, WebOS, node.js (on the JVM I'd rather program rhino or ringo than groovy), and an enormous number of excellent supporting libraries.

Thomas Fuchs has blogged that we should help make JavaScript documentation better and I rather agree. He also suggests blogging about JavaScript particularly about topics that make it easy for new JavaScript programmers to pick up skills quickly.

So I will! But what topics should I include?

  • Testability and TDD of JavaScript is important to me.
  • The separation of business logic and DOM access.
  • Functional programming style, and how it simplifies things.
  • Learning with server-side JavaScript to improve client-side code.
  • Explaining some odd stylistic idioms, (such as single var, or bracketed immediate function definition and call).

Yesterday I put together a lightning talk of some non-beginner techniques. But they might make good topics as well. Sometimes getting your head around a few tricky problems can make the less complex stuff much easier. Running a few miles each day and getting up the stairs will be much easier.

Friday, September 17, 2010

Not just generalists

We have a development culture that says developer work is ideally directly associated with a user story. And that more or less any developer could work on any user story.

We like to think that if there are technical problems they will be solved by the developers on the user story that is encountering the problem. Technical problems include things alien to the actual user story: tooling and IDE plug-ins, code and project organisation, continuous integration requirements, and deployment complexities.

Implicit in our arrangement is that any developer should be able to solve any of these problems. That isn't the truth for us.

In reality not all programmers are equally able to solve all the problems we face. We hire developers after testing their programming and design skills, and we pay some attention to their interpersonal skills and customer focus. There's a lot of stuff we don't test for, and if we have better than average skills with that other stuff then it's luck not planning. And we have made some poor quality systems that hold us back because of it.

Our CTO says that we are a group of generalising specialists, which to me means we should be first of all specialists who can also serve in general roles. I think that's an aspirational statement: we don't look for specialists and our hiring process wouldn't favour them. Similarly if you go with the flow of our development process you'll become more and more generalised. So far as I can see our baseline is generalist.

And yet we do have developers among our number who are interested and able to do well at many of these things that are problematic for us. Specialists and people interested in specialising.

So my puzzle is how to maintain our user story focus, because I'm proud of that at the end of the day, but also how to encourage developers to usefully pursue their interests and apply their particular skills where they add value. Genuinely, how to be what my CTO would like to say we are, specialists who can work in general situations.

Frankly I don't want to feel vaguely guilty when I say that I don't like some of the stuff I do, and I don't want to feel guilty for doing the stuff that I do like when it adds value. I want to feel allowed to add value rather than just ticking off story cards.

I imagine a situation with developers respected for taking some hours of the week to step away from specifically story centred work and giving attention to technology questions that they particularly care about.

When I say step away, I mean this literally. For my own part I get up from my regular work when I hear people having trouble with the front end and go and get involved. Sometimes I just listen in to understand new things. Sometimes I actively contribute. Sometimes other people call on me to help. Sometimes I identify problems and go fix them myself. It's not all of my time, it's not as much of my time as I'd like, but I enjoy it and it's good for overall productivity.

If that's valuable and it's just me then it's a company vulnerability: we miss the opportunity to get value from efficient use of specialist skills. Also, I'd love people who have big ideas to have a sympathetic forum to share them and also to be informed balancing opinions for big decisions. So I'm thinking about working groups (my CTO wants advisory groups) intended to satisfy those roles, shouldering the load together and encouraging each other to better, without being a drag on our growing development team. Let's see how it works out.

I should say, the simple words "Working Group" are important. "Group" because I don't want specialisation to promote single points of failure. And "Working" because it's about actually doing the work we see needs doing rather than feeling we shouldn't do it because it's out of story.

Thursday, September 16, 2010

Super-Star Programmer: The Game

Years ago I was a keen participant in a game design forum called The Forge where they'd made a taxonomy of player priorities: gamist, narrativist, and simulationist (GNS). Different game systems favour different priorities and are more or less satisfying to different players depending on their priorities. [1]

I think formal software development processes are games: you follow the rules to get the experience of being a software developer, a super-star programmer.

The game itself

We all have a notion of super-star programmers and we set up a whole lot of rules and rewards that guide us toward behaving like super-star programmers.

There are lots of things we think super-star programmers do: They make use of computers to prove things about their work. They know about the up coming tasks. They interact with interested parties throughout their work. They make it easy for others to find out what they're doing. They actively learn and share their knowledge. They seek out feedback and create opportunities to act on it. Their code works all the time. They stay on task and their work is relevant to requirements.

We prescribe various practices to get those outcomes in our work: various meetings (planning games, retrospectives, root cause analyses, stand ups); various reporting systems and information radiators; Kanban; process steps that require consulting with specific individuals or teams; TDD and pairing; Continuous Integration; and other practices that we consciously build in to our process. All these together are our game.

It doesn't always work: all these rules don't always produce the playing experience we hope for. And it works differently for different people. A given process may be effective at guiding some developers and be quite disheartening to other perfectly professional developers.

We'd like to be super-star programmers who just do all this stuff without the need for the explicit process. That said, game playing can be one of the best ways to learn hard things.

Lessons from game design

GNS gives us some clues about why processes sometimes go wrong and other times they go right.

The gamist priority: gratified by the rewards and adhering to the rules in order to get those rewards. On the one hand they will stick to the rules, often with lots of energy and enthusiasm for the system itself. On the other hand they are likely to exploit flaws in the system. If you create an environment were some important activities are outside of the rules, then they may ignore those activities. "Gaming the system", sums up the risks of relying on process with gamists.

The simulationist priority: these guys really want following the rules to deliver the goods, and they can adapt to quite intense and restrictive systems. They'll work with flaws in the system if they understand the objective. They'll make the effort to do it right rather than just doing a minimum to satisfy a rule. By contrast they can be quite annoyed if after abiding by the rules the results don't follow as promised. The most diligent players can be the harshest critics of the game and are easily frustrated by imperfect systems.

The narrativist priority: attentive to the arc of activity, the big picture and human concerns. At best they'll take the simplest framework and make it work. But they are notorious for abandoning the rules to make the game suit them. Rules that say what you're trying to achieve are important to them. Conversely insisting on a process without giving attention to the desired outcome will seem like manipulation and frustrate them in very short order.

(I always self identified as a narrativist with some simulationist leanings.)

How we play in our office

Looking around my office I see all of these priorities to varying degrees among my colleagues.

We have a step in our process to talk to QA. The simulationists are likely to actually take time to talk to QA when that step requires it, after all it's what we're supposed to do. But the system gets abused by the gamists who might take a few passing comments as justification to say they've satisfied that step. Meanwhile the narrativists get annoyed because they did talk to the right people but at some other time when it seemed to fit in better.

Thinking about when we've tried to introduce promising new practices. The gamists take to them with gusto, making them work to get new rewards from a new rule. The simulationists trudge through, annoyed that it seems we don't really know what will deliver the desired outcome. The narrativists grow frustrated at yet more things slowing them down.

How about stripping back or simplifying a system when it's going smoothly? How quickly do the gamists stop doing that good thing? The simulationists grumble about changing the rules when they were working just fine. While the narrativists feel liberated by being able to do it differently and perhaps even better out of the opportunity.

And so we suffer seemingly endless rounds of process tuning, never satisfying anyone.

It's not a game!

In the gaming world we could always say that if you don't like the game then don't play. The situation in the office is harder.

For all my love of games, playing this game is secondary. Yes, it helps me when I'm not up to super-star programmer standard, it helps keep us all on the same page. But what matters more is actually striving to be a better programmer, not just playing at the game.


[1] I've taken some liberties in my interpretation of GNS for sake of illustration, I don't intend to argue the fine points of GNS theory here.

Tuesday, September 14, 2010

First days of node.js and mongodb

I'm putting together a little web application to manage Delphi Method style collaborative priority setting and decision making. I'm using it as a chance to try out node.js, mongodb with a native mode driver, and express.js.

Altogether they make a sweet team. I'm loving the lack of the typical language impedance mismatch in this type of project. And the possibility of sharing code between client and server without adding the bulk of something like GWT or cappuccino.

However I'm not happy with the mongodb driver interface style for node.js. Consider the following variation on it's simplest example, which checks if a record exists and adds it if it doesn't:


var db = new Db('my_db',
new Server(host, port, {}),
{native_parser:true});
var query = {'key': 'value'};
var record = {'key': 'value', 'data', 'stuff'};

db.open(function(err, db) {
db.collection('test', function(err, collection) {
collection.find(query, function(err, cursor) {
cursor.toArray(function(err, items) {
if (items.length > 0) {
db.close();
sys.puts("query already exists");
} else {
collection.insert(record, function(err, docs) {
db.close();
sys.puts("record has been added");
});
}
});
});
});
});


This mongodb driver follows the node.js idiom for non-blocking IO, providing a function which will be called when the IO is complete. Each of the nested functions above is not being executed immediately. They are deferred until the mongo database interaction is done, giving control back immediately to the caller, playing nice in a cooperative multitasking environment.

But the simplistic code style presented above and in sample code encourages code duplication. And frankly naming anonymous functions is one of the first things to do if you want other folk to be able to read your JavaScript code. Let's pull those anonymous function out and see if it's any clearer.


var database = new Db('my_db',
new Server(host, port, {}),
{native_parser:true});
var db;
var collection;
var query = {'key': 'value'};
var record = {'key': 'value', 'data', 'stuff'};

openMyDb(database);

function openMyDb(database) {
database.open(openTheTestCollection);
}

function openTheTestCollection(err, database) {
db = database
db.collection('test', findRecordsWithKeyAndValue);
}

function findRecordsWithKeyAndValue(err, coll) {
collection = coll;
collection.find(query, convertingTheCursorToAnArray);
}

function convertingTheCursorToAnArray(err, cursor) {
cursor.toArray(insertKeyAndValueIfRequired);
}

function insertKeyAndValueIfRequired(err, items) {
if (items.length > 0) {
db.close();
sys.puts("query already exists");
} else {
collection.insert(record, closeDb);
}
}

function closeDb(err, docs) {
db.close();
sys.puts("record has been added");
}


Notice how there needs to be a db and a collection variable to share those things between the functions. But that means that this is no longer safe in face of parallel operation.

The nasty nested version was necessary because those nested contexts were creating new variables needed for safe parallel operation. I can't reuse the nested functions, but I can't safely use the non-nested functions.

A solution might be something like a push forward context along side the function parameter:


openMyDb(new Db('my_db',
new Server(host, port, {}),
{native_parser:true}),
{'key': 'value'},
{'key': 'value', 'data', 'stuff'});

function openMyDb(db, query, record) {
db.open(openTheTestCollection,
{'query':query, 'record': record});
}

function openTheTestCollection(err, db, context) {
context.db = db;
db.collection('test', findRecordsWithKeyAndValue, context);
}

function findRecordsWithKeyAndValue(err, collection, context) {
context.collection = collection;
collection.find(context.query, convertingTheCursorToAnArray, context);
}

function convertingTheCursorToAnArray(err, cursor, context) {
cursor.toArray(insertKeyAndValueIfRequired, context);
}

function insertKeyAndValueIfRequired(err, items, context) {
if (items.length > 0) {
context.db.close();
sys.puts("query already exists");
} else {
context.collection.insert(context.record, closeDb, context);
}
}

function closeDb(err, docs, context) {
context.db.close();
sys.puts("record has been added");
}


This would give me a safe way to reuse these functions and make it possible build up some nice reusable chunks for things that are being duplicated at the moment, (like opening a database and a given collection).

I can easily imagine the driver itself copying things into the context, like I've done above with db and collection, and that would slim things down even further.

Perhaps I should fork the driver and try out those changes myself, as I've presented them they wouldn't even break any existing code.

Tuesday, September 7, 2010

Not Fungible

If developers are not just fungible, and development is not merely a collection of standard developers solving a problem, then what is going on? If you can't just replace a developer then that developer must be bring something unique to the task.

In reality developers come and go on a project. But we make accommodations, we reorganise ourselves to allow that developer to contribute their particular touches, the project changes because we changed developers. I think we under play the extent to which a new developer or the loss of a developer changes our work.

If we're not fungible then a development team is an accommodation that allows a bunch of developers to contribute their distinctive stuff to a unique solution in the effectively infinite space of possible solutions. (Okay, finite computer resources and acceptable execution time mean we're exploring a finite space of solutions, but it's a big space.)

If I was managing the project then I'd be trying to allow these distinctive craftsmen and artisans and artists and puzzle-solvers and engineers and explorers and scientists and philosophers to make something which brings sufficient value to enable us all to make living. The perspective becomes one of trust: I trust that these people will solve my problem.

It takes a lack of trust to prescribe how developers will do their work: if I define their roles to make them fungible then I'm almost definitively preventing their unique contributions and preventing them contributing their best. I suspect out of fear or some misguided fumbling after security we shape projects so that specific developers don't make a difference: but that way we demotivate developers and prevent them making their best contribution.

What happens if I really accept that I'm not fungible? I'm not an interchangeable resource filling a seat. The projects don't simply have quotas of seats that need to be filled.

Then I'm a creative person, and this particular workplace is where I currently exercise my creativity. The project would be different if I were not here. And any project I move on to will be different because of me.

So what do I bring? And what do I want to bring?