Thursday, August 27, 2009

My Broken Test Found My Missing Object

I'm writing a rails application off and on for the last six months or so. Yesterday I started a major architectural change which broke a few tests. Getting those tests to pass proved pretty difficult until I finally realized what the tests were trying to tell me: "You are missing a domain object."

I'm writing a chore tracking tool for families. You enter the chores you need done on a weekly basis, and it helps organize them by letting you assign due dates and family members to do the chore. I call the family members 'doers'.

Initially, I had a User model and a Doer model. The User was the login, and the Doers were the ones who were assigned to chores. Perhaps you see the problem: doers did not have a way to login. That meant the User had to give her user credentials to all the doers, so each doer could update his or her chore. This also meant doers could reassign chores they didn't want to do or change due dates, or delete the chore altogether.

I came up with two solutions, because you can't pick the simplest until you have at least two. First, I could copy a lot of the User login code over to the DoerController and then check against both models. This would lead to a lot of duplication until I could refactor it out. Second I could remove Doer and just have Users with different roles. This would involve more changes (possibly).

I drew a little IBIS chart in my notes and determined idea two would be the best long term solution.

I created a migration that deleted the doer table, added the fields from Doer to User, and copied all the Doers into the Users table. I then began to fix all the code this broke. When I got to the tests that were failing, I ran into a little issue. See, I added a field to User called user_id that linked Users playing the role of Doers to the User with the login credentials. This created a problem with my rails fixtures because I needed the fixtures to assign a user_id to a User before the User was created. The User didn't have an id yet. ActiveRecord doesn't create an id until the model is saved. I was in a paradox.

The real problem was that I was using User to determine which Board the Doers belonged to. But I didn't even have a Board model. And I need one. That's what the failing tests were trying to tell me. And that is why tests and the five whys are so important. If the tests would have just worked, I never would have had the opportunity to start digging into why the tests didn't work.

Imagine this conversation going on in my head (which I strongly advise you don't do very often):

Me: The tests are failing!
Me: Why are the tests failing?
Me: Because the fixtures don't know how to load user_id's of Users that don't exist yet.
Me: Why are you trying to load user_id's of Users that don't exist yet?
Me: Because each user determines which set of chores the user_doers have access to.
Me: Why is that a User responsibility?
Me: Because the User is the one who owns the board.
Me: What board?
Me: A HA!

Here's my best secret of consulting: a well thought out question goes a lot farther than even the best proposed solution.

So, test and test first. When you are stuck, ask why until you are unstuck.

Thursday, June 25, 2009

Pair Programming != Two Keyboards

At my previous job we pair programmed because we thought we were doing XP. I could go on and on about why we weren't, and perhaps I will in another post as an example of what not to do, but I won't. I want to write about the paring area and one mistake we made: two keyboards and mice at each station. Don't do that. Here's why.

Pair programming is about communication. Two developers sitting at the same workstation solving the same problem in tandem. Two heads are better than one, so to speak. Pair programming works best when the communication is at its highest. If you can't communicate about wanting the keyboard, how good is the rest of the communication going to be.

Pair programming is about roles or 'hats'. There is a driver and a navigator. The driver worries about the language, syntax, formatting, and other low level programming things. The navigator worries about the task at hand, the design of the solution, and keeping the pair on track towards that solution. When those roles are reversed, there needs to be a clear indication. It's a context switch, and the pair needs a little switch time to orient to the new role. The act of passing the keyboard and mouse is the signal that the roles have been reversed.

Pair programming is about cooperation. It's hard to work together in a field where working alone has been the norm. At the risk of leaning on stereotypes, programmers tend to be solitary creatures. Sharing a keyboard and mouse is a baby step towards the cooperation the pair will need to work effectively together.

I'll always remember my supervisor coming to me after a few weeks working with one of the developers in the new pairing area and saying, "It seems like Joe is just not engaged when we pair. Any idea why?" I said because you have two keyboards and two mice. You have yours and Joe has his. He can focus on his set and think about what he'll do when it's his turn to use them, and not on what you are doing. When you both have a keyboard in front of you, the role each of you is supposed to play is blurred.

So if you have two keyboards and two mice in your pairing area, remove one set for a few weeks. Have the pairs pass the keyboard back and forth for a while. Once they get good at it, which can take a long time, ask them how they feel about one keyboard and mouse. Remind them of the things I've written about pair programming, and have them focus on which hat they are wearing when. I predict that once they get good at it, they'll prefer the one keyboard and mouse, communication between pairs will improve, and productivity might even improve as the role each pair plays are made clear so progress towards task completion is accelerated.

Sunday, June 7, 2009

TDD and Fantatics

I don't understand how or when TDD == Fanaticism? Yes, TDD is a high discipline practice, but so is using SCM. You don't just arbitrarily decide to not check in code. Or start the day without pulling down the latest changes. Or not branch/label/snapshot the tree each release.

Yes, TDD advocates can sound like zealots, and that's OK.
We sound like zealots because we've done TDD long enough to get good at it. We are good enough at it to see the benefits. And it took a little longer than half a day to get good at it. We remember the code we wrote and read pre-TDD, and we prefer the code we write and read now with TDD.

I've been an XP consultant/coach for 8 years now, and these sorry assed excuses about how TDD doesn't scale, or my process/project/team is unique and it doesn't fit, or I'm such a bad ass programmer that I don't need no stinkin best practice telling what to do, are getting really old. To be completely honest, they all have a slight aroma of laziness to me.

There are a lot of best practices and processes out there that I do not do or do not advocate, but that's all I do. I don't go out of my way to bash them or the people who find them valuable.

Monday, March 23, 2009

Moving

I'm not moving my blog to my new home page at http://curtiscooley.com. The main reason is I'm starting my own software contracting and agile consulting company. At first it'll just be me trying to find companies willing to give me money in return for helping them build software. My main value add will be my many years of agile consulting. Not that I'll try to convert shops to agile, but just that my agile experience has taught me how to maximize value to the customer.

Thank you all for following me here and I hope you follow me to my new site.

You can also follow me on twitter:CurtisRCooley

Also, my new blogging software does a much much better job of formatting source code, as you'll see by my first post there: Why You Should Program To Interfaces Part I

Wednesday, March 11, 2009

Are We Asking Too Much From Programmers?

In the olden days software teams were split into two distinct groups: architects and programmers. Architects designed the system and programmers implemented the design.

Then Extreme Programming came along and said splitting teams up wasn't a good idea. Everyone should be in the same room. Collective code ownership said that anyone should be able to work on any piece of code. Now architects had to code and programmers had to design.

Architects advanced to architect from programmer, so they'd coded before. The transition to writing code, while humbling, was not a huge task.

Programmers were programmers because they were not architects. They had not designed before. This transition is more troubling.

I'll cop out a little and lean back on the age old construction metaphor. See, construction workers aren't architects. Most of them don't even strive to be architects. They build things with their tools and their hands and enjoy it, why would they want to build things with paper and pencil?

In agile development we are asking programmers to design. Maybe they don't want to design. Maybe, for some reason, they can't design. It's not part of their mental makeup. They construct. They are good and constructing. Why are you asking me to do more than construct?

I've read and signed the Craftsmanship Manifesto, http://manifesto.softwarecraftsmanship.org/ and joined their Google group, http://groups.google.com/group/software_craftsmanship/web/the-new-left-side?pli=1. There is a thread in the group titled something like "Should we/can we convince programmers to care about their craft?" This thread got me thinking and that thinking lead to this blog.

So, are we, the Agilists and craftsmen, asking too much from programmers?

Tuesday, March 10, 2009

OO Basics: Create the right object

Most people learn structured programming before they are exposed to object oriented programming. That's just the nature of the beast, since you need to learn the basics of a language before you can move on. At least that's the latest theory. The problem with structured programming is you learn to use flags to signal changes in state. You then end up with ifs and switches all over the code. For example


if (item.isTaxable()) {
item.setCost(item.getCost *= TAX_RATE);
}


The root of the problem is that you only have one kind of Item.


public class Item {
private boolean taxable;

public boolean isTaxable() {
return taxable;
}
...
}

where two types are needed:

public interface Item {
double getTax();
}

public class TaxableItem implements Item {
public double getTax() {
return cost * TAX_RATE;
}
}

public class NontaxableItem implements Item {
public double getTax() {
return 0;
}
}

You also have a problem with how the object is created:

Item item = new Item(true);



Say you are implementing a shopping cart, and as the user adds items to the cart, the correct type of item is added. Then at the end, you can calculate tax for the taxable items because the cart already contains the right kind of item.

cart.addItem(ItemFactory.createItem(itemId));

Now there are no ifs in the domain code:

for (Item item : cart.items()) {
tax += item.getTax();
}

The moral: a lot of ifs and their accompanying headaches go away when you take the effort to create the right object.

Tuesday, December 23, 2008

Refactoring

Perhaps there are already enough refactoring examples, but if not then I present yet another. I was writing some code investigating a potential project here at Venture, and since it was just spike, code, I just hammered it out. Here's run() from a Runnable:

public void run() {
Map fileDetails = new Hashtable();
try {
BufferedReader reader = new BufferedReader(new FileReader(MainFrame.WATCHED_DATA));
String directoryName = reader.readLine();
File fileData = new File(directoryName, ".vobas");
while (directoryName != null) {
if (!fileData.exists()) {
fileData.createNewFile();
} else {
ObjectInputStream fileDetailsReader = new ObjectInputStream(new FileInputStream(fileData));
FileDetail fileDetail = (FileDetail) fileDetailsReader.readObject();
while (fileDetail != null) {
fileDetails.put(fileDetail.getName(), fileDetail);
try {
fileDetail = (FileDetail) fileDetailsReader.readObject();
} catch (EOFException e) {
break;
}
}
}
File[] files = new File(directoryName).listFiles();
for (File file : files) {
FileDetail fileDetail = fileDetails.get(file.getName());
if (fileDetail == null) {
ScpTo.send(directoryName + File.separatorChar + file.getName());
fileDetails.put(file.getName(), new FileDetail(new Date(), file.getName()));
} else if (file.lastModified() > fileDetail.getModificationDate().getTime()) {
ScpTo.send(directoryName + File.separatorChar + file.getName());
fileDetails.remove(file.getName());
fileDetails.put(file.getName(), new FileDetail(new Date(), file.getName()));
}
}
ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File(directoryName, ".vobas")));
for (FileDetail fileDetail : fileDetails.values()) {
objectOutput.writeObject(fileDetail);
}
objectOutput.close();
directoryName = reader.readLine();
}
reader.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

If you're anything like me, you'll need a few minutes to compose yourself after reading this. If you read that code and didn't immediately feel a little ill, I strongly suggest you pick up Clean Code by Robert Martin. Code like this should make you at least a little queezy.


Abstractness In Methods


I learned from Clean Code that all methods should have the same level of abstractness. Public highlevel methods should do high level things. They should be composed of medium abstract methods that are themselves composed of highly detailed methods that do lowlevel things.

For example this code

ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File(directoryName, ".vobas")));
for (FileDetail fileDetail : fileDetails.values()) {
objectOutput.writeObject(fileDetail);
}
objectOutput.close();

writes FileDetails to a file. It can be extracted into a self describing method

writeFileDetails(fileDetails, directoryName);
...

where writeFileDetails looks like

private ObjectOutput writeFileDetails(Map fileDetails, String directoryName)
throws IOException, FileNotFoundException {
ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File(directoryName, ".vobas")));
for (FileDetail fileDetail : fileDetails.values()) {
objectOutput.writeObject(fileDetail);
}
objectOutput.close();
return objectOutput;
}

The section just above that starts with File[] files = new File(directoryName).listFiles(); reads the files from the directory, checks to see if each file needs passed to ScpTo, then updates the FileDetail map. With judicious inlining, renaming, and extract method the for loop can be refactored to

for (File file : eachFileIn(directoryName)) {
if (fileModified(file, fileDetails.get(file.getName()))) {
ScpTo.send(directoryName + File.separatorChar + file.getName());
fileDetails.put(file.getName(), new FileDetail(new Date(), file.getName()));
}
}

and then replaced with a self describing method

sendModifiedFiles(fileDetails, directoryName);

The fileDetails Map is starting to look pretty interesting to me. It seems this naked collection should be made a first class object. I'll repress this urge for now, but I wanted to mention it during the same part of the process that it occurred to me.

My run method is starting to fit on one page. Always a good sign. Arguments over method length can be as heated and never ending as brace wars, but while we can't seem to agree on one or two line methods, most can agree they should fit on one page in your editor.

The if/else clause is about reading from the file detail file and loading up the fileDetails Map. Let's extract that method so

if (!fileData.exists()) {
fileData.createNewFile();
} else {
ObjectInputStream fileDetailsReader = new ObjectInputStream(new FileInputStream(fileData));
FileDetail fileDetail = (FileDetail) fileDetailsReader.readObject();
while (fileDetail != null) {
fileDetails.put(fileDetail.getName(), fileDetail);
try {
fileDetail = (FileDetail) fileDetailsReader.readObject();
} catch (EOFException e) {
break;
}
}
}

becomes

readFileDetails(fileDetails, fileData);

Now we can extract the try clause into a very abstract method.

try {
BufferedReader reader = new BufferedReader(new FileReader(MainFrame.WATCHED_DATA));
String directoryName = reader.readLine();
File fileData = new File(directoryName, ".vobas");
while (directoryName != null) {
readFileDetails(fileDetails, fileData);
sendModifiedFiles(fileDetails, directoryName);
writeFileDetails(fileDetails, directoryName);
directoryName = reader.readLine();
}
reader.close();
}

becomes

try {
sendModifiedFiles(fileDetails);
}

The two page try clause has been reduced to a single method call that explains succinctly what the method is trying to do. By breaking the long try clause into small chunks and extracting self describing methods, I've converted a fairly incomprehensible chunk of code into a self documenting class. I'll save wrapping the file details Map in a class and moving most of the methods to it since it seems to be the one doing all the work for another entry. Until then, here is the resulting class:

public class VobasBackupService implements Runnable {

public void run() {
Map fileDetails = new Hashtable();
try {
sendModifiedFiles(fileDetails);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

private void sendModifiedFiles(Map fileDetails) throws FileNotFoundException, IOException,
ClassNotFoundException {
BufferedReader reader = new BufferedReader(new FileReader(MainFrame.WATCHED_DATA));
String directoryName = reader.readLine();
File fileData = new File(directoryName, ".vobas");
while (directoryName != null) {
readFileDetails(fileDetails, fileData);
sendModifiedFiles(fileDetails, directoryName);
writeFileDetails(fileDetails, directoryName);
directoryName = reader.readLine();
}
reader.close();
}

private void readFileDetails(Map fileDetails, File fileData) throws IOException,
FileNotFoundException, ClassNotFoundException {
if (!fileData.exists()) {
fileData.createNewFile();
} else {
ObjectInputStream fileDetailsReader = new ObjectInputStream(new FileInputStream(fileData));
FileDetail fileDetail = (FileDetail) fileDetailsReader.readObject();
while (fileDetail != null) {
fileDetails.put(fileDetail.getName(), fileDetail);
try {
fileDetail = (FileDetail) fileDetailsReader.readObject();
} catch (EOFException e) {
break;
}
}
}
}

private void sendModifiedFiles(Map fileDetails, String directoryName) {
for (File file : eachFileIn(directoryName)) {
if (fileModified(file, fileDetails.get(file.getName()))) {
ScpTo.send(directoryName + File.separatorChar + file.getName());
fileDetails.put(file.getName(), new FileDetail(new Date(), file.getName()));
}
}
}

private boolean fileModified(File file, FileDetail fileDetail) {
return fileDetail == null || file.lastModified() > fileDetail.getModificationDate().getTime();
}

private File[] eachFileIn(String directoryName) {
return new File(directoryName).listFiles();
}

private ObjectOutput writeFileDetails(Map fileDetails, String directoryName)
throws IOException, FileNotFoundException {
ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File(directoryName, ".vobas")));
for (FileDetail fileDetail : fileDetails.values()) {
objectOutput.writeObject(fileDetail);
}
objectOutput.close();
return objectOutput;
}
}