Thursday, August 27, 2009

Thinking about levels of Risk and Releases

One aspect of releases in the real world that seems to get little mention is the reality of working on more than one of them at a time. Many of us work in shops with a main branch and then multiple named releases hanging from various places off of the main branch. This is standard.

So, the inconvenient truth is that when I make a fix in release 2.3 it probably needs to also go in release 2.3.1, release 2.4 release 3.0 and "main" (whatever you call your root branch). I tend to think of those as pass through integrations. Someone made a fix in a past release, likely for a real customer (so it was done quickly) and then wanted to make sure it got propagated to all of the other releases that might correspond to any existing or future customers.

Now, the awkward question is: do you think the developer ran a full regression test on every release they checked the code into? Officially we all probably say yes, but in reality the answer is "I made sure it compiles" if you're lucky.

Depending on your branching strategy and the level of automation of your regression tests you may not need all builds deployable at all times or you may simply not be able to afford it even if you want it. If your tests are not automated it may simply be too expensive for each checkin to cost a developer-day of regression testing.

So, the two key questions are: how much risk do you have and how much can you afford?

A proposed approach to dealing with these questions is as follows. Each time a developer makes a check in to a branch the risk that the branch has destabilized has increased.by a quantum. Each time QA runs the regression suite the risk is reduced. Make the invalid but simplifying assumptions that all checkins add a constant risk and the regression suite reduces the risk to zero.

Imagine a graphical tool that shows a graph of the accumulated risk associated with each branch. This would allow you to answer the question of how much risk you have with each branch. Put another way, it lets you know how much deferred work would be required should any particular branch need to be released.

The Atlassian tool suite might already do this but I suspect that any development shop with a code management system and decent script writer could create a system for building such graphs.

Once you have the ability to measure your risk you can make an informed decision about how much to accept. You might decide that some branches should be regression tested everytime they’ve accumulated x checkins, while other branches only need testing every y checkins. The point is that you get to decide.

Tuesday, August 18, 2009

Spark: The revolutionary new science of exercise and the Brain. John Ratey, MD

This book is strongly recommended reading for anyone who uses their brain.

This book starts with the assertion that conditioning the body is just a side effect of exercise, and the real benefit is the changes that exercise causes in and on the brain. Many people have made the case that our current lifestyle doesn't match the conditions we evolved for, but the assumption is that too many caleries and not enough running only effects the heart and lungs. Ratey argues that part of what we evolved for was hunting and gathering that required speed, stamina, cunning and fine motor control. We lose all of that when we're sedentary.

In a Chicago High School gym class was transformed into exercise class, with emphasis on effort as measured by heart rate monitors. This allowed even unfit kids to succeed, by working enough to elevate their heart rate. This school's obesity rate is now 3% compared to the national average of 30%. They are also setting records for academic performance.

This book has a lot of chemical names in it and one of the most important is brain-derived neurotrophic factor (BDNF). BDNF causes an increase in synaptic connections which is how we learn. This molecule is produced in the hippocampus and studies show the exercise causes an increase in its production. Ratey calls BDNF "miracle grow" for the brain. BDNF doesn't make you smarter but it creates an environment conducive to forming new connections. So, if exercise and then learn something its easier for the brain to encode the learning.

We used to think that we were born with all the brain cells we'd ever get, but we now know the neurogenisis occurs all the time. New neurons are born and then have 28 days to get connected into a network. If they don't they die. Exercise increases both the rate of nerurogenisis, and the ability of new neurons to make the connections they need to survive.

Ratey goes on to say that the benefit can be enhanced by combining aerobic exercise with complex activity such as Tai Chi. "The more complex the motions, the more complex the synaptic connections". Although these neurons are associated with exercise they can be recruited for other tasks (such as learning)

When under stress we produce cortisol which tells the hippocampus to selectively process data and memories (so as to focus on the stressor). While beneficial in the short term, in the long run it actually causes the non-stressor related nerves to degenerate and lose connections. This makes it harder for the non-stressor memories to be accessed, which can lead to more perceived stress. Exercise on the other hand causes a reduction in the production of cortisol, which can break the cycle.

The book does get somewhat repetitive as it explains the positive effect exercise has on the neurochemistry of anxiety, depression, ADHD, addition and aging. On the other hand you really can’t fault the author for providing so much good news. Each of these conditions represents a deviation from normal brain chemistry and exercise is a strong force for bringing the brain back into its normal condition of plasticity. Which is to say, that our brains were designed to be adaptable and exercise creates the right chemical soup for the brain to swim in to enhance the flexibility.

Wednesday, August 5, 2009

Another class of Bugs that Are Hard to Unit Test

Imagine the set of classes listed at the bottom of this post. The point is that we're performing a calculation using an expensive test, but when using a derived class we can make use of an inexpensive test. Both tests will give the same result but clearly its better to use the inexpensive test if we can. How can we write a unit test that verifies that we've used the inexpensive test (and used it correctly)?

Several possibilities come to mind. One option is to write a unit test that times the call to the method. This is pretty clearly not a good idea as it leaves the results hostage to the load on test system.

Other approach is to embed a flag in the class that gets raised when the expensiveTest method is called. That's not a terrible idea but it does put test code in the actual production code. A slightly better idea is to create a test class that extends BaseThing and implements the flag in its own version of expensiveTest.

@Test testQuickReject() {
DerivedClass foo = new DerivedClass () {
boolean flag = false;
boolean expensiveTest() {
flag = true;
return super.expensiveTest();
}
};

foo.pickBestThing();
assert(flag == true);
}

This leads to an observation about the effect of testing on code design. The approach being taken only works if the expensive test is a separate and thus overridable method. If the code representing the expensive test was just a bunch of inline code then there would be nothing for the test class to override. One could certainly go back and to a Refactor:ExtractMethod on the code, but if the class had been designed with testing in mind from the start that would not be necessary.


class BaseThing {
Thing pickBestThing(List thingList) {
Thing bestThing = null;

for(Thing thing : thingList) {
if(quickReject(thing)
continue;

if(expensiveTest(thing))
bestThing = thing;
}
return(thing);
}

boolean quickReject(Thing thing) {
return false; // no-op method
}
}

class DerivedClass extends BaseThing {
boolean quickReject(Thing thing) {
// some real test that only applies in the derived class case
}
}