Why OpenID sucks from a user experience perspective
Many people seem to be touting OpenID as the next big thing in authentication. Early adopters request it, web geeks love it, and sites having it claim to be easier to use and more modern. The idea of maintaining only one login to access everything else sounds like a great idea until you thoroughly examine it. I personally have been considering the concept, especially since many Cashboard customers are requesting OpenID login as a feature addition.
What problem are we solving exactly?
The first gripe most people have with regular login systems is memory. There’s no doubt about it, maintaining all of the login information across your accounts can become tedious.
I personally have about 40 web logins and passwords I have to maintain for various services. The idea of having only one login to remember is nice, but is this really a problem?
Today all browsers allow you to save your login/password information, and if you’re worried about security you can always use a program to manage your passwords which also encrypts everything for safe keeping. Most password managers also have mobile versions for your phone so you can take login information with you on the go.
Replacing non-problems with confusion
OpenID claims to solve this memory "problem" another way, by providing one password to rule them all. It sounds good in theory, but in reality quickly it falls apart from a usability perspective.
I stumbled across a great article that explains the usability downfalls of OpenID which I suggest you check out. I won’t rehash all of the discussion there. Rather, I’d like to take a look at a real world example I personally ran into on Stackoverflow.

One can imagine the following thoughts racing through the average web visitor’s head when this screen initially pops up.
- Huh?
- Where’s the username and password fields?
- I like Google, but don’t like Yahoo. Should I click Google?
- What do I type here?
- What’s my OpenID URL?
Even those that know what OpenID is could be challenged when presented a screen like this. I personally have a Google account, 2 Yahoo accounts, a WordPress account, and an AIM/AOL login. Which one do I use to login here?
At least with the majority of my other accounts I use a standard email address which I’ve been conditioned to remember. OpenID invents a whole new bag of problems, this being just the first.
New problems being invented with OpenID
I’ve actually logged into Stackoverflow before and had linked it with my Yahoo account. Returning to the StackOverflow site to ask a question I attempted to login with my Yahoo OpenID once again. The problem is, now Stackoverflow didn’t recognize my Yahoo OpenID.
Instead of being logged in after completing the OpenID process I was greeted with this screen.

I thought I must have forgotten which OpenID I used to login. Perhaps it was my Google account? Nope, not that one…not any of them in fact.
Feeling frustrated I finally stumbled to this page which is supposed to email your forgotten login information. I played roulette with my different email addresses, finally hitting one that it found acceptable.
When I received the "account recovery" email it told me something quite bizarre; I had linked my account to my Yahoo/Flickr OpenID. The problem is I had just deleted my Flickr account a couple of days ago thinking I would never use it again. Even though I still had a Yahoo account, I did not have my Flickr account. It turns out that you can actually have MULTIPLE OpenIDs through the same provider.
This is supposed to be better than a regular username / password combination how?

Unfortunately there is simply no way to ever login again to the site, or reset my account to be linked with another OpenID.
What a horrible user experience.
Where do we go from here?
I’m sure the example I ran into is just one of many usability scenarios that nobody has bothered to think through. Multiple this by the number of sites implementing OpenID logins and you can quickly start to imagine the myriad of usabilty problems being invented daily.
OpenID does solve a number of interesting security problems, but at the moment I think it’s not mature enough from a usability standpoint to be useful.
I hope the interaction problems surrounding OpenID continue to be worked on, as Yahoo is doing. They’ve conducted a very thorough usability study on OpenID, which I encourage you to read if you’re interested in the topic. It appears they’re making progress, but at a slow pace.
Alternatives
There seems to be a few great implementations of Facebook connect and Twitter oauth starting to pop up around the web.
I really like what Disqus is doing with blog commenting and linking to the social web, and I’m sure we’ll continue to see more interesting alternatives appear.
I’m interested to see where things go from here. Have you seen any great implementations of OpenID yet? Share them with me.
Making Workling::Return::Store predictable
This took me about 4 hours to figure out today, so I figured I might as well blog about it.
I’m using the Workling / Starling combo to handle background processing tasks on Cashboard for various things.
One of those things is the Cashboard / Basecamp project sync, which provides a really nice progress bar while it does it’s thing.
I was encountering unpredictable results when I started refactoring some code from the worker into various models, and stumbled upon this oddity.
>> Workling.return.set('foo', 'bar')
=> "STORED\r\n"
>> Workling.return.get('foo')
=> "bar"
>> Workling.return.get('foo')
=> nil
I expected Workling.return.get to be a non-destructive action, but I was dead wrong. It turns out that the default Memcache Workling::Return::Store client acts like a first-in-first-out (FIFO) stack on each key.
Adding these methods allow me to use any Workling::Return::Store as I initially expected. This will add the methods to the default MemoryReturnStore (for testing), and the StarlingReturnStore (for dev/production).
Now I can run the following code with no problem.
>> Workling.return['foo'] = 'bar' => "STORED\r\n" >> Workling.return['foo'] => "bar" >> Workling.return['foo'] => "bar"
Hopefully this helps someone bashing their head against the wall, like I was. Don’t hurt yourself.
Cashboard sponsors Rails Rumble 09

Each year since its inception we’ve sponsored the Rails Rumble by offering comped Cashboard accounts for each member of the winning teams. This year is no different as we’re providing free DYNAMIC accounts for all winners.
The Rumble is getting ready to kick off this weekend, with over 200 teams competing for top prizes from many different companies.
If you’re not familiar with the Rails Rumble, here’s a primer
The Rails Rumble is a 48 hour web application development competition. As a contestant, your team gets one weekend to design, develop, and deploy the best web property that you can, using the awesome power of Ruby and Rails.
As a judge, you get to review some fun new micro applications and help determine which teams fare best in a number of categories. Along the way, you might discover a new service that’s really useful or fun.
Oh yeah, and there are prizes too. For both contestants and judges. Sounds great, eh?
There have been many excellent entries in the past, some going on to become full fledged commercial services. If I were starting a new app this would be a great way to get some press for it, especially with the attention the event is getting from the business world.
I’m excited to see what this year’s contestants come up with.
Using concerned_with and autotest for Rails models
Cashboard is growing to be a huge software project, over 11k lines of code, written in Rails.
Some of the models have gotten extremely fat using Jamis Buck’s advice about "skinny controllers, fat models". One obese file in particular was causing me a ton of stress due to it’s massive content, even though I comment liberally and tend to break up sections in the code with large ASCII dividers.
To compound the stress, my unit tests were also getting extremely bloated and hard to read. I couldn’t tell if I’d tested a particular behavior at a glance. Things were becoming extremely hard to manage on both fronts.
I ended up searching out ways to break chunks of logic into separate files and finally settled on using the concerned_with trick for ActiveRecord.
Autotest problems
I’m a firm believer in using autotest while developing. There’s no way I could manage a huge project like Cashboard myself without it. I’ve come to rely on it.
The concerned_with trick is great to break up logic into multiple files for one model, but I ran into some issues with the trick and autotest.
All of my tests for a model were still crammed into one huge file and the unit test didn’t even run when I saved one of my new "concern" files.
Solutions
Through some trial and error, I stumbled across a wonderful way to structure all of my files and hooked up autotest to recognize them all properly. Hopefully it helps someone else out there fighting with the same issues.
Right now I’ve got my project structured like so.
app/models/ * account.rb * account/ ** billing.rb ** validation.rb ** ... test/units * account_test.rb * account/ ** billing_test.rb ** validation_test.rb
Using this layout, I’d expect the billing_test.rb file to get run by autotest if I saved the billing.rb file. This is quite easily accomplished by adding a .autotest file to the root directory of your Rails project – as described in this article.
The contents of my .autotest file automatically map my concerned_with files to my custom unit tests for model behavior.
Post subversion changes to twitter
Here’s a quick post-commit script for subversion that posts changes directly to www.twitter.com.
I’m using it to power the Cashboard twitter feed. If you preface any SVN commit message with "tweet" the rest of the commit message will be pushed to twitter.
Just modify the twitter username and password, then the script in your subversion "hooks" directory, and mark it as an executable (chmod +x post-commit).
Import production SQL to your development box with Capistrano
I got sick of using Navicat to import production data from our servers in order to test it locally on a development box.
Here’s a quick Capistrano task that imports production data from your server to your development box locally.
This is great way to debug problems that are happening on your production server. It gives you the ability to mess with things locally without the risk of fucking up someone’s important data ;)
Substruct 1.0.a4
Substruct, the Ruby on Rails e-commerce and content management software just tagged and bagged the last release before we go 1.0.
The version number is seriously misleading as the project has been active for over 2 years. Oh well…
Major changes
- Update to Rails 2.1.0
- Lots of bug fixes and updates from our community
Get the good stuff
- See all of the latest changes
- Download the latest version from SVN, or a tar / gzipped archive
- Discuss Substruct with us at Google Groups
Enjoy…
Don't take it personal
As most of you know, we released a product last year, Cashboard, which handles time tracking, invoicing, and project quotes (or estimates).
Since the release I’ve refined the product based on my own needs and the multitude of feedback I’ve received via email, forums, and comments strewn across blogs all over the web.
Cashboard is my baby. I pour my blood, sweat, and redbull into it each and every day. Working on something so hard and so often, one starts to develop a relationship with the work…an attachment.
I also handle the majority of customer support and potential customer emails. It’s great because I get to keep my finger on the pulse of what needs attention. However, the downside comes in the form of insane rants, negative comments, and downright outlandish demands a few people make.
Some days it takes all my restraint not to fire off an email that looks more like the lyrics to a gangsta rap track than a customer support response. I’ve slipped a couple of times, but I’m getting better at sleeping on things. It’s a crucial skill.
I try not to take jabs against my baby as a personal attack, but it’s hard. Yet another skill I’m still trying to master.
Of course, a lot of these comments are valid, once you strip away the venom and bile. Being able to sift through the garbage and determine the meaning has paid off multiple times now in the form of design updates, and changes to Cashboard.
So next time someone bashes something you’ve done, instead of immediately lashing out – sleep on it and take it in stride. Discard the way the comment was delivered, and get to the real meaning.
Who knows, there might even be some value in it.
Substruct v1.0a3
Substruct v1.0.a3 was just tagged and bagged.
Lots of bugfixes and a mostly working test harness. Next step, fully working tests :)
Go to the home page to download, or get your fix directly from here.
Find out what ports your mongrels are using
I like mongrel. I deploy all of our Rails apps using it.
But…I got sick of opening and reading each of my mongrel_cluster.yml config files when adding a new mongrel to the server.
I cooked up this quick script to list all Rails apps on a production box – and their mongrel ports. It assumes all of your apps live in /var/www/(app_name)/current. You might have to do some modifications if you store your Rails apps in a different place.
Maybe you’ll find it useful as well.
list_mongrel_ports.rb
#!/usr/bin/env ruby
#
# Loops through all rails apps and compiles port information
# for mongrels.
#
# Great for figuring out what ports are in use when deploying a new app.
#
# Written by seth @ subimage llc - http://sublog.subimage.com
#
require 'fileutils'
APP_DIR = '/var/www/'
def get_mongrel_config_info(app)
config_f = File.join(APP_DIR, app, 'current', 'config', 'mongrel_cluster.yml')
if File.exists?(config_f)
starting_port, servers = ''
File.open(config_f, 'r') do |f|
while line = f.gets
if line.include?('port:')
starting_port = line.gsub(/port:|"| /, '')
elsif line.include?('servers:')
servers = line.gsub(/servers:| /, '')
end
end
end
servers ||= 1
ports = []
servers.to_i.times do |i|
ports << starting_port.to_i+i
end
return ports.join(', ')
else
return nil
end
end
Dir.open(APP_DIR).each do |app|
info = get_mongrel_config_info(app)
puts "#{app}\n ports: #{info}" unless info.nil?
endSample output
-> ./list_mongrel_ports.rb
cashboard
ports: 8500, 8501, 8502, 8503, 8504
getcashboard
ports: 8010, 8011
cbinfo
ports: 8700
cbforum
ports: 8705Hrm…looks like I could do a little reorganization of my ports :)
