Wednesday, July 29, 2009

Today's weather

My cousin Judy is visiting from London. She had a flght from Heathrow to Newark, but given today's awful weather, she was redirected to Albany, about three hours from Newark by car. Because Albany's airport isn't prepared to handle immigration, she could not disembark.

After over three hours on the ground, the flight finally left for Newark.


when to made several rotations around an arbitrary area


then redirected to Boston.


This is not Judy's day.

Monday, July 27, 2009

The kind of customer service I like

Kudos to Telestream, authors of ScreenFlow. This is the kind of customer service I admire.


When all too often I hear nothing about bug reports, here's someone just letting me know my request hasn't been forgotten.

Thanks, Willow.

Monday, July 20, 2009

How not to empower your users; don't let them use their primary email address.

Awesome.

PSE&G only allows you to set up one customer service account per email address. So, if you live on one property in their service area, and then move to another property in their service area, you must register a new account with a brand new email address.

I'm genuinely still a little shocked. No matter how hard I tried, I could not find a way to either add a second account or remove the first one to my email address. I messed with pseg.com for 15 minutes, so I called them for help. After 20 minutes on hold, I asked how to change the account. The customer service representative told me, no, I couldn't do that, I had to create a new account. Being hasty, I thanked the CSR and ended the call, assuming he was just wrong.

Registering a second account with the same address failed. Registering a second account with email+pseg@domain.com also failed, not because it was a duplicate but because PSE&G considers that an invalid email address. Honestly, not much of a surprise, that.

So I called again. Was it really like that? After another 20-minute hold (whoo, these guys are popular) I confirmed with a second CSR that, yes, really, it was like that. She said I should just "create another email at like yahoo mail." The CSR agreed to lodge my complaint and I rung off for the second time.

I have one email address upon which I rely, the destination of all my important personal correspondence. Why should PSE&G email have to be delivered elsewhere? I could create a second account whose purpose is merely to forward all mail to the primary one. I refuse to have to remember a new email account just to pay my electric bill. I'm still hoping that one day all my accounts can be registered through OpenID, so this is somewhat the opposite of that goal.

Perhaps people are so dissatisfied with PSE&G that they vow to move out of their service area before ever using them again?

This is a bug, plain and simple. Most likely a bug in requirements or design. And  it got me thinking: what different developement phases might have created such a problem?
  • Requirements gathering phase: perhaps nobody mentioned this as a possibility. Better still, someone mentioned it as a very rare occurrence.
  • Design phase: maybe this was an intentional design decision. Maybe they don't particularly care about account history as a person moves between properties.
  • Database design: assuming a SQL database back-end, perhaps the whole product is wrapped around a database that has a single table called ACCOUNT with columns email_address and account_id. perhaps they can get a lesson in Third Normal Form.
  • User acceptance testing: perhaps no managers or testers had recently moved within the PSE&G service area, if at all.
  • Middle-tier design: first thing I'd do is grep for function createaccount($emailaddress, $accountid)
  • Contract negotiations: The back-end support team has a charging structure based on the number of accounts.
  • UI design: I ... can't really find a way that UI design gets in the way. Here's the registration form. Maybe you can see something.

In my many years working in the pharma consulting industry, there was a common phrase for difficult non non-negotiable tasks: Phase 2. Could this just have been considered a Phase 2 project, complete with database normalization redesign?

Could be. After all, this consumer site is just over 100 days-old.


Sunburst2!

Friday, July 03, 2009

Generic types are not required for covariance

Java 5.0 introduced Generics. It also introduced covariant return types. Wikipedia does a fine job describing covariant return types.

Since they were released simultaneously, I consider them to be tightly coupled. For instance, here are simplified versions of an interface and implementation I recently wrote:

Note: I am having difficulty representing greater-than and less-than symbols in Blogger's editor, so you'll have to do with { and }.

Version 1: Java 5, Generics, Covariant return types
public interface Model{T extends Model{T}} {
  T read(InputStream in);
  T write(OutputStream out);
}
public class MyModel implements Model{MyModel} {
  public MyModel read(InputStream in) {
    ...
  }

  public MyModel write(OutputStream out) {
    ...
  }

 
public MyModel setName(String name) {
    ...
    return this;
  }
  public String getName() { ... }

}
Thanks to the covariance, I can write a method chain like this:
new MyModel()
   .read(in)
   .setName("foo")
   .setStopAtMain(false)
   ...
   .write(out);

With Java 1.4, the code would have to look like this

Version 2: Java 1.4
public interface Model {
  Model read(InputStream in);
  Model write(OutputStream out);
}

class MyModel implements Model {
  public Model read(InputStream in) { ... }
  public Model write(OutputStream out) { ... }
 
...
}
And the method chain would result in a syntax error:
public static void foo() {
  new MyModel()
      .read(in)
      .setName("foo")
     
^ The method setName(String) is undefined
        for the type Model.
      .write(out);
}
Which you could hack around with an ugly cast:
public static void foo() {
  ((MyModel) new MyModel()
      .read(in))
      .setName("foo")
     
.write(out);
 }
Back to the Java 5 example: My point is just this: covariant return types don't require generics. All that messy code in version 1 could look much simpler because covariant return types exist on their own without generics:

Version 3: Java 5, Covariant return types
public interface Model{T extends Model{T}} {
  T Model read(InputStream in);
  T Model write(OutputStream out);
}
public class MyModel implements Model{MyModel} {
  public MyModel read(InputStream in) {
    ...
  }

  public MyModel write(OutputStream out) {
    ...
  }

 
public MyModel setName(String name) {
    ...
    return this;
  }
  public String getName() { ... }

}
Lesson learned: I know generics fairly well, but there's a difference between knowing when it's useful and when it isn't. Said another way: when you have a Generic hammer everything looks like a generic nail.

Thanks to David Plass for pointing this out.