Tom Blomfield

Dont Write Tests The Hidden Cost Of Tdd

Mar 31, 2012

I’ve been following the TDD/testing debate with some interest; I work at an early-stage payments startup called GoCardless where we test religiously. We’re not dogmatic about whether the tests come first or not, but code doesn’t get deployed without full unit & acceptance tests. It’s saved our skins on more than one occasion

I wanted to explore the benefits of testing and suggest some situations in which <heresy>testing may be counterproductive</heresy>  

Testing - The Good Bits

When you’re working as part of a team on a large codebase, comprehensive testing is invaluable. If you measure development velocity on a company-wide basis over the medium-long term, testing provides clear benefits.

Engineers working on one part of the codebase don’t have to worry about their changes having unanticipated knock-on effects in another area. A good mix of unit tests and acceptance tests means that you can write & deploy code quickly, without a lengthly QA process.

When stuff does break, unit tests allow you to identify the individual component that’s causing problems. It’s fantastic - at GoCardless, no code goes into production without comprehensive testing & peer code review.

(If that sounds like a place you’d like to work, we’re hiring!)

Testing slows down rapid prototyping

Having said that, there are situations when comprehensive testing just isn’t the right solution. I’m not talking about whether tests should come before (TDD) or after writing code - there are situations where it might be beneficial to write no tests at all.

The particular situation I have in mind is the ultra early-stage startup struggling to find traction. The number one priority in this situation is not to write beautiful code or solve hard technical problems in innovative ways; it’s to work out what people people want. You do this by iterating quickly & launching early - just as it’s often possible to fake a much more sophisticated or elegant solution by doing stuff manually, it’s also possible to write quick, scrappy code that works just about well enough.

You’ll accumulate buckets of technical debt, but it’s generally ok. If you don’t find traction or “product-market-fit”, you declare bankruptcy on that technical debt and just move on to the next iteration.

Once you’ve found traction, you’ll likely need to re-write your codebase, but that’s ok as well. At this point, you’ll have a much clearer idea of what the product actually does, so writing specs should be much simpler.

As an addendum, I think this conundrum is one reason why outsourcing early-stage tech development is such a terrible idea - any decent web development shop will generally insist on writing comprehensive test suites, and they’ll react badly if the job spec changes dramatically every 2 weeks. But that’s sort of necessary when you’re writing code for an early-stage startup.

Discuss this post at Hacker News !

Automating Customer Service At A Startup

Feb 12, 2012

Customer service can make or break a company; it has been one of the key differentiators as a new generation of startups seeks to compete against incumbents. While people remember examples of awesome customer service, the opposite is also true.

At GoCardless, we decided early on that we wanted customers to have a fantastic experience with the service, so that they’d hopefully tell their friends about it. Unfortunately, being a 5-person startup, we don’t have the luxury of a massive customer support team. What we do have, however, is access to a bunch of great tools that make running a startup much, much easier. These tools, combined with some really smart developers and the instinct to Automate Everything, results in a pretty great customer experience.

I wanted to spend a little time going through those tools, and demonstrating how they can be combined with a sprinkling of API magic powder to produce something vastly greater than the sum of their parts. 

With these super-charged tools, we find one full-time person can generally run customer support on their own, leaving the other 4 to work on the product. We’ve also consciously made the decision that everyone should do some customer support - even engineers. It really helps people stay in touch with what our customers are saying, and lets us react quickly as a team to common problems that emerge. 

I should note that we can’t claim credit for this idea - it was stolen wholesale from a great talk the Wufoo guys gave at a Y Combinator dinner. 

Campfire

$12/month for basic version. As a relatively technical company, our Campfire rooms form the central communications hub for our company. It’s basically like hosted IRC with a few bells & whistles like file uploads & code snippet support. You can choose to make rooms public or private, and generate direct links that will enable customers to chat with you directly through their browser. We do most of our technical customer support through these rooms. And as a small community has built up, we find other developers helping with each others’ problems. Awesome! 

A lot of other services have hooks that will send campfire messages into specified rooms via the API on certain events. For example, Github has a hook that sends new issues & commit messages into our private developer room. There are a few more custom-built ones that I’ll get onto later! 

If you’re using ruby, I’d recommend checking out the Tinder gem, which provides a convenient wrapper to the REST API.

I can also highly recommend hubot, a Campfire bot written in Node by the Github team. It’s very simple to write extensions to perform simple actions like trigger server deployments.

Zendesk

$24/month per agent for standard version. While pretty expensive, Zendesk offers a variety of free trials, particularly for startups. It helps companies run email-based customer service desks, turning inbound emails into “tickets” that can be responded to or re-assigned across available agents.

It was the first customer service product we decided to use properly, and now forms the core of all of our written communication with customers - particularly email & twitter. It’s simple to add “tags” to tickets automatically or manually, so we can count the number of requests for “Repeat Billing” vs “European Rollout” at the end of every month. You can also get a bunch of metrics like response times & satisfaction rates, but at the moment we’re just focussing on keeping our heads above water!

We considered using the in-built Campfire integration to alert our Campfire room of new tickets, but quickly realised this would be more annoying than useful as we approached 30-40 new tickets per day.

Olark

$15/month for 1 operator. Discounts for startups. This provides live text chat on our website - the little “Chat Now” box you see in the bottom-right of your screen at gocardless.com. I was initially really skeptical of Olark’s value, but after using it for a month, I’m a convert. You can see where a user has come from and stay with them as they navigate through your site (including notification of which page they’re currently viewing), which makes it super easy to solve problems step-by-step.

With built-in Zendesk API integration, you can also save conversation transcripts in Zendesk as solved tickets and add “tags” to them - which makes running aggregated metrics really simple. It’s as simple as typing “!tag International” in the chat window. If you want to drill down even deeper, you can run metrics by page - eg 70% of queries from people viewing page X were about “Pricing”, whereas most people on page Y were looking for more information about “International Support”. 

We generally use Adium or a GChat plugin to access Olark.

Phone Support

The one weak link was our phone support - we previously used a Skype number that forwarded to mobile phones. Unfortunately, we had a number of problems with the solution - call quality was really low, sometimes calls didn’t forward or just randomly dropped, and collecting voicemail was a nightmare. I’d looked at the Zendesk voice features, but they were only available in the US & Canada.

Then, a couple of months ago, some guys from Twilio moved into an office nearby. I’d been wanting to try out their service for a while, so we decided to hack together a simple customer service phone app. A few hours later, we had a pretty decent phone system with voicemail.

What’s more, we’ve built-in a tool to check who’s available in our private Campfire room to take the call. If someone doesn’t pick up, it will try one of our other agents idling in Campfire. It also talks to our Zendesk ticketing system, notifying us if someone leaves an mp3 voicemail, so that someone always follows up. Sweet!

One small gripe is that voice-to-text transcription is terrible, to the point of being unusable. Maybe it’s my English accent, but I couldn’t get above a 50% accuracy rate. So we turned it off and stuck with voice recordings.

A landline number costs almost nothing, but the call forwarding to our agents’ (right now, me & my co-founders!) mobiles comes in at $0.14/min, which isn’t cheap.

I’ve posted the technical implementation details here

Conclusion

The key for us is that consumers can contact us however they like - phone, twitter, email or live chat - and we can respond quickly using tools that we’re familiar with. 

Metrics are also really important - we want to be able to track the number of queries by channel and easily categorise them by subject matter. This allows us to fine-tune our landing pages to address frequently asked questions more directly and also prioritise new product development according to customer need. But it’s vital that this we can do this tagging either automagically or at least in-line with whatever we’re doing. The “!tag International Expansion” in Olark is a good example of this. As soon as you need to open a separate list of “Feature Requests” and add a +1 to a tally chart, people stop using it.

The two weak points in this regard are Campfire and the Twilio voice app. For Campfire, we could do a text search on a month’s logs, or write a simple hubot extension (“!tag International”). With Twilio, I’ve still not come up with a good solution. Suggestions welcome!

If you want to work at a startup that hacks Twilio & Campfire apps together during the week, check out our jobs page!

Discuss this post at Hacker News

Home Brew Customer Phone Support

Feb 12, 2012

This is the technical part of a longer piece on “Automating Customer Service at a Startup” 

DIY Phone Support

We implemented a home-brew customer service phone line in a few hours with a basic Sinatra app on Heroku, using the Twilio-ruby gem to generate TwiML (saving us the pain of writing raw XML) and a simple Campfire gem called Tinder.

require 'twilio-ruby'
require 'tinder'
require 'net/http'
require 'uri'

The idea was to query our private Campfire room to see which agents are available, then render the relevant TwiML to get Twilio to forward any inbound customer calls to the phone number associated with that agent. If the agent doesn’t pick up the call with 15 seconds, it should try the next available agent, until all agents are exhausted, at which point it should record a voicemail.

known_agents = {
"Tom Blomfield"   => "+4477XXXXXXXX",
"Jim Beam"        => "+4477XXXXXXXX",
"Fred Flintstone" => "+4477XXXXXXXX",
# etc
}

campfire = Tinder::Campfire.new 'gocardless', :token => CAMPFIRE_TOKEN
room = campfire.find_room_by_id CAMPFIRE_ROOM

get '/start' do
  # Alert Campfire users that there's an incoming call
  room.speak "Incoming call from #{request["From"]}"
    
  response = Twilio::TwiML::Response.new do |r|
    r.Play "Welcome to GoCardless"
    r.Play "Please wait while we find someone to answer your call"
    r.Redirect "/call?user=0"
  end
  # Render the TwiML
  response.text
end

post '/call' do
  user_id = @params["user"].to_i

  # Query Campfire for available agents
  available_agents = known_agents.select do |name, phone| 
    room.users.map(&:name).include? name 
  end

  # Agent we're going to call
  agent_name   = available_agents.keys[user_id]
  agent_number = available_agents[agent_name]

  response = Twilio::TwiML::Response.new do |r|
    if agent_name
      r.Say "We're connecting you to #{agent_name}"
        
      # Alert Campfire users that we're calling someone
      room.speak "Calling #{agent_name} on #{agent_number}"

      # Tell Twilio to try to call the agent
      r.Dial agent_number, :callerId => '+44XXXXXXXXX', :timeout => 10

      # Fallback action if the agent doesn't answer
      r.Redirect "/call?user=#{user_id+1}"
    else
      # No agents left, redirect to voicemail
      r.Redirect "/voicemail"
    end
  end
  # Render the TwiML
  response.text
end 

One gotcha to be aware of is that Sinatra will evaluate the entire script when the server boots up; any variables you assign (eg, who’s in our Campfire room) will be set once, and used like constants. On the other hand, each “controller” method (eg get ’/start’) takes a block; this block is evaluated every time Sinatra serves a page. That’s why we ask for room.users inside the post ’/call’ block - it will make sure that the list of available agents is up to date.

In reality, we tweaked the above script a little; looping through 5 or 6 different agents could get a little irritating for a customer, so we limited it to 1 re-try before diverting to voicemail.

The voicemail script is pretty simple:

post '/voicemail' do
  # Tell Campfire
  room.speak "No-one answered call from #{request["From"]}!"

  response = Twilio::TwiML::Response.new do |r|
    r.Say "Unfortunately no one is around to take your call"
    r.Say "Please leave a message and we'll get back to you"
    r.Record :action => "/goodbye", :timeout => 10
    r.Redirect "/goodbye" # if no message is left
  end
  response.text
end

post '/goodbye' do
  # Tell campfire
  room.speak "Voicemail from #{request["From"]}.
    #{@params["RecordingUrl"]}.mp3"

  response = Twilio::TwiML::Response.new do |r|
    r.Say "Thanks for your message"
    r.Say "We'll get back to you as soon as we can. Goodbye"
  end
  response.text
end

This records an voicemail that’s stored at on Twilio’s servers, and we output a link to the recording in Campfire. But to be certain that someone deals with the voicemail, we wanted to log it as a ticket in Zendesk:

# Insert this into your "goodbye" block.

# Create a zendesk ticket
zendesk from: request["From"], recording: @params["RecordingUrl"]

The “zendesk” method is just a simple http post to the ZenDesk API using ‘net/http’, which creates a new Zendesk ticket. There is a Zendesk ruby gem, but it wasn’t well supported at the time of writing.

def zendesk(args)
  from = args[:from]
  recording = args[:recording]

  url = URI.parse "http://help.gocardless.com/tickets.json"
  req = Net::HTTP::Post.new(url.request_uri)
  req.body = {
    :ticket => {
      :subject => "New Voicemail from #{from}",
      :description => "New recording at #{recording}.mp3",
      :ticket_type_id => 33, # Zendesk code for voicemail!
    }
  }.to_json
  req.basic_auth ZENDESK_USERNAME, ZENDESK_PASSWORD
  req["Content-Type"]= "application/json"
  connection = Net::HTTP.new(url.host, url.port)
  response = connection.start do |http|
    timeout(5) { http.request(req) }
  end
end

That’s it!

One thing to note - all the Campfire and Zendesk API calls are performed synchronously, which increases response time and can leave the caller waiting around. At the moment, it’s usually imperceptible, but we’ll eventually move the API calls to asynchronous methods, probably using Redis.

To find out more about GoCardless, check out our site. If you’re a developer and want to play with Twilio & Campfire APIs at work, apply for a job! 

Automate Everything

Feb 4, 2012

Performing manual, repetitive tasks enrages me. I used to think this was a corollary of being a programmer, but I’ve come to suspect (or hope) that this behaviour is inherent in being human.

But being able to hack together scripts simply makes it much easier to go from a state of rage to a basic solution in a very small amount of time. As a side point, this is one of the reasons that teaching the basics of programming in schools is so important. It’s hard to think of any job which wouldn’t benefit from a few simple scripts to perform more automation.

When we’re hiring, even for non-developer roles, we look for this kind of mentality - it’s extremely useful, especially when building a software businesses, if costs don’t scale linearly with revenue. The more we can invest up-front in automation, the less time our team has to spend on performing stupid, manual tasks. As we add more employees, the benefits are compounded. And less rage generally makes the workplace a much happier place.

I encountered a practical example earlier this week. It was time to submit expense reports, and I could feel the rage starting to build up. For some reason, our accountant decreed that we had to fill in a spreadsheet, line-by-line, for every expense item. It looked a lot like this.

image

Presumably, hundreds of millions of people have to do this every month, costing millions hours of lost productivity. And in most companies, it’s because a well-meaning HR or Finance person has said that It Must Be Done. But if that Finance person had a modicum of programming experience, they might be minded to try to find a better way. That’s is what I mean by hiring people with this kind of mentality. We don’t want anyone who’ll lump some stupid task on the rest of the team because they’ve not got the mindset to think about automation in a sensible way. Even if they can’t programme the solution themselves, they need to be able to figure out pretty quickly that a better solution must exist

After briefly raging out, I decided to do something about this particular problem. 

1 - quickly define the requirements;

  • Record details of every expense item including date, amount & description
  • Be able to query the list to produce reports (by month and/or person submitting the receipt)
  • Keep copies of receipts for HMRC

2 - think about how receipts fit into our workflow at the moment, and the major problems

  • Receipts can be physical (till receipt for lunch) or electronic (Rackspace email)
  • They can pop up at random points in they day - not conveniently all at the same time every month
  • People have to store small pieces of paper for up to 30 days. Unreliable
  • People then have to spend an hour or two each month going through thousands of emails and hundreds of small pieces of paper to find the receipts, remember what they’re for, and write them down one-by-one. At least the spreadsheet auto-sums the amounts to a total… Inefficient!

3 - Apply a modicum of brainpower to automate the pain-points.

To avoid the unreliability of storing small pieces of paper and the inefficiency of examining thousands of emails, perhaps we could store them all in once place, electronically. This place could be an email account called something imaginative like “expenses@mycompany.com”. 

To avoid having to go through each receipt one-by-one, perhaps some kind of machine could parse relevant information out of each email and persist it somehow. Perhaps a database might be useful. To produce the report, perhaps the database could output certain information based on some kind of structured query language. So that the people at HRMC don’t suffer an instantaneous and fatal brain-explosion when we send them the data, perhaps we could separate values with a comma, save them all in a file and advise HRMC to open the file with their preferred Microsoft spreadsheet program.

As a side note; In the interests of not re-inventing the wheel, it’s generally a good idea to check if someone has solved this problem before. I decided to roll my own in this case because I was interested in learning about email handling after watching this great railscast from Ryan Bates. And because paying $9 per month per user for something I could probably write myself in a couple of hours seemed silly.

Technical Details

If you’re interested in the technical details, I used a ruby gem called Mailman, hosted on Heroku’s new cedar stack, to poll our POP3 mail server every minute. Attachments (pdfs, photos etc) are saved to AWS S3, and simple details of each receipt are stored in a postgres database. A Campfire gem called Tinder immediately alerts our company chat room that someone’s spent some of our hard-earned cash (just for amusement, really), and a very simple Rails app hosted on Heroku makes the data available in HTML or CSV, which can be queried by date-range or employee. 

Emails are required to have a line similar to “Amount 12.50”. Running OCR on photos of receipts and detecting the right line to take the Bill total from seemed like too much effort. We might switch to mechanical turk if people find this step troublesome.

If there’s any interest, I’m happy to stick the code on github.

Conclusion

Don’t put up with repetitive, manual tasks - automate them! It’s the jedi hacker way.

If you want to come and work at a place which encourages you to hack on cool projects to improve your colleagues’ lives, check out our jobs page

What kind of stuff do you automate? Discuss at Hacker News!