Tim Harvey :: Blog

Icon

I help organizations who feel stuck

Validate Ruby on Rails model entries with Cucumber

If you use Cucumber, chances are good that at some point in your testing, you needed to verify that a certain set of database records had been created. While this flies in the face of most BDD best practices, I’ve run into a few edge cases the last few months that required this level of minutia. Ideally, you’d use RSpec or Test::Unit for checking database/model level details, and let Cucumber focus on the higher level full stack checking.

In my case, I needed to verify that the admin tool I’m creating builds records necessary for the public side of the site (which hasn’t been built yet). There really wasn’t any visibility in the admin area to this data, except when it’s created. While I did have a solid set of RSpec tests for the model functionality, I felt it necessary to make sure those model methods were called correctly through the controllers and routing that went on.

As a result, I wrote this step:

Then /^I should find these "([^\"]*)" records$/ do |model_name, table|
  model_name.constantize.count.should == table.hashes.count
  table.hashes.each do |record|
    model_name.constantize.find(:first, :conditions => record).should_not be_nil
  end
end

You would use it in your Cucumber feature like this (intentionally bland example):

Then I should find these "Post" records
  | title          | permalink      | category   |
  | A blog article | a-blog-article | Technology |

The first row specifies the fields that will be used as conditions. The following rows will be used to search against. The step will make sure that at least record in the model matches. It will also verify that the number of records in the model matches the number of rows in the Cucumber feature.

Can you be friends with your clients? I think so.

Over the years, talking heads have told us that one should keep your business and personal life carefully separated. While I’ve generally agreed with that approach, recent shifts in my client base towards more ideal clients has given me opportunities to head in a new direction.

Getting our families together

Working with John King from Metta Audio has been my first experience with a truly ideal client. The work we do together has been extremely interesting and engaging, as well as being a joy to build.

20100120 MilesWe enjoy working together so much, and appreciate each other as fun people, that it’s been natural to connect outside of work. Our families have gotten together several times for dinner and play time. I have to admit, it helped a lot that John and his wife Betsy (who is a stellar photographer) are really open and genuine people. Just reading their blogs made me feel like I knew them. Reading about their experiences made it pretty obvious that we had a lot in common.

Building solid foundations

The really amazing thing I’ve found is that as we’ve become friends in addition to business associates, our relationship has strengthened. I suppose that sounds obvious, but it has rather significant implications for the long-term success of any business. While I don’t seek to be friends with John to manipulate him, there are several tangible business outcomes as a result of our growing friendship:

  • Loyalty – Because the business relationship is not based on some fleeting detail like price, I feel more comfortable than ever that John would come to me first when he needs help. I get to spend more time doing the work I love and less time building up my client base. Plus, I’m more loyal to John! I work hard to take care of his needs and have gladly learned some new tools to fit his requests.
  • Profitability – I’ve found that every new client needs some level of training on how you do business and how your services can best meet their needs. By fostering long-term clients, you reduce this non-billable time and effort. Plus, when a client really values what you offer (both in terms of who you are as well as what you do), you can both arrive at a mutually agreeable rate for your services that’s almost sure to be more than what you might feel comfortable asking when doing the first project for a new customer.
  • Fun – Don’t underestimate this one! I’ve had more fun the last six months working for my ideal clients that I ever have before. That’s resulted in me doing some of my best work, feeling less stress, and growing my skills tremendously. I think you’ll find that this does have a measurable financial impact. Clients can tell when you love what you do and they’ll want to be a part of it with you.

Hitting the museum

Spending time with your clients doesn’t have to involve dinner at your home (although the intimacy of that can be powerful). For our latest excursion, my wife Sara and I invited John’s family to join us for a trip to the fabulous Indianapolis Children’s museum.

20100120 JennaA brief side note is in order…spending time with my family is the most important thing on my list when I’m away from work. So the last thing I would do is drag my wife and kids along to some boring business get-together with a client. Instead, we planned something that the family would love and invited John and his family to join us! This is important, too. While your clients will appreciate you for taking care of them, they will adore you when you do special things with their kids. But remember, this all needs to be something you do because you want to and love it, not some cheap to do list to curry favor with a client.

20100120 Miles and John

So we spent several hours at the museum cruising around and playing with the kids. Afterwards, we introduced John to Skyline Chili for lunch. It was a terrific day and everyone had a great time. I think we talked shop for less than 5 minutes the whole day (Which is good! No shop talk would have been fine.).

Getting several clients together

I’m looking forward to going again when John’s wife Betsy can come. Plus, it didn’t work out this time, but for our next trip, I hope that Matt Outten from Squaremouth can bring his family. Matt is my latest ideal client and has been really enthusiastic about getting our families together. He’s even been gracious enough to invite our family to come have lunch at his office each week when his family comes!

If you haven’t picked up the message yet, the critical theme is to share, share, share what you have, whether it’s a fun trip, a friendship, or knowledge. Give it away, you won’t be sorry.

When building web apps, utilize proper indexing

Rails does a lot of crazy wonderful stuff. The downside is that all that awesome power gives you the ability to make some pretty dumb moves. Just because I can get the URL for one of the sites my Am I Down? users get alerts for by using…

current_user.sites[0].host.url

…doesn’t mean that I necessarily should. That command generates some heavy SQL, running a join across several tables (after running a separate query to get the current user):

SELECT "subscriptions"."id" AS t0_r0, "subscriptions"."user_id" AS t0_r1, "subscriptions"."host_id" AS t0_r2, "subscriptions"."alert_id" AS t0_r3, "subscriptions"."created_at" AS t0_r4, "subscriptions"."updated_at" AS t0_r5, "hosts"."id" AS t1_r0, "hosts"."url" AS t1_r1, "hosts"."created_at" AS t1_r2, "hosts"."updated_at" AS t1_r3 FROM "subscriptions" LEFT OUTER JOIN "hosts" ON "hosts".id = "subscriptions".host_id WHERE ("subscriptions".user_id = 35) ORDER BY subscriptions.updated_at DESC, hosts.url ASC

That’s going to take 21ms to run, just for a lookup. Is that a big deal? Perhaps not. Indexes (in the right situations) may make that SQL query run at an acceptable level.

Real world problems

In the case of Am I Down?, my service that alerts you when your web sites go down, I run some pretty slick lookups back through the logs for a web site to see if the change we see in a sites’ status is significant enough to warrant an alert. Since there are thousands of web site checks going on every hour (currently over 20,000 each day), a 21ms SQL statement is seriously bad. You can see the result below:

20100119-db-indexing-screenshot

The solution

The logging table didn’t have an index on the host_id, so it took forever (20ms or so) to find only the log entries for the current host, which for various reasons happens 8-10 times per check. So almost a 1/4 second is taken up by SQL checks every time I checked a site. You do the math and that means you can only sustain about 1,200 sites (240 checks per minute, every 5 min) for each checking worker. Not good.

You’ll see that the there’s a vertical bar towards the right of the graph, after which the bars drop off considerably. That would be the point I added an index for the host_id. Suddenly the time drops to about 3ms. That’s more like 3,600 sites for each checking worker. Much better!

Improving your Rails app

For a Rails app, the Rails_Indexes plugin offers a great way to track down any obvious indexes you may be missing. To speed up queries and reduce the number of queries you make, take a look at Bullet.

Client stats for 2009

As we begin the new year, I would encourage you to review your business and see what unexpected insights you might find. I’m not talking about another look at your products or services, but rather at your processes, finances, and customers.

Starting to work ON your business, not just IN it

It’s easy for me to spend the whole year thinking about code, forgetting that it’s just as important to work on the business as it is in the business (check out the terrific book, The E-Myth for more on this subject).

For me, a major revelation came when I looked at my top customers as a percentage of sales a few months back:

Google Chart

Tail wagging the dog

Wowzers! While it felt like I had a bunch of customer jobs going, the reality was that 3 clients kept everything going. Worst of all, my top client of 2009 was my least profitable and most demanding (It’s worth noting that they had some very good qualities as well: very consistent work and they always paid on-time).

Based on some thoughtful review of these stats, I made a few changes:

Putting long-term relationships first

I saw that too much of my work was provided by clients that needed the services I offered, but had little need for the “Tim-ness” I bring. Partially, that’s because I initially pursued the marketing and design agency niche. Unfortunately, their margins are very tight and for many of the players, web is an afterthought. There’s very little really innovative development going on locally. Clients aren’t asking for it yet.

More than anything else, my work needs to come from organizations that find me irreplaceable. Not in that lame, “I’m the only one who knows how it all works” sort of way. Rather, it’s about finding and working with people who really appreciate the unique talent, perspective, caring, and personality you bring to every project. It’s about brining so much enthusiasm and productivity that the client can’t believe their luck in finding you. Those are things that cannot easily be replaced by anyone else.

My work with John King from Metta Audio has this feel. We both really appreciate what we bring to the relationship. He’s been kind enough to tell me that the chemistry we have as we work together is as much or more valuable than my coding. Plus, I’m always thrilled to work with him on something new!

Demoting the tough client

My favorite book for self-employed consultants, Book Yourself Solid, reminds us that we have to drop our worst clients. They sap our energy and we end up doing poorer work for everyone. I’m not one to fire a client unless there’s something drastically wrong, I’d rather have them fire me. If the work isn’t worth it, I may have to raise my rates significantly to maintain profitability. That way, the client can still get help and I don’t need to complain. If the rate becomes unreasonable for the work they need, the client can replace me. Along those lines…

I’ve known for a while that I needed to change the relationship my top client. 90% of their work was supporting an existing environment, and that’s not really my strength. And because the work is support-related, it’s almost always urgent and interrupts scheduled projects for other clients. My frustration was a clear indication that my rate did not reflect the cost of doing business with this client.

As I mentioned in a previous blog post, I began the transition in October. I completed several open projects and started discussions about eliminating the retainer I was on and associated minimum hours. Since their budget had tightened, it turned out to be a natural move for them, shifting me over to a higher rate that allowed them to only call when I’m urgently needed.

I work hard to protect my time on scheduled client projects, so we also agreed on a minimum billable time when they call. This ensures that I’m compensated for time lost to the interruptions and ensures that they don’t call frivolously. With over a month under our belt with the new agreement, I think it’s been a real success. I’m pleased to take their calls and help when they need it, and they’re spending less. Best of all, I’m freed up to do more of the work that I love.

Increasing the apps available

In addition to client work, I want to add to and continue growing the subscription services I’ve created:

Focusing on larger projects

My business thrives on doing ongoing work for a small number of stellar clients. Part of that is due to my personality. I can really do effective work on one or two projects a day. If I’m pulled away to make calls and juggle 8 projects, trying to move each forward in the course of a day, it feels like a disaster. I enjoy the larger development projects more. They offer a more significant sense of accomplishment that designing lots of little marketing sites.

It looks like I’ll be able to transition over to just a few clients with ongoing needs in 2010. If everything goes as I suspect it will, I’m already completely booked through March, and likely into mid-year. That’s tremendously encouraging. I also had to start a waiting list for a couple potential clients that want some work done and were comfortable waiting for me to free up. It’s a pretty wonderful spot to be in.

Many of the relationships I worked hard to build in 2009 have resulted in requests for work. I’ve been pleased to refer many of these to other independent developers and designers in the area, helping them keep busy while taking care of my contacts. It’s always hard for me to turn down a project as I love to help people, but it’s what I have to do for my current clients.

It’s going to be a stellar 2010!