Chip Camden recently participated in the Global Day of Coderetreat, which he recommends for anyone who loves to code.
On Saturday, December 3, 2011 I attended the Seattle local edition of the Global Day of Coderetreat. According to the Global Day of Coderetreat website, "Over 2200 passionate software developers in 90 cities around the world" participated in the event. Thanks to Chad Perrin for suggesting Coderetreat to me.
Sponsors paid for the event, so it was free of charge. Substantial hosted and sponsored the Seattle venue, and they even provided lunch. Their open office space with views across Cal Anderson Park provided a great atmosphere for intense pair programming. Gary Bernhardt facilitated.
The Coderetreat format involves 45-minute pair-programming sessions, followed by a group retrospective and break before proceeding to the next session. For each session, you choose a new partner. You can work in any language upon which you and your partner can agree. The facilitator may impose constraints on each session, and Test-Driven Development (TDD) figures prominently. At the end of each session, you throw away your work. The idea is not to create an end result, but to learn about the process.
The problem domain is always the same: Conway's Game of Life. Nobody ever completes this game. In fact, nobody ever gets around to implementing a user interface. If you even get a working model of the generational algorithm, you're either exceptionally good, or you're not following directions.
Our event made it through six iterations, and I learned something from each of them. I tried to make each one as different from the others as I could.
First iteration: Ruby
If I had to choose one language in which to work all the time, it would be Ruby, so that was a natural choice for the first "get your feet wet" iteration. I chose as partner the guy who was sitting next to me, Dan, who happened to be a Rails developer. We agreed to use straight-up Ruby. Dan introduced me to RSpec. I had heard of it, but never used it before (I had always just used Test::Unit).
Dan and I used a classic object-oriented approach. We began with the Cell class, writing tests for its behaviors and making them work. We started to mire down at the end of the 45 minutes when we got to the relationships between cells and how the infinite grid gets instantiated without an infinite recursion.
During the retrospective, Gary suggested that for the next iteration we should avoid focusing on the objects, and instead work from the abstract rules. Obviously, Dan and I had fallen into a common trap.
Second iteration: Clojure
I randomly chose my second partner in the break room. Josh's strong language was Java, so I agreed to do that. Josh also had a heavy math background, so he suggested that we formulate the rules of the game in functional notation before getting started with tests. I've been a fan of Functional Programming for some time now, so I supported that idea enthusiastically. Our formulation turned out to be much simpler than the four rules listed by the facilitator -- we expressed it as a single function on one line.
"OK, let's do it," Josh said, as he spun up IntelliJ IDEA on his paper-thin MacBook. When he went to go create a new project, I noticed that in addition to Java, the IDE offered options for Scala and Clojure.
"You know," I said, "starting with a functional notation, one of those other languages should be easier."
So Josh agreed to use Clojure instead. I had never used it before, but I'm familiar with some other Lisps so it wasn't hard for me to learn. We didn't have a test framework for Clojure, so we wrote a simple one that only supported tests for equality.
We didn't get far, but this iteration refined my understanding of the problem.
Jason also clued me in to a feature of the Life algorithm that he had discovered in an earlier session. Rather than describing the new state of a given cell as either "dead" or "alive," add a third state: "unchanged" (which happens if a cell has exactly two living neighbors). At first, I thought that would complicate things (after all, I had just participated in an elegant formulation of the rule that had a binary result). But it turns out that Jason's rule separates the action to take from the state of the current cell. It allows you to take smaller steps in building your tests and satisfying them, and I was to find out later how it's a better functional model.
For our fourth iteration, Gary instructed us to use a ping-pong approach: one person write a test, the other person write the code necessary to make it pass -- then swap roles. He furthermore asked us to write the test that creates the most work possible, but to make it pass with the least amount of code possible.
I expected that our constraints for this iteration would result in utter chaos, but instead it seemed to make the evolution of the program more disciplined. In retrospect, we were probably both too nice about the tests that we wrote. Both my partner and I were thinking incrementally. If we had instead written the tests from a broader result perspective, then we would have mocked to death without foresight the minimum code to satisfy those tests. Some of the other groups seemed to experience that.
Fifth iteration: C
During the retrospective following the fourth iteration, someone asked if anyone had ever attempted this in C. A voice from the back said, "If anyone wants to, I'll do C." I quickly volunteered.
Gary then gave everyone a constraint: "no conditionals." No if statements, for loops, while loops, or any other form of conditional.
"And here I just committed to C," I said, getting a laugh from the crowd.
I sat down with my partner Alan, a big lumberjack of a guy with a full beard and stocking cap. We once again created a simple test framework, with assertions that sent feedback to stdout instead of throwing exceptions. Then he looked at me, his hand nervously tapping the keyboard. "How do we do this?" he said.
"It won't be easy," I replied.
He started coding, and then I came up with an idea. "We could use an array of function pointers, one element for each number of possible live neighbors." That's where Jason's earlier observation turned to gold. We could have one function for making a cell live, a second for killing a cell, and a third for leaving it unchanged. That third function eliminates the need for a conditional test!
Of course, that only solved one area of the problem. "You think of our next strategy while I code this one," Alan said.
After solving our second and third problems without conditionals, our adrenaline was flowing and we were swapping high-fives. If we had another ten minutes, I think we could have completed the generational algorithm, sans UI.
When Gary called time, Alan and I just looked at our creation. I've been writing C code since 1984, but never in all those years had I worked on a more amazing 45-minute project. I had never written anything more complicated than "Hello world" without conditionals -- never mind the algorithm for a game. "Before you delete it, just pipe it to 'mail firstname.lastname@example.org'" I said. Alan laughed, a little sadly.
Sixth generation: C#
I wouldn't have felt like I had stretched this problem enough if I hadn't included at least one .NET approach. So I partnered with Ken, who liked C#. This choice turned out to be a double-edged sword.
Gary placed the hardest constraint of all on this iteration: no primitives, nor any class that comes with the language. No lists, arrays, strings, integers, etc.
In one respect, that makes a statically-typed language a good choice. We were able to use the type system in place of an integer: you have a class for a cell with no neighbors, another for a cell with one neighbor, etc., all derived from an abstract class. These classes have methods for incrementing and decrementing the number of neighbors, which result in a new instance of the appropriate class.
On the other hand, a language like C# makes writing all these classes time-consuming, even though Ken knew how to use Visual Studio to its full advantage. We had barely begun dealing with the problem of the grid before time expired.
The folks from Substantial had generously offered us the beer in their kitchen fridge, and I took them up on it after the last iteration. Standing with the group around Gary during the final retrospective, I thought back over what I had experienced. I felt physically tired but exhilarated, like an athlete after a vigorous workout.
I would definitely do this again, after a sufficient rest. I would recommend Coderetreat for anyone who loves to code (if you hate programming, this is not for you). You'll learn more in one day than you can imagine, and you'll meet some interesting fellow code-geeks.
Afterwards, Alan came over and shook my hand again. "That was great, man," he said, smiling.
"It was frickin' awesome," I replied.