Testing Clients to an HTTP API in Go

An example on how to test a client which calls out to an external API, without requiring the API server to be up and running. From Testing Techniques video by Andrew Gerrand at Google I/O 2014.

Go has a standard library net/http/httptest which you can use to create a test HTTP server, similar to Go’s normal HTTP server. The test server will create a server that listens locally on a random port.

A test consists of

  • Create test server
  • Setup server to return what you want
  • Pass server URL to your test

Here’s a simple example from my fetcher library which tests retrieving a URL with a GET parameter.

I setup my test server to just echo back whatever parameter gets passed in, so then I just need to confirm what I pass in, is what I get back.

Create test fetcher_test.go and run from my package dir using go test

// This tests GET request with passing in a parameter.
func TestGetParams(t *testing.T) {

    // echoHandler, passes back form parameter p
    echoHandler := func( w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, r.FormValue("p"))
    }

    // create test server with handler
    ts := httptest.NewServer(http.HandlerFunc(echoHandler))
    defer ts.Close()

    // call library I want to test, using test server ts
    f := fetcher.NewFetcher()
    f.Params.Add("p", "hello")
    result, err := f.Fetch(ts.URL, "GET")
    if err != nil {
        t.Errorf("Error: %v", err)
    }

    // confirm result
    if result != "hello" {
        t.Errorf("Unexpected result: %v", result)
    }
}

That’s all there is to it, check out the video for a ton more useful information around testing in Go. Plus read the testing and httptest package documentation.

Midterm Elections

Voter turnout for midterm elections is significantly less than a Presidential election. A look at why turnout drops, which has done since the 1840’s and who it might help.

Midterm elections are just as important, voting for Governors, Senators, House and numerous Propositions may effect your life more than a President ever will. Remember all politics are local, and that is who you are voting for midterm.

My company, Automattic, is working with Pew Charitable Trusts and Google to help improve midterm election turnout. See what you can do to help

Find your ballot and polling information below, election day is Nov 4th.

Long Beach Half-Marathon

IMG_20141012_071627

I signed up with my family to run the Long Beach half-marathon in December. The race was over 10-months away when we planned, so plenty of time to train and prepare for it. I’ve run several half-marathon’s previously but didn’t run at all in 2013, so a good excuse to get back at it.

The deadline was too far in the future and I kept delaying getting started, my first training run wasn’t until June; adding it to my running spreadsheet the previous run was Sept 2012, a 20 month gap!

For training, I only got out and ran 16 times over the next 4 months. My total training was 54 miles, the longest run being only 5 miles, the average run just over 3 miles. So going into the race, I felt a bit under-prepared and a little nervous but since I committed to doing it I was going to give it a shot, at the worst I could walk.

My training plan, start slow and taper off

IMG_20141010_150121

The day before the race, my daughter said she wanted to give me a sticker to wear during the race. You can see it in the above selfie, she just got a pack of Frozen stickers, so giving one up was kinda of a big deal. She gave me the sticker of the family, so I’ll have my family with me while running. Sometimes 3-year olds can really surprise you.

Later that night, while sitting quietly getting ready for bed, she said with all seriousness, “Daddy, I need to tell you something”. I was curious what was so important that she had to tell me she was going to tell me something, “What is it, honey?” She said, “I can do this with my tongue” and preceded to stick it out and wiggle it all around. Too funny!

It was remembering this, and my family was to be at the end waiting to cheer. Remembering advice from Crazy Running Guy to take it slow, and co-workers wishing me well. Confidence that I’ve finished races before and trusting somehow my body still remembered how, even after such a long break.

All of this got me to the finish in 2 hours 24 min 28 secs, my slowest half-marathon to date, but considering my lack of training I was happy to finish. The donut holes at mile 10 also helped, thanks Long Beach Triathlon Club!

I’m sore today, but feel good I finished and looking forward to getting back into a better training cycle. My next race I’m aiming for a PR!

Here are my times from the seven half-marathons I’ve completed:

Oct 28, 2001 Silicon Valley Half Marathon 1h 53m 03s
July 31, 2005 SF Half Marathon (2nd half) 1h 51m 16s
Oct 30, 2005 Silicon Valley Half Marathon 1h 58m 52s
Oct 14, 2007 San Jose Rock ‘n Roll 2h 00m 22s
Aug 3, 2008 SF Half Marathon (1st half) 2h 03m 42s
Oct 9, 2011 Long Beach Half Marathon 2h 9m 30s
July 29, 2012 SF Half Marathon (2nd half) 2h 07m 41s
Oct 12, 2014 Long Beach Half Marathon 2h 24m 28s

I really need to blog more

most-likes-in-one-day

I’m not sure if the WordPress notification is supposed to be encouraging or discouraging. I know it’s one of our achievements, so should be celebrated but really, my best day was only 4 likes previously.

Twitter has an equally discouraging/encouraging message on their analytics page. One of the challenges with analytics, social media and displaying information in an easy to understand way. It makes it easy to understand, even when it isn’t good. Data needs to learn the old adage, if you don’t have anything nice to say…

twitter-engagement

I understand marketers and businesses need the analytics data, but still find it funny when looking at it from a personal account. Am I really trying to grow this!? My replies are up but 28-day retweet metric is dropping. Hopefully my kids halloween costume garners more retweets!

A Day in the Life

I wake up at 5:30am, the girls got my body trained for this time, and even with them now sleeping in I can’t break the habit.

I make my morning coffee, get a good stretch in and read my morning news, its a Monday so I check in how all my fantasy teams did and read more on how great that 18-inning Giants game was, epic!

I’m on Team Tinker, which is a bit of an R&D special products team, we are ramping up the work on a really cool new product. I’m building out the backend API and excited to be using the Go language. This week I hope to really get cranking, so I figuratively cut out all our work by opening up a dozen or so issues in Github.

Around 6:30am, my 2-year old wakes up so I hold her while I finish opening the last couple of issues and she has her morning milk. She likes to cuddle while I’m at the computer, who am I to complain. Her sister also wakes up and hangs out with me in the office as part of our morning routine. I read them a book and start to get us all ready for the day.

The girls don’t go to pre-school today, so we have a bit slower start getting breakfast and changed before my wife comes back from her workout and takes over. Also, an electrician is coming by our house today to look at a broken ceiling fan, so I stick around to talk with him.

While waiting, I chat with some teammates on Slack, a couple are in Europe, and I’m in California, so the morning time is the best to talk about the project, what they are working on and our plan of action. Lots to hammer out early as we define the APIs. I also document on our team’s internal blog a conversation I had last week discussing storage options.

Where to work today?

We have an office up in San Francisco, which I live about 30-45 minutes away. It’s a day-to-day decision if I’m going to work up there or down on the Peninsula. I have a co-working space locally but it is pretty quiet and a little lonely. Our office may only be a handful of people, bit it is lively and fun place to work. However, today there is a big Giants game during the day and I don’t feel like fighting traffic, so I’ll work locally.

Around 9:30am I head to my local space and dive into development, ok, maybe a little reddit, Hacker News and another quick sports article to get my mind focused. My work environment is relatively minimalist – a normal desk, my laptop is a Macbook Pro and I live in Terminal and Textmate. Since I’m building an API, barely even need a browser. I normally listen to music or podcasts while I work, on Monday catch up with TWiT podcast, but they’re talking too much about Windows 10, so switch back to Spotify.

After lunch, I’m helping out one of my co-workers on a small project he’s doing, which will be part of our holiday surprise for the company. I’m helping him put together a post about it. A cool thing is we have a team working on an awesome new editor which is what I use for building the post. Dog fooding FTW! The editor is a joy to use especially with multimedia content. I do discover a few bugs and report them back to the team, but really excited for the new tool to launch.

As I continue to work, I start to fade and find myself being distracted by the Giants game which just started up. I decide to go get a cup of coffee and actually head home to watch the game. I figure its easier to have the game on in the background and work instead of switching tabs and reload constantly.

I come home to a couple of screams and big hugs, which answers the “are they napping” question. At home, I work on this post and setting up a development server to run our test API site, neither of which require huge chunks of concentration; a few minor interruptions by baseball and kids aren’t a big deal.

I typically put away my computer around 5pm and focus on playing with the kids, dinner, baths, getting ready for bed and all the typical evening with family stuff. Today’s Monday, so I’ll go to my bowling league with a buddy, otherwise I might get back on my computer a little while watching TV. Most evening time is mindless browsing and I’ve been trying to cut more of it.

Tonight I won’t get to bed until midnight or so, which even that doesn’t help me sleep past 5:30am, typically I’m in bed around 10pm.

So that’s my Monday. It isn’t a typical day of work, but one of the benefits of Automattic is a flexible schedule. A co-worker shifted his hours all summer so he could spend his days playing with his kids; others travel and work from whatever cafe or spot they end up.

If this sounds interesting, come work with us. If you want to read more in our “A Day in the Life” series, check out the #a8cday tag where other co-workers are documenting their day.

Karkle WTF, a web comic

For some bizarre reason I started a web comic. I’m calling it “Karkle WTF!?“. I’ve been a fan of comics for years and have always doodled but never really tried building something in such a creative and expressive format. My photography and even various sites, apps and writing have all been a bit more technical.

The comic series will be a bit technical too. I’m using SVG, HTML5 and even a bit of D3.js and some other bits depending on the strip. The focus though will be making them fun and interesting and not just gee whiz for its own sake.

I haven’t seen many comics taking advantage of the web medium nearly as much as they might. Using SVG and standard HTML and CSS, the panels are responsive and should look just as good on mobile and retina displays.

The comic will also be a good outlet to explore some newer front-end, graphics and display technologies. A lot is going on with animation, javascript and rich interactive apps, not quite sure how this will translate but will be interesting to toy with and how it can add to the stories.

So we’ll see how it goes, it’ll be a fun experiment. I’ll be happy with a laugh or two, or even just a half-smile.

Eliminate Work in Progress

I was listening to the recent Bootstrapped with Kids podcast and they were talking about doing too many things at once and the pain it was having on the business.

A good listen to hear how development methodology effects the business. A few key tenets of software development; doing small releases, eliminate work in progress and deploy frequently. If you have finished code, don’t wait and sit on it, deliver it to your customers. Deploy.

Here is an example my colleague Ken and I would use to explain on why doing too many things at once is not ideal.

Three Projects Illustrated

Imagine you have three projects each of which is about the equal amount of time and each will deliver some value at their completion. For the sake of argument, let’s say each takes a month to complete and each will deliver a $1,000/mo.

Scenario One: Asynchronous – Multi-Tasking

One way of attacking these projects, it to break them up into small chunks and start work on them simultaneously. If you were orderly and consistent, the work might look like this.

Each project gets a little bit of time and at the end of the three months all projects are complete. This process is most commonly used when trying to optimize resources, you might hear “We can’t have anyone not working, have them start next thing while they wait”

Scenario Two: Synchronous – One after the Other

An alternate scenario is to tackle each project until it is complete and shipped; not starting the next one until it is out the door. This scenario would look like this.

Notice, after month one Project A starts realizing value and earns $1,000 in month two and another $1,000 in month three. Project B would get completed in month two and earns $1,000 in month three. At the end of three months, all projects are complete, but doing the projects one-at-a-time delivered an extra $3,000 since it was shipping completed Project A and B.

In Reality

Most people do realize this and a true Scenario One is not really attempted. Here is a more realistic scenario, a team starts off on the same project together. As people finish and hand-off their piece, they move on to the next.

For example Design finishes mocks and hands to Engineering, so Design starts on next project. However, if Engineering asks a question or needs the mocks updated with new requirement, the Design team is busy. So Engineering submits a design request and instead of being idle they start on the next project.

This might look like:

This ends up delivering the same results as the first scenario, which is none until the end of the three months. The real killer is when there are a bunch of projects almost done and just need that little bit more to finish up, be it a approval, production environment, wordsmithing or whatever.

Worrying about the “idle” time. Management gets worried when people appear to be idle, but there is always work to be done. Odd requests from last project, clean up work, polishing up hacks or helping colleagues. All this needs to get done and rarely scheduled. Also a little down time allows people to recoup, tidy up your own work environment, learning a new tool or tutorial. All beneficial.

A way I’ve seen successful is when teams are organized not by function, but by project. So there is no Design or Engineering team but X product team which has all the necessary team members. This works well because as a team they have one goal and will work to complete the overall goal and not just the bits and pieces of work in progress.

Summary

In summary, try to eliminate as much work in progress. Focus the entire team to deliver value in smaller chunks and at a faster rate. Finish up those little bits and get it shipped. This delivers value quickly to your customers and you start realizing it sooner.

Python Argparse Cookbook

I use Python as my go to tool for command-line scripts. So I find myself often parsing command-line arguments for these script. Since I use various programming languages I don’t remember anything, so I’m constantly looking at documentation and sadly, Python docs are a bit challenging to read.

So, in a similar vein as my Python String Format Cookbook, I wrote my own docs for argument parsing which I’ll use myself as a reference and hopefully useful to you.

What Library to Use

I don’t know the history but there are a couple of different standard libraries that can be used for parsing arguments. The one you want to use is argparse module. Similiar in name to optparse which is the older deprecated module.

Also confusingly there is a getopt module which handles more or less the same parsing of command-line arguements but is a bit more complicated and usually requires more code to be written.

Basic Example

First, up a simple example which is just grabbing a single argument and no flags or other parameters passed in. For this case, you can just use sys.argv array which contains all of the parameters passed in.

The first element in sys.argv is the script itself. So a parameter passed in will be in the second element: sys.argv[1]

import sys

if len(sys.argv) > 1:
    print( "~ Script: " + sys.argv[0] )
    print( "~ Arg   : " + sys.argv[1] )
else:
    print(" No arguments ")

Saving as test.py and running gives:

$ python test.py Foo
~ Script: test.py
~ Arg   : Foo

Multiple Arguments with sys.argv

Since sys.argv is simply a list, you can grab blocks of arguments togther or slice around as you would any other list.

Last argument: sys.argv[-1]

All args after first: " ".join(sys.argv[2:])

Flag Parameters

You need to start using a module when you want to start including flags such as --help or want to have optional arguments, or varying length parameters. As mentioned, the best standard module to use is argparse.

Help and Verbose Examples

import argparse

parser = argparse.ArgumentParser(description='Demo')
parser.add_argument('--verbose',
    action='store_true',
    help='verbose flag' )

args = parser.parse_args()

if args.verbose:
    print("~ Verbose!")
else:
    print("~ Not so verbose")

Here’s how to run the above example:

$ python test.py
~ Not so verbose

$ python test.py --verbose
~ Verbose!

The action parameter tells argparse to store true if the flag is found, otherwise it stores false. Also a great thing about using argparse is you get built-in help. You can try it out by passing in an unknown parameter, -h or --help

$ python test.py --help
usage: test.py [-h] [--verbose]

Demo

optional arguments:
  -h, --help  show this help message and exit
    --verbose   verbose output

A side effect of using argparse, you will get an error if a user passes in a command-line argument not expected, this includes flags or just an extra argument.

$ python test.py filename
usage: test.py [-h] [--verbose]
test.py: error: unrecognized arguments: filename

Multiple, Short or Long Flags

You can specify multiple flags for one argument, typically this is down with short and long flags, such as --verbose and -v

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--verbose', '-v',
    action='store_true',
    help='verbose flag' )

args = parser.parse_args()

if args.verbose:
    print("~ Verbose!")
else:
    print("~ Not so verbose")

Required Flags

You can make a flag required by setting, required=True this will cause an error if the flag is not specified

parser = argparse.ArgumentParser()
parser.add_argument('--limit', required=True, type=int)
args = parser.parse_args()

Positional Arguments

The examples so far have been about flags, parameters starting with --, argparse also handles the positional args which are just specified without the flag. Here’s an example to illustrate.

parser = argparse.ArgumentParser()
parser.add_argument('filename')
args = parser.parse_args()

print("~ Filename: {}".format(args.filename))

Output:

$ python test.py filename.txt
~ Filename: filename.txt

Number of Arguments

Argparse determines the number of argument based on the action specified, for our verbose example, the store_true action takes no arguments. By default, argparse will look for a single argument, shown above in the filename example.

If you want your parameter to accept a list of items you can specify nargs=n for how many arguments to accept.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('nums', nargs=2)
args = parser.parse_args()

print("~ Nums: {}".format(args.nums))

Output:

$ python test.py 5 2
~ Nums: ['5', '2']

Variable Number of Parameters

The nargs argument accepts a couple of extra special parameters. If you want the argument to accept all of the parameters, you can use * which will return all parameters if present, or empty list if none.

parser = argparse.ArgumentParser()
parser.add_argument('nums', nargs='*')
args = parser.parse_args()

print("~ Nums: {}".format(args.nums))

Output:

$ python test.py 5 2 4
~ Nums: ['5', '2', '4']

If you want to require, 1 or more parameters, use nargs='+'

Positional arguments are determine by the position specified. This can be combined with the nargs='*' for example if you want to define a filename and a list of values to store.

parser = argparse.ArgumentParser()
parser.add_argument('filename')
parser.add_argument('nums', nargs='*')
args = parser.parse_args()

print("~ Filename: {}".format(args.filename))
print("~ Nums: {}".format(args.nums))

Output:

$ python test.py file.txt 5 2 4
~ Fileanme: file.txt
~ Nums: ['5', '2', '4']

You can also specify nargs='?' if you want to make a positional argument optional, but you need to be careful how you combine ? and * parameters, especially if you put an optional positional parameter before another one.

This makes sense, not requiring the last args:

parser = argparse.ArgumentParser()
parser.add_argument('filename')
parser.add_argument('nums', nargs='?')
args = parser.parse_args()

Output:

$ python test.py test.txt 3 
~ Filename: test.txt
~ Nums: 3

$ python test.py test.txt
~ Filename: test.txt
~ Nums: None

However, using the nargs='?' first will give unexpected results when arguments are missing, for example:

parser = argparse.ArgumentParser()
parser.add_argument('filename', nargs='?')
parser.add_argument('nums', nargs='*')
args = parser.parse_args()

Output:

$ python test.py 3 2 1 
~ Filename: 3
~ Nums: ['2', '1']

You can use nargs with flag arguments as well

parser = argparse.ArgumentParser()
parser.add_argument('--geo', nargs=2)
parser.add_argument('--pos', nargs=2)
parser.add_argument('type')
args = parser.parse_args()

Output:

$ python test.py --geo 5 10 --pos 100 50 square
~ Geo: ['5', '10']
~ Pos: ['100', '50']
~ Type: square

Variable Type

You might notice that the parameters passed in are being treated like strings and not numbers, you can specify the variable type by specifying type=int. By specifying the type, argparse will also fail if an invalid type is passed in.

parser = argparse.ArgumentParser()
parser.add_argument('nums', nargs=2, type=int)
args = parser.parse_args()

print("~ Nums: {}".format(args.nums))

Output:

$ python test.py 5 2
~ Nums: [5, 2]

File Types

Argparse has some built in filetypes which makes it easier to open files specified on the command line. Here’s an example reading a file, you can do the same writing a file.

parser = argparse.ArgumentParser()
parser.add_argument('f', type=argparse.FileType('r'))
args = parser.parse_args()

for line in args.f:
    print( line.strip() )

Default Value

You may specify a default value if the user does not pass one in. Here’s an example using a flag.

parser = argparse.ArgumentParser()
parser.add_argument('--limit', default=5, type=int)
args = parser.parse_args()

print("~ Limit: {}".format(args.limit))

Output:

$ python test.py
~ Limit: 5

Remainder

If want to gather the extra arguments passed in, you can use remainder which gathers up all arguments not specified into a list

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--verbose',
    action='store_true',
    help='verbose flag' )
parser.add_argument('args', nargs=argparse.REMAINDER)
args = parser.parse_args()

print(args.args)

Specifying remainder will create a list of all remaining arguments:

$ python test.py --verbose foo bar
['foo', 'bar']

Actions

The default action is to assign the variable specified, but there are a couple of other actions that can be specified.

Booleans

We have already seen the boolean flag action which is action='store_true' which also has a counter action for action='store_false'

Count

You can use the count action, which will return how many times a flag was called, this can be useful for verbosity or silent flags

parser = argparse.ArgumentParser()
parser.add_argument('--verbose', '-v', action='count')
args = parser.parse_args()

print("~ Verbose: {}".format(args.verbose))

Output:

$ python test.py
~ Verbose: None 

$ python test.py --verbose
~ Verbose: 1

$ python test.py --verbose -v --verbose
~ Verbose: 3

Append

You can also use the append action to create a list if multiple flags are passed in.

parser = argparse.ArgumentParser()
parser.add_argument('-c', action='append')
args = parser.parse_args()

print("~ C: {}".format(args.c))

Output:

$ python test.py
~ C: None

$ python test.py -c hi
~ C: ['hi']

$ python test.py -c hi -c hello -c hey
~ C: ['hi', 'hello', 'hey']

Choices

If you only want a set of allowed values to be used, you can set the choices list, which will display an error if invalid entry.

parser = argparse.ArgumentParser(prog='roshambo.py')
parser.add_argument('throw', choices=['rock', 'paper', 'scissors'])
args = parser.parse_args()

print("~ Throw: {}".format(args.throw))

Examples

I’ll end with two examples, many of the example above are not as complete I would use in reality, they were kept short to focus on the idea being illustrated.

Copy Script Example

import argparse
import sys

parser = argparse.ArgumentParser(description='script to copy one file to another')

parser.add_argument('-v', '--verbose',
    action="store_true",
    help="verbose output" )

parser.add_argument('-R',
    action="store_false",
    help="Copy all files and directories recursively")

parser.add_argument('infile',
    type=argparse.FileType('r'), 
    help="file to be copied")

parser.add_argument('outfile',
    type=argparse.FileType('w'),
    help="file to be created")

args = parser.parse_args()

Bug Script Example

Here is an example for a script that closes a bug

import argparse
import sys

parser = argparse.ArgumentParser(description='close bug')

parser.add_argument('-v', '--verbose',
    action="store_true",
    help="verbose output" )

parser.add_argument('-s',
    default="closed",
    choices=['closed', 'wontfix', 'notabug'],
    help="bug status")

parser.add_argument('bugnum',
    type=int,
    help="Bug number to be closed")

parser.add_argument('message',
    nargs='*',
    help="optional message")

args = parser.parse_args()

print("~ Bug Num: {}".format(args.bugnum))
print("~ Verbose: {}".format(args.verbose))
print("~ Status : {}".format(arg.s))
print("~ Message: {}".format(" ".join(args.message)))

Resources

Customer surveys, and the lies they tell

In the past few years I’ve traded in and purchased two new cars, because kids. Each time the process was pretty much the same, long and arduous. For our minivan it took about 6 hours total, and for the most recent sedan it took 3 hours.

It took shorter the second time because I asked them to start the appraisal process before I did any test drives or even started talking. It was in my opening hello.

Them: “Hi, how can we help you today?”

Me: “I want to buy a new car and trading in this car, let’s start the appraisal process now.”

However, each time, after negotiating and deciding on a final price for both trade-in and new car purchase it still took another 90 minutes to complete the transaction. This is the part that absolutely baffles me. There is nothing to do but draw up paperwork, sign and be done. Once I get into the finance guys office it is all done in 15 minutes. I have no idea what happens in the dead 75 minutes between.

The Salesman says its nothing he can do and apologizes its taking too long, he’s my buddy.

At the end of it, all paperwork signed and I finally get the keys; the Salesman takes me out to the car and shows me the other features. This is when he lays on the story about I should get a call from headquarters asking how everything went today and Honda really looks down on anything that is not 5-stars. So if you could answer everything with 5-stars, that’d be great.

So, the Salesman has been my guy throughout the day, making sure I’m comfortable, making sure I’m taken care of, etc. It’s always the Manager or someone else causing the delays. So now I don’t want to give my guy a bad review.

So two days later, when I got the call from Honda, sure enough I give them 5-star reviews. The dealership was great, no problems. Pleasure doing business with them. I’d recommend them.

There are a few other reasons I give them 5-stars for everything. First, the person on the phone wants me to give 5-stars, otherwise they’ll have to ask follow-up questions and elaborate. I really don’t want to elaborate on the survey, I just want to finish it so they can check my name off and not call me again.

Secondly, I think if I don’t give them 5-stars they may try to follow up with me later to help me. I don’t have any problems and don’t need help, beyond not wanting to spend six hours at a dealership to buy a car.

Thirdly, I don’t think there is any way my answers on this survey will have any effect on the car sales process. Saturn attempted to sell cars differently, they failed. Tesla is in lawsuits galore about trying to change the car buying process. Do I really think my 5-minute survey is going to change that, nope. So why trouble myself to be honest, just answer the questions and get off the phone.

So now, what did Honda learn from that customer survey? Absolutely nothing, but everyone involved sure feels better about themselves. The dealership can brag about posting up 5-star reviews, and HQ feels happy that everything is just humming along fine. Stick head back in sand.

My point of the article is not that buying a car sucks, everyone knows it does. The point is look at why are you asking customers for feedback and surveys. How are you using what they give you, is it really to solve a problem, or is it to judge how your doing?

If you are really what to know, dog food it. If I were Honda and wanted to know how the car buying process is, I’d send the people making phone surveys and have them go out and buy cars from dealerships and see what it’s like. Though this might take them too much time.