Every minute we spend just thinking and talking, and not programming, is wasted!
Remember that in our Agile Skills workshop, we do 90-minute iterations. One of the things that always happens, especially in early iterations, is that a team will spend a large fraction of that time discussing things, leaving them ten or twenty minutes to actually program. This never ends well: they don’t get very much done.
Now go with me a bit here. What I’m about to say is obviously wrong. And yet, it isn’t.
We could get more done if we spent more time programming.
To be the thing we’re trying to build, the program needs some minimum number of features. It needs some absolute minimum amount of code. Furthermore, our team builds code at some rate, lines per unit time, which translates into features per unit time. In some sense, every minute we spend not programming means that we’ll get less code done. Less code means we’ll get fewer features done. Fewer features means that when time runs out, we’ll be further from having what we need than if we had used more of our time programming.
No way! To do the right thing right, we need to think!
Of course, this is crazy. “We all know” that we have to write the right code, and we have to write it right, so it makes sense that we’d have to figure out what it’s all about and how we’re going to proceed, and by the nature of the workshop there’s no other time to work on the analysis, the architecture, the design – you know, the thinking that’s obviously necessary for getting a good program.
So we must do a lot of thinking in each iteration, and it makes sense that in the early iterations, we’d do more thinking.
Sure. Think like mad. But not at the expense of code!
Seriously. There is some maximum amount of code we can possibly write in N 90-minute iterations, some maximum number of features or capabilities we can possibly provide. Every moment not spent writing code reduces the amount we can get done, or extends the time needed to get done.
The point of “Agile” methods is to produce running, tested software in every iteration, even the first. Scrum, the most famous if not best example, requires that the team produce a potentially shippable “Increment” of software in every Sprint. Every. Even the first.
We do that for many good reasons. It focuses each Sprint on value. It gives the business and technical people, who, according to the Agile Manifesto, should be working together every day, something to talk about that they can both understand. It teaches the team to deliver working software. And many more.
Good, well-chosen software, all the time!
Perhaps most important: if we can produce good, well-chosen software all the time, we can get more software done. Yes, it does have to be well-chosen software, and it does have to be well done. But delaying the writing, reducng the amount of writing, doesn’t make the choices better or the code better. Let’s learn to choose, code, and make it better, all at once!
So here’s the rub: we have ninety minutes, or two weeks (same thing) to work. The amount of actual product we’ll get done is in some important sense proportional to the number of minutes we spend actually building product. In that sense, every minute we spend not programming, just thinking and talking, is wasted.
That’s crazy, Ron. We need to think! We need to talk! We need to plan!
This is the point where the cartoon character says “Crazy like a fox”. I’m not what one would conventionally call “a fox”, but I do have a point here, and I think it’s an important one.
Let’s learn to think, to talk, to plan, and to code, all at the same time!
A long time ago, we used to talk about letting the code participate in the design discussion. I believe I first heard the phrase from Kent Beck. It’s a good way of describing the ATDD, TDD, Refactoring, Continuous Integration cycle that is the core of good “Agile” software development.
Let’s work an example. As we go, try to notice where we’re doing analysis and design. We’re not just coding, or even coding and testing.
Our story is, let’s say, “An employee’s base pay is the regular hours on their time card, times the employee’s base rate.”
So we code a very small test, sketching as much as we understand:
def test_base_pay base_rate = 10 regular_hours = 40 base_pay = base_rate*regular_hours assert_equal(400, base_pay) end
And that seems kind of dumb but it does run, and it makes the point, so then we’re all “Yeah but there’s an employee that has the base_rate and that has a time card with the hours”, so we change the test:
def test_base_pay base_rate = 10 employee = Employee.new(base_rate) regular_hours = 40 time_card = Timecard.new(regular_hours) base_pay = employee.base_rate*time_card.regular_hours assert_equal(400, base_pay) end
And that totally won’t even compile, so we’re like:
class Employee def initialize(base_rate) @base_rate = base_rate end def base_rate @base_rate end end
And it still won’t even, so we’re all:
class Timecard def initialize(regular_hours) @regular_hours = regular_hours end def regular_hours @regular_hours end end
And voila! it compiles and ZOMG the test even runs! But wait, actually the Employee has a time card associated somehow. Should they literally have them, or should they be in some SuperTimecardRepository in the sky, with all kind of CRUD stuff? I’ve got an idea. For now, let’s just put the time card in the employee:
def test_base_pay regular_hours = 40 time_card = Timecard.new(regular_hours) base_rate = 10 employee = Employee.new(base_rate, time_card) base_pay = employee.base_rate*employee.time_card.regular_hours assert_equal(400, base_pay) end
So that still works just fine. But we say “Wait, who’s doing the paying? We’re just calculating the base pay right in the test. That won’t do. We have to move the base pay calculation into the system somehow.” We have no real idea how to do that but maybe there’s a Payroll object. What if we wrote the test like this:
def test_base_pay regular_hours = 40 time_card = Timecard.new(regular_hours) base_rate = 10 employee = Employee.new(base_rate, time_card) payroll = Payroll.new(employee) base_pay = payroll.base_pay assert_equal(400, base_pay) end
Then we’ll have to extend Employee to accept the time card:
class Employee def initialize(base_rate, time_card) @base_rate = base_rate @time_card = time_card end ...
And the test doesn’t compile yet, so we’ll have to create a Payroll class, that knows an employee, that knows their base rate and their time card, and we’ll do the math in payroll. Trying to do that will drive us to add a time_card accessor to Employee, and a few other odds and ends. The entire program now looks like this:
require 'test/unit' class PayrollTest < Test::Unit::TestCase def test_base_pay regular_hours = 40 time_card = Timecard.new(regular_hours) base_rate = 10 employee = Employee.new(base_rate, time_card) payroll = Payroll.new(employee) base_pay = payroll.base_pay assert_equal(400, base_pay) end end class Employee def initialize(base_rate, time_card) @base_rate = base_rate @time_card = time_card end def base_rate @base_rate end def time_card @time_card end end class Timecard def initialize(regular_hours) @regular_hours = regular_hours end def regular_hours @regular_hours end end class Payroll def initialize(employee) @employee = employee end def base_pay time_card = @employee.time_card regular_hours = time_card.regular_hours rate = @employee.base_rate regular_hours * rate end end
What’s your point, Ron?
My point is this. A few minutes, and only fifty lines of code later, we have a payroll with a fairly reasonable separation of concerns. We have an Employee that knows pay rate and time card. We have a Timecard that knows hours worked. We have a Payroll that takes employees and computes their base rate. Instead of doing analysis, then architecture and design, then coding, then testing, we did it all at once.
Isn’t all this quickness wasteful? Aren’t we making mistakes? Hey, wake up! It’s fifty lines of code! It takes less time to build this and learn from it than it does to read about it!
Maybe at this point, you’d like to go back with four different colors of pen and mark bits of analysis (hey but there’s an employee who has the base rate), and bits of design (that discussion and also
class Employee), as well as the more obvious bits of code and test. Notice how analysis, design, code, test, and all the thinking winds up going immediately into the code. The code expresses all our ideas. The code is participating in our analysis and our designing, not just in our coding and testing. As soon as we have a question, we express it in a test to make it concrete. Making the test run, we produce code that isn’t quite right. We notice that there’s no concept of employee in the code, so we put it in. We notice that there’s no place for calculating to stand, so we put it in.
Just one story, and already we have a little bitty payroll program, with reasonable design, reasonable separation of concerns, reasonable everything. Not final everything, not great everything, reasonable everything.
Then we grab another story, we see what it makes us think, and we put it into the code immediately. Things get better, and we have more stories done. What’s not to like?
We don’t talk for three quarters of the Sprint and then implement for one quarter. We talk and implement all the time. We get more done. It’s concrete. It expresses our ideas. It calculates what we set out to calculate, and it does it with a decent, clear, simple design.
We just keep doing that, story by story. Analyze, design, test, code, all at once. It’s not easy, but it is simple and not all that hard to learn.
That’s how you do it. Got questions? Ask them, I’ll try to answer.
Coda (no pun intended)
Before we move on to another story, let’s take a last look at the code above, and see how else it might need improvement. What, it’s only fifty lines! What could it need?
Well, there’s duplication, and we know that duplication is the enemy.
Don’t you see it? It’s most obvious in Employee
class Employee def initialize(base_rate, time_card) @base_rate = base_rate @time_card = time_card end def base_rate @base_rate end def time_card @time_card end end
Look at the duplication. Look at it! There are two reader accessors for the member variables! Two! And Timecard has one as well. We can do better. Ruby has attr_reader, which automatically provides reader accessors:
class Employee attr_reader :base_rate, :time_card def initialize(base_rate, time_card) @base_rate = base_rate @time_card = time_card end end
So that’s all shorter and nice. Don’t see that as duplication? I can live with that. Call it “minimize entities” if you wish. Either way, it’s shorter, and better idiomatic Ruby.
I’ll do the same thing elsewhere:
class Timecard attr_reader :regular_hours def initialize(regular_hours) @regular_hours = regular_hours end end
The code is a bit better, and we’ve created a sort of default coding standard: use attr_reader and such where applicable. Our fifty-line program is down to forty-one lines, and it still includes good starting ideas for Payroll, Employee, and Timecard.
Is there more? Almost certainly, but I’m of a mind to stop for now. Really. For now.