You’re done when

Every team that I’ve worked with has eventually come to ask the question: when is it done? And they don’t just mean “done”, they mean, when is it “done-done“? Every time, for whatever reason, this seemingly innocuous question becomes a sticking point of a lot of conversations. Sometimes everyone can agree on what it takes, but there ends up being concerns about the feasibility of the definition in practice. Other times there is some dissent about the points of done related to what is actually in the team’s control. All of these are perfectly valid concerns. Some of those concerns point to larger, organization problems that are being faced and some point to fear of tackling what at first looks like a daunting mountain of change to reach “done-done” of knowing when you are “done-done”.

I’m not going to try to solve all of those issues that come up during those conversations, but I’m going to do my part to try to coax them on a little bit and put out what I’ve started using as my baseline definition of “done-done”.

You are done when:

  1. The code has been merged into all relevant branches.
  2. All automated tests pass on all affected branches.
  3. End user documentation about the change has been written.
  4. The feature or bug fix has been demoed to another member of the team, and that person has had the chance to explore the bug fix/feature and find any potential problems.
  5. Packages for all new releases of the product have been made.
  6. It has been confirmed by the customer to be what they wanted.

In essence it comes down to: the change is made (points 1 and 2), it has been communicated to others (points 3 and 5), it has been shown to be working (point 4), and it is what was actually wanted (point 6).

If you want to take it to the next level you can add another point to being “done-done”:

  1. The customer is using the change and is happy with it.
Posted in Software | Leave a comment

You’re Wrong and Can Never Be Convinced Otherwise

Are you a rational, logical person? When you are given the data about a situation, do you change your mind? I would be willing to bet that you like to think about yourself in that light. I know that I do.

However, I know that I still hold beliefs in spite of some data. If you give me some data about a situation, I’m not very likely to believe it. I might accept it, I might even agree with it, but I won’t likely change my thought process that you, as the presenter of that data, think I should be changing. Why is that?

While sitting around, drinking some tea, and eating some lovely, lovely bread with Jeffrey Fredrick, we had covered topics ranging from ease at work, the results of the Strengths Finder test, beliefs that people hold, estimation and tracking in software development, and the problems people have understanding estimates, when Jeff blurted out (paraphrasing), “I’d love to put together something about naive pitfalls. For instance in selling, a naive way of going about it is believing that people want to hear about your product. They don’t, they want to hear how you’ll solve their problem.”

This brought out a whole list of naive fallacies that we’ve both found that people hold.

Fallacy 1: Scheduling

Belief: The way to get more done is to schedule more stuff.

Reality: The way to get more done is to work on a single thing until it is finished.

Fallacy 2: Persuasion

Belief: The way to persuade people is to give them data.

Reality: The way to persuade people is to agree that they have a certain understanding of something, and then provide a story that leads from their understanding to your understanding.

Fallacy 3: Feedback

Belief: Feedback is something that is given to someone.

Reality: Feedback is something that the person has to seek out.

Fallacy 4: Experience

Belief: The longer you do your job the better you get at it.

Reality: The more you deliberately practice your job, the better you get at your job.

The commonality that I see in all of these is the belief that the data alone will change what belief a person holds. In all cases the person who holds the belief needs to be the one taking themselves down a path that leads them from one belief about the world to another belief.

Take the fallacy of experience as an example. Purely gathering experience is not what produces change. Instead, the change occurs when the person experiencing the situation reflects on what has been occurring, inspects their own belief structures, and finds ways to change themselves to produce different outcomes. The data pointed to the existence something around them, but the person had to find a path to changing their surroundings.

It may be true that I can’t convince you that you are wrong (oh, the irony of linking to an article about confirmation bias here). I can, however, provide you with a story that will maybe lead you down the path of understanding why I think you are wrong.

Posted in Software, Stories | 2 Comments

CITCON 2012 in Portland

CITCON is a conference that I’ve been attending since 2006 in London. I showed up because it was “near” where I was living in Germany and sounded like it might be interesting. Boy was it ever interesting! The only European one that I missed was because I had a final exam on the same day as the Brussels conference (damn you, formal methods!).

Well, now that I don’t live in Europe I probably won’t be making it to those CITCONs very much anymore, but there are still the North America ones. And what do you know? The next North America CITCON is right in my back yard here in Portland.

Having just moved to the city I didn’t know much about much of anything. Knowing that Portland has a lot of beer and breweries doesn’t do much when you are trying to find a space that fits a 100 people in an open-spaces style conference, but after some searching (and drinking a few McMenamins beers) we found a place. Next was to get the word out.

AgilePDX people there gave me a lot of advice. The RubyPDX group also get behind it. Various meetups that I crashed put up with my pitches to get people to show up, as well. I used my now tenuous connections to some groups in Seattle to get the word out up there a little.

In doing this I meet a lot of great people and groups that I’m planning on keeping in touch with. One is AgilePDX here in Portland who is going to be hosting another conference that meshes very well with CITCON: Agile Testing Open NW. If you can’t make CITCON, or if you find CITCON great, then I think you’ll probably want to go to ATONW, too.

Another group that I got in contact with, for those of you up in Seattle, is the The Center of the Agile Universe (aka The Fremont Agile Meetup). They are also putting on a very interesting sounding conference, that once again, if you like CITCON or miss it, I think you should attend MAQCon.

I’m really looking forward to this CITCON and hope to meet yet more people in the area.

Posted in Uncategorized | Leave a comment

Leaving of London

Looking back on the last five years of my life spent living abroad it is easy for me to dwell on the things that didn’t work out well. That is just the way I seem to be. I’d like to try to spend a little time dwelling a bit on the things that really worked out well, though, because once I start listing them out it will become apparent to me just how many there are. And in the end the events that were good really outweigh the problems that came up.

I learned a lot. Really. All in all, this time has been the most jam-packed learning of my life. In no particular order, I learned:

  • to speak German. I could speak it ok before I moved there, but living in the country took it to a different level.
  • various forms of formal analysis. Like many things learned at university you won’t be applying it every day, but it completely changes the way you see the world.
  • how to give a five minute talk on just about any subject with just a few minutes of preparation.
  • how relational database systems work. I missed out on this in my undergraduate degree, and boy, was it an eye opener.
  • how to be (a little bit) comfortable not speaking the language. From being out of my depth with German to taking trips in countries where I don’t speak a lick of the language to not understanding someone’s accent, I’ve learned that it isn’t the end of the world and it’ll work out in the end.
  • to think about my communication style and how it impacts those around me.
  • that it is really hard to overdo it on refactoring and unit testing.
  • the fragility of a team.
  • that constant improvement is hard but pays off in spades.
  • the cooperative learning/teaching potential of a group of like minded people meeting every Tuesday for a beer and a chat.
  • that being a stranger in a strange land can feel like you have no connection to those around you.
  • that different cats, even sisters who have spent their entire lives together, will end up with completely different personalities.
  • that ruthlessly tracking down the real cause of an error can take you to very uncomfortable places that you probably wouldn’t expect to find yourself at the outset.
  • how many amazing people there are in London to learn from.
  • to understand people with different accents (this was a point of hilarity many times).
  • to concentrate on the journey and the destination will work itself out.

As well as learning things I also got to know a lot of people. Each one of them ended up being a great friend and helped my wife and I feel like we belonged where ever we were.

I got to do a bit of traveling. Ate some tasty pies in the Lake District. Found hops growing wild on the bank of the Rhein river. Crossed the channel on an overnight ferry (luxury!). Cycled along Loch Ness on a beautifully sunny day. Climbed a rock face in the French Alps (and then ran away when a storm came in and a rock fell a short distance away). Visited Neuschwanstein when it was shrouded in mist which revealed the castle now and then in its fairy tale splendor. Discovered that Marrakech is an amazing oasis of bustling activity on the edge of the desert.

When I left the United States I was a cocky, young programmer who felt that he had the solution to most, if not all, of the problems in software development. Now that I’ve returned to the United States I think that I’m a somewhat less cocky, not-quite-so-young programmer who feels that he has something of value to add, but has learned that he doesn’t know a lot about a lot of things. For helping me start that transformation I am eternally grateful and indebted to all of my Mitstudenten and Professoren from my degree program (every time I walk into an Apple store I think, “my god, I studied with the guy that wrote one of the programs running on all of these iPads!”). Then for giving me the feedback, space, and incentives to continue down this path there is everyone that I met at XTC and everyone I had the privilege and pleasure to work and learn with at TIM Group. If you ever find yourself in London try to make time for some beers with the XTC crowd and drop the guys at TIM Group a line and see if you can join them for a day, you won’t be disappointed.

Posted in Stories | 3 Comments

An exercise in refactoring

The other day at work I was implementing a new feature and found myself in a nice little, self-contained bit of code. I was adding the ability to parse a pair of query parameters from an HTTP request into an optional Period (a class used to represent a period of time in the application). After TDDing my functionality into place I reached a fully functional version but cringed every time I looked at it. I normally try to refactor during the TDDing but I just couldn’t see what to do until I had a bit more meat to work with. Once it was all in place I had enough to start chopping away at it.

The code you see below is pretty much the code as it is in the application. I’ve changed a few of the types to remove references to internal structures, but I haven’t changed the logic. It makes use of a modified version of Nat Pryce’s Maybe class and uses Guava, which shows up in some of the interfaces that get implemented.

So this is where we start:


public static Maybe<Period> optionalPeriod(Maybe<String> from, final String fromParamName, Maybe<String> to, final String toParamName) {
  final Maybe<Date> fromDate = optionalDate(from, fromParamName);
  final Maybe<Date> toDate = optionalDate(to, toParamName);

  if (fromDate.isKnown() && toDate.isEmpty()) {
    throw badRequestError(invalidPeriodError(INCOMPLETE_STARTED_PERIOD_MESSAGE, fromParamName, toParamName, from.otherwise(MISSING_VALUE), to.otherwise(MISSING_VALUE)));
  }

  if (fromDate.isEmpty() && toDate.isKnown()) {
    throw badRequestError(invalidPeriodError(INCOMPLETE_ENDED_PERIOD_MESSAGE, fromParamName, toParamName, from.otherwise(MISSING_VALUE), to.otherwise(MISSING_VALUE)));
  }

  return fromDate.bind(new Function<Date, Maybe<? extends Period>>() {
    @Override public Maybe<Period> apply(final Date from) {
      return toDate.transform(new Function<Date, Period>() {
        @Override public Period apply(Date to) {
          Period period = new Period(from, to);
          assertPeriodInOrder(period, fromParamName, toParamName);
          return period;
        }
      });
    }
  });
}

The first few lines seemed alright. They fairly clearly state that the two parameters needed to be considered optional dates and followed up with some lines declaring that if we have one but not the other then there is an error. The last statement was a horrible monstrosity, though. That said, my first victim was something that can’t even be easily seen in the above code: if neither date is there then the function should return nothing(). After that I took a swipe at the little bit of duplication that I felt was showing up in the two if statements about one or the other date being missing:


public static Maybe<Period> optionalPeriod(Maybe<String> from, final String fromParamName, Maybe<String> to, final String toParamName) {
  final Maybe<Date> fromDate = optionalDate(from, fromParamName);
  final Maybe<Date> toDate = optionalDate(to, toParamName);

  if (fromDate.isEmpty() && toDate.isEmpty()) {
    return Maybe.nothing();
  }

  if (fromDate.isKnown() && toDate.isEmpty() || fromDate.isEmpty() && toDate.isKnown()) {
    String message = fromDate.isKnown() ? INCOMPLETE_STARTED_PERIOD_MESSAGE : INCOMPLETE_ENDED_PERIOD_MESSAGE;
    throw badRequestError(invalidPeriodError(message, fromParamName, toParamName, from.otherwise(MISSING_VALUE), to.otherwise(MISSING_VALUE)));
  }

  return fromDate.bind(new Function<Date, Maybe<? extends Period>>() {
    @Override public Maybe<Period> apply(final Date from) {
      return toDate.transform(new Function<Date, Period>() {
        @Override public Period apply(Date to) {
          Period period = new Period(from, to);
          assertPeriodInOrder(period, fromParamName, toParamName);
          return period;
        }
      });
    }
  });
}

At this point I was pretty happy with the first few lines of the function. But that error checking was now an even worse eyesore! Working through the logic I realized that the way it was now structured I should never reach the final, eye-bleed inducing final return statement unless both dates are specified. That bit of information means that all of that obscure bind() and transform() logic is completely unnecessary. The second breakthrough was in noticing that if either of fromDate or toDate end up being empty after the check that neither is known then it is an error.

With those two new realizations I could get rid of bind() and transform() and replace it with just getting the values out of the Maybes or throwing the appropriate exception.


public static Maybe<Period> optionalPeriod(final Maybe<String> from, final String fromParamName, final Maybe<String> to, final String toParamName) {
  final Maybe<Date> fromDate = optionalDate(from, fromParamName);
  final Maybe<Date> toDate = optionalDate(to, toParamName);

  if (fromDate.isEmpty() && toDate.isEmpty()) {
    return Maybe.nothing();
  }

  Supplier<WebApplicationException> missingStartDate = new Supplier<WebApplicationException>() {
    @Override public WebApplicationException get() {
      return badRequestError(invalidPeriodError(INCOMPLETE_ENDED_PERIOD_MESSAGE, fromParamName, toParamName, from.otherwise(MISSING_VALUE), to.otherwise(MISSING_VALUE)));
    }
  };

  Supplier<WebApplicationException> missingEndDate = new Supplier<WebApplicationException>() {
    @Override public WebApplicationException get() {
      return badRequestError(invalidPeriodError(INCOMPLETE_STARTED_PERIOD_MESSAGE, fromParamName, toParamName, from.otherwise(MISSING_VALUE), to.otherwise(MISSING_VALUE)));
    }
  };

  Period period = new Period(fromDate.otherwiseThrow(missingStartDate), toDate.otherwiseThrow(missingEndDate));
  assertPeriodInOrder(period, fromParamName, toParamName);
  return Maybe.definitely(period);
}

Not bad. But having those two Supplier creations inline wasn’t so nice. Let’s extract those out.


public static Maybe<Period> optionalPeriod(final Maybe<String> from, final String fromParamName, final Maybe<String> to, final String toParamName) {
  final Maybe<Date> fromDate = optionalDate(from, fromParamName);
  final Maybe<Date> toDate = optionalDate(to, toParamName);

  if (fromDate.isEmpty() && toDate.isEmpty()) {
    return Maybe.nothing();
  }

  Supplier<WebApplicationException> missingStartDate = missingPeriodPortion(INCOMPLETE_ENDED_PERIOD_MESSAGE, from, fromParamName, to, toParamName);
  Supplier<WebApplicationException> missingEndDate = missingPeriodPortion(INCOMPLETE_STARTED_PERIOD_MESSAGE, from, fromParamName, to, toParamName);

  Period period = new Period(fromDate.otherwiseThrow(missingStartDate), toDate.otherwiseThrow(missingEndDate));
  assertPeriodInOrder(period, fromParamName, toParamName);
  return Maybe.definitely(period);
}

Much better. I find that the inlined fetching of the dates from the Maybes doesn’t lead to the clearest expression of what is going on. So let’s pull out the dates and inline the exception Suppliers. With that the dates can even be given names that give a better clue as to what is special about them at that point in the code.


public static Maybe<Period> optionalPeriod(final Maybe<String> from, final String fromParamName, final Maybe<String> to, final String toParamName) {
  final Maybe<Date> optionalFromDate = optionalDate(from, fromParamName);
  final Maybe<Date> optionalToDate = optionalDate(to, toParamName);

  if (optionalFromDate.isEmpty() && optionalToDate.isEmpty()) {
    return Maybe.nothing();
  }

  final Date requiredFromDate = optionalFromDate.otherwiseThrow(missingPeriodPortion(PERIOD_MISSING_FROM_DATE_MESSAGE, from, fromParamName, to, toParamName));
  final Date requiredToDate = optionalToDate.otherwiseThrow(missingPeriodPortion(PERIOD_MISSING_TO_DATE_MESSAGE, from, fromParamName, to, toParamName));

  Period period = new Period(requiredFromDate, requiredToDate);
  assertPeriodInOrder(period, fromParamName, toParamName);
  return Maybe.definitely(period);
}

Finally I decided to pull out the creation and checking of the Period itself. There were two reasons for this: I don’t like having a possibly “invalid” object floating around in a larger function, and there was another bit of code just above this function that was doing the same thing.


public static Maybe<Period> optionalPeriod(final Maybe<String> from, final String fromParamName, final Maybe<String> to, final String toParamName) {
  final Maybe<Date> optionalFromDate = optionalDate(from, fromParamName);
  final Maybe<Date> optionalToDate = optionalDate(to, toParamName);

  if (optionalFromDate.isEmpty() && optionalToDate.isEmpty()) {
    return Maybe.nothing();
  }

  final Date requiredFromDate = optionalFromDate.otherwiseThrow(missingPeriodPortion(PERIOD_MISSING_FROM_DATE_MESSAGE, from, fromParamName, to, toParamName));
  final Date requiredToDate = optionalToDate.otherwiseThrow(missingPeriodPortion(PERIOD_MISSING_TO_DATE_MESSAGE, from, fromParamName, to, toParamName));
  return Maybe.definitely(periodInOrder(requiredFromDate, requiredToDate, fromParamName, toParamName));
}

This is the point at which I stopped. Next time I go through this code I hope to find a few more things to clean up, though; this point is in no way the final destination. There are a few more possibilities that could be done here that I can see right now. The most obvious one to me was that the combination of parameter value and parameter name is just screaming out for a class. That would give a place to start moving a lot of this validation and transformation functionality that is right now just living in static methods (although the static methods are working pretty well right now).

I hope that this is a good illustration of refactoring in action on a real codebase. Something that I want everyone to get out of this is that not every step of the refactoring took me to a nice next step. This exercise was full of false starts and backslides in the readability of the code. I haven’t shown every dead end that I went down and had to undo out of, but rest assured it happened pretty often. What is important is that I kept trying to figure out how to better express what was happening. As I tried to make it clearer I ended up with a better understanding myself and could try to reflect that in the code.

Posted in Uncategorized | Leave a comment

Toward a Modern Java

Modern Java? Isn’t that an oxymoron?

At XPDay this year Julian Kelsey and I presented a session entitled “Refactoring to Functional Style”. It seemed to be pretty well received and drew a lot more people to the room than, I think, either Julian or I ever expected. The theme of functional style in Java, and other common languages, continued throughout the conference. The ideas showed up in several other sessions as side topics and in one more session specifically about testing functional style programs that was led by Nat Pryce. There seemed to be a new way of working in Java that was starting to emerge.

Continue reading

Posted in Software | Tagged , , , , | 2 Comments

UX Test Your Code

So you think that new module of code that you just whipped up is just fine? You even wrote it with a pair or two! All of the names are the best that you could think of, the thesaurus is still smoking as it sits next to your keyboard. Control flow effortlessly flows from one expression to the next. Data dances in happiness for the structures that you have decided to use. This is not in any way going to be a maintenance problem!

Are you sure? Have you tested it? No, not that kind of testing. I trust that you have a good set of unit tests. Maybe even some nice acceptance tests that were looked at, or provided, by your stakeholder. No, what I mean is have you tested that it won’t be a maintenance problem?

Sure, sure. It was written with pair programming. You swapped pairs several times a day. You probably even stepped away from the keyboard for a bit, went to a whiteboard and hashed out what you were going to build. You communicated this to the team and you all knuckled down and gave it your darnedest. But did you check that someone else can understand it?

What? Someone else? What do I think all of this pairing is about! All of this whiteboard writing. All of these acceptance test shenanigans!

Bad code is not employment insurance

The problem is that your entire team might have succumbed to groupthink. Every one of you got on the same page and stayed there. You might have become blind to something that you think is obvious, but is completely unintelligible to someone outside the group. So let’s do a little test and show that code to someone who has not seen it before. Let them be the judge of how good it is.

Now I’m not saying that you should be doing a sit down formal code review. Think of it as a simple form of usability testing. Grab another programmer that is familiar with the domain and sit her down in front of the code. Ask her to perform some actions on the code and to answer some questions about it. You might try:

  • How does this code achieve foo?
  • You need to add functionality bar. Show me what will need to change.
  • When I perform action baz in the application why does it respond with blub?

Watch how your subject interacts with the code. Watch her fumble and second guess her understanding. See the ease with which she performs some actions and has trouble on others. Now take that information and try to improve your code!

Something that you really need to remember when doing something like this: you are not testing your colleague! Any failings that the test uncovers are not because she is “stupid” or “doesn’t understand,” they are because the code you wrote wasn’t fully up to the task at hand. Having that pointed out can be hard to deal with, but it is there in the data. Now it is up to you to improve and figure out how to fix the problems. Good luck!

Posted in Software | Tagged , | 3 Comments