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.
TDD Masterclass
I recently attended a two day TDD training course by Jason Gorman.
Although we practise TDD on a daily basis, I was interested to see if we are applying all the practises correctly or see if we are missing out on anything.
The Jason presented what he called the baker’s dozen of TDD practises.
- Write a failing test
- Write the assertion first
- Don’t refactor with a failing test
- Isolate tests from each other
- See the test fail
- Triangulate
- Organise tests to reflect model code
- Write the simplest code to pass the test
- Choose meaningful names
- Test one thing in each test method
- Refactor to remove duplication
- Keep test and model code separate
- Maintain your tests
The great way Jason reinforced these practises was to apply another great agile practise, pair programming. In pairs we applied TDD to solve various programming problems, e.g. generating Fibonacci numbers, FizzBuzz, etc. Solving these problems in pairs was the most enjoyable aspect of the course. By enforcing frequent pair rotation I met a lot of nice people plus I even got a taste of TDDing in C#!
So what did I find in the end? We are doing pretty well at youDevise. We nearly apply all the practises (we can probably do triangulation more). Overall, I think the course is a good introduction to TDD, especially learning through pair programming.
Design Perfume - The sweet smells of quality
Bob Martin wrote about smells that are signs of bad designs. While it’s convenient to have a vocabulary to describe problems, that’s only part of the picture if we want better designs. With a vocabulary for good designs we can more easily identify the strengths in our work and build on them, and we can structure our thinking about the work of others and bring the good back to our own work.
Good design shouldn’t be terra incognita, let’s have some sign posts to guide us in the right direction.
So here are my corrollaries to Bob Martin’s design smells - Julian’s design perfume:
Supple - System is easy to change (not Rigid)
- Examples: adding new modules, alternate implementations, substitute technologies, additional processing, new functionality, clearer design and refactoring.
Resilient - Problems and their solutions are localised (not Fragile)
- Failures don’t bring whole systems down
- Failures don’t introduce bad data
- Bad data doesn’t propagate
- The consequences of failure make sense given the causes
- Technologies are used in obvious and limited scopes
- Dependencies on specific configurations are localised
- No significant effects come from accidental patterns of use
Re-usable - Fits in anywhere it might be useful (not Immobile)
- Any dependencies should make sense
- Configuration and maintenance should be proportionate:
- Easy to figure out
- Sensible defaults
- Updates should seem relevant and not a burden
- Reuse should add clarity:
- Making intention and correct use obvious
- Not introducing too much unused functionality
Enabling - Makes good practices easy (not Viscous)
- The right information and operations are available:
- Back doors are hard find or make
- It’s easy to find what you need
- Examples:
- Guice makes DI easier than calling new
- Collection-processing libraries lead to less error prone loops, and more explicit condition and operation objects
Appropriately Complex - Reasons for complexity are obvious (no Needless Complexity)
- Clearly connecting complexity to business needs, and thinking about the complexity reveals important things
- Complexity is visible up front:
- No nasty surprises when you start to dig in
- Complexity hiding shouldn’t produce time-bombs
DRY - Doesn’t require users to repeat themselves (no Needless Repetition)
- Good default values and reusable configurations
- Useful state and memory of previous actions
- No need to code up the same things repeatedly
Transparent - Good code is obvious and easy to understand (not Opaque)
- It does what it says it does
- It doesn’t do anything unexpected
- It matches reasonable expectations
Although these are in many places around the internet, and I do recommend reading Martins’ books, I’ll include his list of smells here:
- Rigidity - System is hard to change.
- Fragility - Changes cause the system to break easily and require other changes.
- Immobility - Difficult to disentangle components that can be reused in other systems.
- Viscosity - Doing things right is harder than doing things wrong.
- Needless Complexity - System contains infrastructure that has no direct benefit.
- Needless Repetition - Repeated structures that should have a single abstraction.
- Opacity - Code is hard to understand.
Shh! Performance Secrets
Here are some slides on Performance Secrets from a talk at Google OS Jam. Links from the slides:
Test Data Buildering: Take 2
After the great comments on the last post about the test data builders we kept poking and prodding to see what we could do. What we came up with looks like this now:
Code:
FundOfFund fohf = derivedFromA(new FundOfFundTemplate() {{ with(name, "Blah"); }});The structure isn’t really different, but the naming has changed. Instead of being called “builders” or “makers” we call them “templates". This was prompted by Antony Marcano’s comment about things being tailor made. At first we tried calling them “patterns” but realized that this would cause far too much confusion during discussions because of the whole design pattern domain. Name clashes can make a great idea very quickly turn bad.
We tried to keep on the tailoring idea for a bit, but decided to drop it in favor of deriving things from templates (with the path there being something like: pattern -> template pattern -> template -> create things based on a template).