Start doing sprint reviews, not demos

There’s a misunderstanding I’ve noticed in quite a few Scrum projects. Teams use Scrum and at the end of their sprint they do what they call a “sprint demo”. It works out like this:

  • They demonstrate their increment of the product to the product owner
  • Product owner seats passively
  • Product owner accepts or rejects the increment
  • No modification is done to the product backlog
  • After a while the product owner is unhappy with the current state of the product and the progress being made

There is nothing like a sprint demo in Scrum. But there is a sprint review.

Scrum is empirical, meaning that there are inspect and adapt points along the way. The sprint review is the inspect and adapt point where the product increment, the most current product backlog and the current conditions are for inspection. The adaptation is the modified product backlog.

During the Sprint Review, the Scrum Team and the stakeholders collaborate about what was just done during the sprint and what are the things that could be done during the next sprints. The presentation of the product increment is not the goal. It is used to understand what should be the next sprint goal. The sprint review provides input to the next sprint planning meetings. One of the possible consequences of this evaluation is the decision to not proceed further with the development of the product. Another possible consequence is the decision to release the existing product.

Consider your next sprint review. If you get out of the meeting without a modification to your product backlog and an insight to your next sprint goal, it’s probably because you’re not inspecting and adapting. You might be suffering from the “sprint demo” syndrome.

Asking powerful questions to hire right

Many organizations spend a significant amount of time defining the experience, education, skills and other factors required for open positions. Most of the time, though, their hiring process fails to make sure they hire right. If you’re looking for a different way, you might be interested to know how it works at Pyxis.

This morning I sat down with François to prepare the first hiring interview of a candidate looking for a software developer position. We decided to not have a look at his resume just yet. Since one of our colleague had recommended the applicant, we trusted he was a good candidate and wanted to understand if there was a cultural fit.

We talked about the values, characteristics and behavioral traits we wanted to find in a potential colleague. We figured out we wanted someone who shared the following characteristics:

  • Accountable
  • Humble
  • Passionate
  • Intelligent
  • Team-oriented
  • Continuous improvement orientated

We then devised a series of powerful questions to help us figure out if the applicant was someone we wanted to work with. Below are some of the questions we used during the interview process. Keep in mind that those questions are no more than tools we used to orchestrate the conversation. How we frame the questions is decisive. To make sure we hire right, the questions have to be ambiguous, personal, and stressful:

  • Tell me why it is more important for you to be having this conversation with us, rather than being doing something else?
  • What are the things you hear yourself most often complain about in your current (or last) position?
  • What have you done to change the very things you complain about?
  • What would be an extraordinary accomplishment for you?
  • What is the greatest contribution that you plan to make to the organization?
  • What will you hold the organization accountable for?

With answers from the candidate to questions such as these, we now have a pretty good idea whether the candidate is a good fit for our organization or not.

The next step is to validate the technical skills of the candidate. I know of no other way to validate the skills of a developer than to orchestrate a conversation around code. So we will give the candidate an opportunity to show us what kind of developer he is through his code. But that’s another story.

Making good use of assertion messages in tests

Have you ever struggle with coming up with useful assertion messages in your tests? Well, I have; until not so long ago.

I remember when I started using JUnit. It was summer 2000, and I became addicted to writing tests first. I was already writing automated tests at that time, inspired by my lecture of Thinking In Java. Not using any automated unit test framework, I was writing my tests last in the good old main() function. I had to inspect the outcome of my tests for correctness each time I run them, and that was painful.

JUnit was a discovery that changed the way I programmed and approached software development forever. Central to xUnit frameworks is the notion of making the tests self-checking by using assertion methods, basically utility methods that evaluate whether an expected outcome has been achieved. Now I had a way to express the expected outcome, let the computer check it for me, and produce a useful message for me (and others) - the human readers - to help diagnose problems. One of the goals of automating tests is to use tests as documentation. In case of test failure, what you want is for the test to act as a tracer bullet, helping you understand very quickly what the problem is. Assertion messages play a role in this. A well-crafted assertion message makes it clear which assertion fails and what the symptoms of the problem are. Now that’s easy to say, the hard part is to figure out what the message should say.

As Nat Pryce and Steve Freeman explain in their book, during the test-driven cycle, after you make the test fail, take a moment to read the assertion message, ask yourself what the reader will get out of it, and adjust to make the diagnostics clear. They provide a number of best practices in the book to help with test diagnostics. One is about making the assertion messages explanatory.

I have long been in the school of thought that strives to have a single assertion per test method and as result, for a long time I felt no need to use assertion messages. If you use small and focused tests and name them well, the tests will tell you most of what you need to know to diagnose the problem. For example, when the following test fails:

calculatesGrandTotal() {
    String[] prices = { "50", "75.50", "12.75" };
    BigDecimal expectedTotal = new BigDecimal("138.25");

    for (String price : prices) {
        cart.add(anItem().priced(price).build());
    }
    assertThat(cart.getGrandTotal(), equalTo(expectedTotal));
}

the failure report can be considered enough to understand what the problem is:

Expected: <138.25>
     but: was <139.25>
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:8)
	at test.com.pyxis.petstore.domain.order.CartTest.calculatesGrandTotal(CartTest.java:53)
	...

Indeed, the name of the test tells us that the cart fails to calculate its grand total correctly. The assertion messages shows the difference between the expected and actual outcome. Yet we can make the diagnostics even easier for the reader by simply adding an assertion message to identify the value being asserted:

    assertThat("grand total", cart.getGrandTotal(), equalTo(expectedTotal));

See how that helps:

grand total 
Expected: <138.25>
     but: was <139.25>

I’ve come to adopt that practice since reading Pryce and Freeman’s book. Assertion messages are not used as often as they should. They can really help make failure reports more helpful, whether you have a single or multiple assertions in your test.

Another help in making assertion reports clearer comes from using Hamcrest matchers and assertThat(). Consider the following example:

findsItemsByNumber() throws Exception {
    havingPersisted(product);
    havingPersisted(anItem().of(product).withNumber("12345678"));

    Item found = itemInventory.find(new ItemNumber("12345678"));
    assertThat("available inventory", found, itemWithNumber("12345678"));
}

In case of failure, here is what we get:

java.lang.AssertionError: available inventory
Expected: an item with number "12345678"
     but: number was "87654321"
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:8)
	at test.integration.com.pyxis.petstore.persistence.PersistentItemInventoryTest.findsItemsByNumber(PersistentItemInventoryTest.java:56)

which I hope make the point.

As Pryce and Freeman say, diagnostics are a first-class feature. I now do my best to make assertion messages helpful so that whoever has to change the code in the future will understand what the expected behavior is.

Change is the job of the ScrumMaster

I recently finished reading Community, the Structure of Belonging, by Peter Block. While reading the book, I could not help but draw parallels with what I teach about Scrum.

In his book, Block talks about the small group as the unit of transformation. I already wrote about Scrum as a tool to foment change. In the context of Scrum, the Scrum Team is that small group, that unit of transformation. It changes the context of work in a way that alters how we think, behave and thus work together, which in turn can radically improve performance. François shared a similar view recently on Scrum teams, compelling goals and performance.

Block also states that for real transformation to occur, it is not more or better leaders that we need, but more care and belonging. I’m often asked what role management plays in Scrum. People ask the question : “So in Scrum, that means we don’t need managers?”. Mike cohn has already addressed part of that question in his last book, Succeeding with Agile, by saying that management has a key role to play by creating the right environment for self-organization to occur. To add to that idea, we can say that management task is to shift the context to create in the Team a sense of belonging and ownership. Engagement from the Team will come from care so we must be careful to choose care over speed or scale.

Scale, speed, and practicality are always the coded arguments for keeping the existing system in place.

Scrum is all about changing the existing system. This is why the ScrumMaster primary responsibility is to ensure the Scrum process is followed. He or she uses Scrum as a tool to ask powerful questions to spark off change.

The ScrumMaster job is to change the conversation. The ScrumMaster is initiator, facilitator and leader of real discussions. This is a leadership role about creating commitment and accountability, which are the building blocks of self-organization.

To make a difference we must start by naming the way things are, use powerful questions as a tool, and listen, rather than advocate or defend.

ScrumMasters, take the time to master the art of orchestrating conversations, for this is what your job is about.

Scrum is not about project management

I often hear people talk about Scrum as a project management framework. I don’t like that idea. Scrum is for managing complexity to provoke change. I don’t like the notion of Agile Project Management either. Nor do I believe in the Agile Project Manager.

Our conventional approach is to solve problems. In fact we love problems and we take pride in devising complex solutions. We operate on a belief that we can make a real difference with more or better problem-solving. It’s only natural then, that we think of products in terms of projects (project resonate with problem to be solved) and that we see Scrum as another approach to manage projects (hopefully a better one). In my experience, product developments that use Scrum as a project management approach rather than a tool to ask powerful questions to spark off change, do not make a real difference. They only manage to project the past into the future.

Scrum is about the art of the possible. It is when we start thinking in terms of products, people and possibilities that real change is possible. To make a meaningful difference in our software development efforts we have to embrace uncertainty (and the anxiety that comes along) and tap into the possibilities it brings. We have to acknowledge that we don’t have all the answers, and that we’re going to explore how to best meet our goals and that of our users.

Product vision and Strategic Design

When I coach Scrum teams, I usually spend a significant amount of time working with the Product Owner to teach her how to create an effective product vision, one that will act as a guiding light for the Scrum team and stakeholders.

I don’t want to delve into the details of product envisioning activities. I usually use a number of workshops and techniques to produce some (or all) of the following artifacts:

  • Elevator statement
  • Product box
  • Business goals
  • User goals
  • Value drivers
  • Key attributes and capabilities
  • Risk assessments
  • etc.

What I’m interested in talking about is the activity of defining the product quality attributes and how strategic design (in the Domain Driven Design sense) fits in. Thinking about the quality attributes is key to defining “done”. Product quality attributes give meaning to the product being potentially shippable.

Among the quality attributes, some will pertain to the external quality of the product, like usability, securability or availability for instance. Others will focus on the internal quality. This is the case of maintainability, modularity, evolvability, etc.

Yesterday, I was out for beers with my colleague Ernst Perpignand and Greg Young. Greg was in Montreal to give his Domain Driven course and we (at Pyxis) sponsored the event by hosting the course in our Laval office. Of course during the evening we talked about CQRS, Event Sourcing, write and read models and all the stuff Greg likes to talk about. Needless to say I had a great time.

Those discussions made me realize how important it is to communicate that context maps are strategic outputs of the product vision activities. There are good reasons why Eric Evans talks about strategic design when he discusses bounded contexts and distillation. He refers to the conceptual core of the system as the “vision” of the system. It is indeed the guiding vision for internal quality attributes of the product. Determining our own bounded context and its relationships to others is both a political and business decision. We need to understand where the potential for ROI lies to design and architecture in consequence.

Imposterizing checked exceptions

I have been frustrated many times by some of Java’s checked exceptions. I’m sure you can recall some of your own experiences where you wished a CheckedException was a RuntimeException instead.

Let me share a useful exception idiom that I’ve been using for a long time and on lots of projects, which I call the ExceptionImposter.

We all know the Java standard libraries use checked exceptions to signal problems that can only occur at runtime either if we have screwed up or if something really bad has happened - something we cannot do anything about anyway. Personally, since I use Java reflection capabilities a lot, I have to deal with the InstantiationException and the IllegalAccessException too often for my taste.

For example, whenever I want to dynamically instanciate a class, even if I know the code should never fail, the underlying Java code throws a checked exception and I end up with code that looks like this:

        ...
        try {
            return WebDriverFactory.class.cast(webDriverFactoryClass.newInstance());
        } catch (Exception shouldNeverOccur) {
            // now what?
        }    
        ...

If I know that the webDriverFactoryClass has a public no-arg constructor that does nothing, the code cannot fail. If it fails, it could be that something is wrong with my environment or that I made an error while programming the class. That’s something I will catch during my development cycle but that I don’t expect to occur afterwards. I cannot just ignore the exception, because if something really went wrong, the application will not behave correctly and I will have a hard time diagnosing the failure and finding the source of the error.

I know I must throw another exception and I want that exception to be a runtime exception because there’s nothing useful the calling code can do about the error. I can wrap the exception into some generic RuntimeException but that’s not very satisfying. It will not help me find the error and it will add another level of nesting and make the stack trace harder to read. I find we already have too many levels of nested exceptions which make framework exceptions very hard to read.

I could also write my own wrapper to rethrow a checked exception. It think it would be slightly better, since I can now name the error, but what still annoys me is that other level of nesting and the longer stack trace.

Instead, I use the ExceptionImposter to mimic the checked exception and turn it into a runtime exception:

    ...
    try {
        return WebDriverFactory.class.cast(webDriverFactoryClass.newInstance());
    } catch (Exception shouldNeverOccur) {
        ExceptionImposter.imposterize(shouldNeverOccur);
    }    
    ...

Here’s how it works:

    public class ExceptionImposter extends RuntimeException {
        private final Exception imposterized;
      
        public static RuntimeException imposterize(Exception e) {
            if (e instanceof RuntimeException) return (RuntimeException) e;
        
            return new ExceptionImposter(e);
        }
      
        public ExceptionImposter(Exception e) {
            super(e.getMessage(), e.getCause());
            imposterized = e;
            setStackTrace(e.getStackTrace());
        }
      
        public Exception getRealException() {
            return imposterized;
        }
      
        public String toString() {
            return imposterized.toString();
        }
    }

As you can see from the following test case, the ExceptionImposter looks like the real exception as much as possible:

    public class ExceptionImposterTest {
        private Exception realException;
    
        @Test
        public void leavesUncheckedExceptionsUnchanged() {
            realException = new RuntimeException();
            assertSame(realException, ExceptionImposter.imposterize(realException));
        }
    
        @Test
        public void imposterizesCheckedExceptionsAndKeepsAReference() {
            realException = new Exception();
            RuntimeException imposter = ExceptionImposter.imposterize(realException);
            assertTrue(imposter instanceof ExceptionImposter);
            assertSame(realException, ((ExceptionImposter) imposter).getRealException());
        }
    
        @Test
        public void mimicsImposterizedExceptionToStringOutput() {
            realException = new Exception("Detail message");
            RuntimeException imposter = ExceptionImposter.imposterize(realException);
            assertEquals(realException.toString(), imposter.toString());
        }
    
        @Test
        public void copiesImposterizedExceptionStackTrace() {
            realException = new Exception("Detail message");
            realException.fillInStackTrace();
            RuntimeException imposter = ExceptionImposter.imposterize(realException);
            assertArrayEquals(realException.getStackTrace(), imposter.getStackTrace());
        }
    
        @Test
        public void mimicsImposterizedExceptionStackTraceOutput() {
            realException = new Exception("Detail message");
            realException.fillInStackTrace();
            RuntimeException imposter = ExceptionImposter.imposterize(realException);
            assertEquals(stackTraceOf(realException), stackTraceOf(imposter));
        }
    
        private String stackTraceOf(Exception exception) {
            StringWriter capture = new StringWriter();
            exception.printStackTrace(new PrintWriter(capture));
            capture.flush();
            return capture.toString();
        }
    }