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?

Wednesday, August 25, 2010

Just Playing Games

I used to be very interested in game design, particularly the principles of reward systems. Basically a game mechanism is a system to reward (or penalise) behaviour and a game is about the behaviours it rewards.

Many games are unsatisfying or unsuccessful because they claim to be about one thing but reward something else. Dissonance between intent and practice is often very distracting. Sometimes players end up with a tacit social contract to not play by the rules but rather to improvise within the game's framework to get the experience and rewards they care about. Subverting a flawed game that way often requires being very motivated and having a firm vision for how things should be.

It seems to me that many of our practices and processes are like games. They aim to limit our behaviour or reward us for particular patterns of behaviour. But sometimes they are blunt instruments that can overly restrict behaviour and not reward the behaviour we really care about. A classic misguided reward is flagging that something has been done at the end of stage when what is really desired is to engage in some activity throughout the stage. For example we allow progress when we can flag that we've spoken to QA but that's just a proxy for involving QA throughout development.

And so we explain there are nuances and principles and that you're not doing it right if all you're trying to do is get past the check point. Imagine a game that says don't do what the rules say, instead improvise to do what the rules intend. At the end of the day the system has been set up to make the game about progress through stages, we reward progress through stages by making that progress visible, so indirectly we reward taking a minimum of time to get through the check points.

Similarly different types of computing work can deliver rewards in different ways. I've found build systems to be all or nothing affairs, indeed anything that involves searching for the right configuration tends to have no visible result until it is complete, a big payoff at the end of all the work. By contrast programming tends to deliver incremental progress from ongoing work, no grand climax but rewarding ongoing tinkering. These different patterns of reward for effort fit or conflict with different peoples motivations.

Being aware of what types of rewards drive different people is important in getting good work done rather than boring or frustrating them. Similarly being aware of the rewards implicit in any given task may make it a chore or treat to different people.

We should being aware that our explicit processes are fundamentally games: constraining behaviour and rewarding players. We need to be attentive to what behaviour is rewarded and how a developer might subvert the intent of the process by observing the game rules to the letter.

Perhaps we need to ask ourselves more deeply what is happening when good work gets done and aim to reward that directly, rather than playing games that need subverting by playing to lose but doing the job right.

We shouldn't assume that the games we've been playing are the best games to play.

Wednesday, August 18, 2010

Who is the process for?

One process to rule them all and in the darkness bind them.

I've been puzzling a lot about process recently. Why are we more interested in Process over People. We talk about processes, we tinker with and tune and polish processes, we treasure our precious processes, gollum, yes we do, my precious.

I've been asking the question of anyone who'll give me a minute, "why do we give more energy to process over people?"

My own answer is the most cynical: I find it hard to speak well of our clients and I'm indifferent toward our products - I give energy to my process to keep me working in a professional way even when I'm not motivated by my clients or product. Another answer is that processes are easier to change than people and programmers are almost by definition control freaks.

Processes are safety nets to ensure that good practices are given appropriate time and effort, knowing that we can't be trusted to always do the right thing.

So it makes sense that I should have a process that compensates for my weakness, but my weaknesses are not your weakness, so why do we imagine one process will suffice? Is that one process the set of all the things that we need to compensate for everyone's weaknesses, (assuming those things are not contradictory)? Or is the process just compensating for our common weaknesses, (perhaps giving us an excuse to not attend to our personal weakness, after all I'm following the process)?

Structured process should be a tool that you shape from a knowledge of your weakness and that serves to compensate for those weaknesses.

I value a team process. But I recognise that not everything in the process is there for my benefit, and some things I really need are not there. As a professional I aim to augment the team process with my own personal practices so that my work is better. As a professional I recognise that playing along with things that are not necessary for me can help others and improve the whole team's quality of work.

But how to strike the balance? How to keep the balance in the face of change?

Wednesday, August 4, 2010

Doing things the old way

We've been talking recently about why we get stuck in processes that no longer serve our needs even though they were the right thing when we started.

There were a few things that came up: tests written in particular ways, particular sign-offs in our development cycle, the idea that we must have certain types of tests, writing code in particular ways, and so forth.

There was a bit of defensiveness when unnecessary things were highlighted. We didn't set out to be inefficient, and we wanted to explain the context that made these things good ideas. Understanding that context and understanding where we're at now are both important for positive change. Lack of understanding just fuels defensiveness.

An experience of mine is a good metaphor:

Years ago I broke my left knee in a motor accident. I was very careful to do as I was told during my recovery and have since restored symmetrical strength and flexibility. (I even went on to do several years of regular fencing practice.)

Recently I've started Aikido classes and a basic training technique requires falling backward when thrown: lower yourself down with the rear leg and roll smoothly on your back, a very safe and effective way of falling. I can do this easily with my right leg but I always hesitate with left leg -- I hop back awkwardly or I fall and land heavily on my butt. I learned to defend my left leg after the accident, and I'm still unconsciously defending it long after the need has passed.

Now I've got pressure on me to change. I'm getting to the mats early and practising that backward roll slowly and gently so I know what it should feel like. I'm paying attention when it turns out right in class and trying to remember those successes more than the failures. I speak to other students and the instructor so that they give me the chance to practice it right. Eventually the right move will become unconscious. Who knows but maybe losing the habitual defence is the last stage of healing for that old injury?


Some of our quirky processes may be the same. At a time of need we carefully included steps which now are not needed. We've learned our lessons, but we've spent so long valuing them as necessary defences that it's hard to let them go. In fact, without some external perspective or change to bring them to our attention, we don't even notice that we're doing anything odd.

The puzzle is how to drive positive change. If my broken leg metaphor is right we need things like: a problem that highlights the cost of now unnecessary practices, recognition of why they were valuable and re-assessment of our current condition, recognition of our growth and trust that we won't backslide, and alternatives that we can practice that might also allow us to grow even further.

In the end an up to date set of development processes and techniques should make our day to day work more comfortable and effective, that's worth the effort.

Monday, July 12, 2010

Design Facilitation

We've a problem of implementations that simply doesn't match our expectations, not so much the low level detail but an over arching problem of coherence, poor quality design.

We dealt with lower level design by various means: testing and TDD, steady refactoring, and some good libraries (collections, list processing, dependency management, etc).

But at a higher level it can be hard to distinguish the business knowledge from the contingencies of implementation. At times it feels like we were incrementally feeling our way into the product, blindly sneaking forward without any wider view to order our route.

And we're talking a lot now about how to resolve that problem. We've got a number of new hires, and they are very good, so getting them to insist on thorough analysis and their best principles and not merely going along with the flow. But also to be more conscious about design, and align implementation to the way we talk about the product, and how the users talk about it, basic DDD stuff.

We talk about taking time to design, having some sort of map, rather than just hacking with a machete down the path of least resistance.

Also we have a few guys who really want to improve their design skills, who want to make that a key part of their career. I was talking with one about how to get him involved with these things and one thing that occurs is to have someone involved in design discussions who is more responsible for facilitating the discussion than for trying develop the design.

Or maybe that's just a bit over the top.

Saturday, May 15, 2010

Kanban, TDD, and Iterative Development

Where I work we make a lot of use of Kanban type systems. It's a key tool in our process and has helped us manage bottlenecks and make activity more visible.

We also favour Test Driven Development which encourages a very fine scale of iteration, and we get benefits from that.

The red green refactor cycle of TDD is pretty good at tuning interfaces and driving looser coupling between modules. Our Kanban process is pretty good at finding bottlenecks in our delivery process and helping us reallocate resources or use our time differently.

Recently we've had some serious production bugs, relatively new code (six to nine months old) being extended with new functionality is causing us grief. Whatever we did in our initial implementation we were missing the secret sauce, we implemented a design that is now hard to fathom, and work in the area is blowing out well beyond initial estimates.

Our designs don't make ongoing use of the code easy.

We were treating Kanban + TDD as our iterative process and we hoped we would get design improvements from our iterative processes, but we weren't really. At the scale of design and iteration of design, neither Kanban nor TDD have given us what we want.

The design bit in TDD is a bit of a lie, it's the hope that if you pay attention to the little stuff then the big stuff will look after itself. Evolution can be a cruel and terribly inefficient process, and there's no guarantee about the future fitness of it's outcome. A program emerges from TDD and satisfies the tests but nothing more is guaranteed.

Moreover, it seems to me that Kanban works against constructive iteration. Of course a larger feature may be broken down as a series of tasks on a Kanban board, and that will be an iteration of sorts, but the motive for task breakdown in Kanban is managing and coordinating resources; creating opportunity to revise earlier decisions is not really Kanban.

Fundamentally, Kanban is monolithic within it's model of a single task. If any iteration is visible within the task cycle you see tokens being pushed back, and anyway you look at that it's negative: it's regression, it's a bug, it's something being rejected. If I take iteration to mean go back and repeat, then from a Kanban perspective iteration within the cycle is a sign of problems.

I feel we're missing something between TDD and Kanban, or parallel to them, that promotes giving attention to higher level code quality issues, quality design and integration, and implementations that align with domain models. Particularly we're missing anything that promotes iteration over higher level design decisions. I'm reluctant to just propose adding more baroque details to TDD or Kanban because any success had with those version is likely to be confused with the basic practice, and when people are weary or don't understand the elaborations they'll slip back to the default practice. I look for methods that support me in my weakest moments, rather than methods that require me to be on top of my game in every moment.

Around the basic development activity (which for us is TDD) I'd like to explicitly promote fine grained analysis and design, review of progress with all sorts of relevant parties, and refactoring to explicitly improve design. Something that encourages multiple iterations for any given task, reinforcing that for any given task a single design step is probably not enough, something that highlights the positive cycle of reconsidering design. Something that revisits the integration of units and their interactions and not focusing just on requirements satisfying interfaces. Something that rewards the developer for going back and having another look.

These concerns are sometimes at a higher level than a naive TDD. I'm concerned with how the collection of objects and services fits together, the coherence of the system as a whole, thinking about future ease of work, clean mapping between implementation and our understanding of the business domain. But at times it's low level but invisible to TDD, it's behind the interface. With TDD alone a thing can be tested and correct and yet have an utterly impenetrable implementation from the perspective of future development.

What might the solution look like? I don't know just yet, perhaps a check list with multiple columns, each column is a fine design iteration, start the next column if you have to put a cross in the box you've already ticked, and then treat getting into more columns as a good thing. I'll be trying out something like that and see where it takes me.

P.S. I know I've presented a very naive view of TDD, but the name itself suggest a simple interpretation. The name TDD gives no clue that you should look for more subtle and nuanced practices even though that's what Beck described in his books.

Tuesday, May 4, 2010

Test Driven Development and Code Quality

I've been digging into some hairy code the last couple of weeks. It's new code, so there's a lot of testing around it, but it's complex, partly of necessity because it is a hard part of our business, but it seems harder than it needs to be. One of the things I've noticed is that no matter how good or bad it is the tests are better.

Tests are an extra layer sketching out a single scenario over the production code, so they are both more abstract and more specific. Any given test isn't trying to generalise all the scenarios and behaviours of the code being tested, in that sense they are more specific. Also they are at least an extra layer of abstraction over the production code.

It may also be important that tests normally form a topmost layer with no distorting pressure from their caller. By way of contrast consider a web application, the code has a very concrete front end and back end, typically the HTTP interface and a database. Wedged between those things is our poor beleaguered layer of abstracted business logic, all too easily squeezed and distorted by both upstream and downstream pressures.

Production code, the actual program, must encompass all the possible uses, in that sense it must be generalised. I hold that generalisation is next to optimisation as a source of complexity and confusion in code. Any developer looking at a generalised piece of code must assume that it supposed to be able to do all the things that it does: being more specific reduces the things the developer has to take into account when working; needless or accidental generalisation forces the developer to think about things that are irrelevant or unnecessary.

It seems the pressures on production code drive it to be concrete and general, while clarity seems to be served by being abstract and specific, there are two axes here and I think we get them mixed up at times.

And we have a model of abstract and specific code sitting right beside the production code: the tests! But it seems that the quality of those tests isn't feeding back into the production code in a way that makes the production code easier to understand or maintain. The good practice of testing is helping to produce correct code, but not necessarily sustainable code.

But we may have some pieces of the puzzle, some seeds of solutions:

  • Wrap general collections so that there is clearly documented set of operations that hopefully communicate the business meaning of interacting the with structure. Isolate and limit misleading generalisation.

  • Perhaps we need principles such that a test should never use domain vocabulary that isn't also in the code. If the test wants to interact with something, then the name should be coming from the code, if the name is not there then the code is missing an abstraction.

  • Similarly, if the test is creating a composite object, then perhaps that represents an abstraction that is missing from the code. And if the test is wrapping an action or a series of steps in a named function then perhaps that represents something missing from the code.

  • Make your implementation match the way you talk about the business: if your intuition or knowledge of the domain tells you something and the code contradicts that expectation, unless the expectation is actually wrong, then the implementation is bad, because being arbitrarily different from expectation requires additional intellectual effort to work with. (That last one is an insight from Domain Driven Design, emphasising ubiquitous language and consistent with models expressed in code.)