Coupa’s product team is hiring!

I’m putting this out there quickly even before it’s posted on our Jobs page. We’ve got 3 openings for key positions on our team. Here’s the lineup and all positions are San Mateo-based and include the goodies of salary, equity, health benefits, sweet Macbook Pro, snacks, nerf guns and more.

QA Lead
Someone that’s got experience or is ready to step up and be a QA Lead at a dynamic startup. Ideally has a great QA track record, including being hands-on in both manual testing and automation. The person will be responsible for defining all of our QA processes, building automation and managing our offshore QA resources. Objective is to ensure the highest quality of code continues to be delivered to our customers!

Rails Developer
We were one of the first (if not the first) enterprise apps companies to choose Ruby on Rails back in early ‘06. We love it and we’re looking for our next rockstar to drive the innovation and simplicity that has made Coupa successful. Ideal candidate loves working with the latest technologies, has a strong CS background, probably contributed to some open source projects, etc. You’ll be challenged for sure, and you’ll be a key contributor to our future.

iPhone Developer
We’ve got some great plans in mobile and looking for an iPhone developer to join us. You have experience developing beautiful mobile apps and ready for your next challenge. And you’ll probably get to do some Android work too. It’s going to be lots of fun!

Contact us via noah @ OR jobs@coupa.com.

Short note on a repeatable starting point for TDD

For testing to be useful, it needs to be repeatable.  Curious, here’s some notes I took when investigating what happens when you run ‘rake spec’.  This also would apply similarly for cucumber.
Rspec:
rspec specs can be run in various ways:
$ rake spec
$  spec spec
$ spec spec/controller/somefile_spec.rb
$  spec spec/controller/somefile_spec.rb:31  # specify a line number
$ autospec    # ( see  http://zentest.rubyforge.org/ZenTest/ for docs or even better, check  the source. )
Only the first command sets up the environment from scratch.  The other methods assume a working environment and if your tests assume a working environment and that assumption is invalid, then boom!

What happens in a rake spec?  Here’s what I see when I run it (edited for brevity and comments added)

$ rake --trace spec | grep Execute                #  ignore the Invoke lines.  we're only interested in what actually gets  run
(in /Users/eddykim/work/coupa_gitrepo)
** Execute environment
** Execute db:abort_if_pending_migrations
** Execute  db:test:prepare
** Execute db:test:purge        #-- drops all the  tables in the db
** Execute db:test:load          #-- creates the  tables, using schema.rb  (does not populate the tables with any data)
**  Execute db:schema:load     #-- is this really necessary for the test  db?
** Execute spec

The following is the bare minimum of things that need to be setup a clean and working database before you start your TDD mojo.

$ rake db:test:purge
$ rake  db:test:load
$ RAILS_ENV=test rake db:seed:load SEEDS='all'     # (This is a custom task in our project)
Notice that I skip running the db:schema:load which seems to be an unnecessary, time sucking task.  At which point you can run spec manually, or autospec if you so desire.
Additionally, the db:seeds:load task has a similar function as running rake db:fixtures:load.
Moral of the story:  Make sure your test database is in a known good starting state before running your tests.  But to keep your testing speed up, you don’t need to re-setup the db on every test run if you are using transactions to keep your test db clean.

HABTM Checkboxes, slightly revised

Many folks, including us, have made use of Ryan Bates’ RailsCast on updating HABTM relationships using checkboxes. We use a slightly revised version that is a little more helper-friendly, in that it doesn’t require any controller code.

Explanation first: You need the controller code in the first place because unchecked checkboxes don’t get submitted as part of your form. As a result, when none of a set of checkboxes are checked, your <habtm_assoc>_ids= method won’t get called, and any existing associations won’t get removed. (This is a bit insidious, and can be missed in testing.) To fix it, you need to make sure you end up with an appropriately empty (but non-nil) Array in your params hash.

So, as an alternative to this in your controller:

params[:product][:category_ids] ||= []

you can add an appropriately named nil hidden field in your view:

<!-- products/_form.rhtml -->
<%= hidden_field_tag "product[category_ids][]", nil %>
<% for category in Category.find(:all) %>
<div>
  <%= check_box_tag "product[category_ids][]", category.id, @product.categories.include?(category) %>
  <%= category.name %>
</div>
<% end %>

The hidden field will ensure that association removal happens as you intend it to, but will otherwise be ignored. Yay! Skinnier controllers, a pattern we can helperize, and working checkboxes, and it’s only a slightly ugly hack.

Wanna Work at Coupa?

Once again, we’re expanding our development team at Coupa.  Even if we want to hire lots of people, we find best success when we bring on 1 or 2 developers at a time.  More hands-on attention which benefits everyone in the long-term.  With that said, we want to find those 2 next superstar developers.

Our job specification is pretty straightforward.  You probably:

  • Want to be challenged intellectually
  • Be with a growing company with real customers and a real business model
  • Love Ruby on Rails (people with limited Rails experience need to have deep CS background and experience in a scripting language like Python)
  • Understand relational databases and how to make them scale – speed rocks
  • Play with javascript, prototype, scriptaculous, jQuery
  • Familiar with HTML/CSS and love creating pretty things

If this sounds like you or one of your friends, please get in touch with us.  Best way is to send an email to jobs@coupa.com.  It goes right to us.  The other good piece is that if you’re looking to move quickly, we’re ready.  We prioritize finding exceptional talent and making sure the interview process is quick, efficient and doesn’t waste your time (or ours).  Come join us!

Staying Agile in Customer Support & Development

2-plus years ago, when Coupa’s first customers came on board, we needed some way to communicate with them on issues and questions.  I had experience how Oracle did it and wasn’t about to go down that route.  Customers called in or logged issues directly into this homebuilt customer help desk system called Metalink.  Then Support spent days with a ticket trying to gather info and trying to reproduce.  If it was an actual code problem, maybe it got to Development a week later in another application called BugDB.  Support worked in Metalink, developers worked in BugDB and there was plenty hidden between the 2.  But that’s the support you get when paying 22% annually of your license fees.

At Coupa, we decided to go with a single system for help desk and issue/bug tracking.  Sometimes we don’t get it right the first time.  Our first solution was a SaaS system, but it lacked one of the main principles that Coupa was founded on:  simplicity.  Customers didn’t hate the system, but it wasn’t exactly user-friendly. Our development team detested it.  It slowed us down and that just couldn’t happen.  It was un-agile.

Graham, our development manager said (I’ll paraphrase), “It just blows….it’s fundamentally unintuitive.”

So one of our guys scanned the world via Google one night and stumbled upon a tiny company from Copenhagen named Zendesk.  They used Rails like us.  They were agile like us. They had free trials like us.  They were affordable like us.  In about 16 hours, our decision was made when our VP of Service Delivery sent an email:

From Ravi’s email…”this product is almost as cool as our app – freakin amazing”

We jettisoned that old system and turned on Zendesk immediately.  A couple months later, Zendesk named Coupa first in their list of customers in their seed investment press release.   It was great to see another Rails company succeeding.

Fast-forward to the present and Zendesk has grown significantly.  Now, headquartered in San Francisco, they have other great customers like Twitter, MSNBC, Scrib’d, IDEO and plenty others.   Most importantly, their solution is part of the collaboration that makes our customers successful and our development team agile.  The team doesn’t waste time on perfect classification of a ticket.  Instead, we get to work on it.  Besides that, it’s got simple and easy reporting, nice ways to communicate new features, and a collaborative forum for product enhancement ideas.  Our product roadmap gets influenced by the suggestions and the dialogue on our Zendesk site.  And we constantly track the percentage of suggestions that we implement and the pace that they’re done.

Without agile development, the cloud, Rails, and open source, I’m pretty sure that companies like Coupa and Zendesk wouldn’t have a fighting chance.  But with them, we’re changing the face of applications for businesses.

Don’t let IE7 ruin your weekend

Last Friday, around quitting time, fellow Coupa developer Ted and I were pairing up on some tickets and decided to attack one last issue before calling it a night. The ticket we landed on was one I had banged my head against for a few hours and only found dead ends. Even worse, it was a total show-stopper. Anyone using only our new Expensing application who clicks on the homepage navigation link in IE7 (which the majority of our customers use) is not directed to our home page – surprise! Instead they are prompted with a “File Download – Security Warning” dialog box and some text about “Type: Unknown File Type”. How bizarre! Clearly IE7 had no idea what to do with the response it received from our server. I clicked save from the prompt and examined the file download. It was our homepage, in all of its HTMLy wonder, now sitting alone and unrendered on the desktop.

My first thought had been – mime type! Having no way to evaluate response headers in IE, I took a shot in the dark and opened up Firebug. Unfortunately nothing was out of the ordinary. This wasn’t terribly surprising given that it does work in Firefox, but I suppose it had been worth a shot. Next I googled around and found that a similar issue exists (websites showing a download prompt instead of rendering in IE7) as a result of having Google Desktop Search installed and active. We don’t have that app (or much at all) installed on our IE-testing box, but whatever interaction had caused that sounded quite frightening. My mind was racing – could an unrelated process on this box be causing this link to download as a file? I calmed down a little when I remembered our QA team had reproduced this first on a different box. On a whim I tried manually appending the “.html” in the URL and – viola – the homepage rendered like a champ. At least I had a way to hack in a solution if I had to, but it was far from ideal.

This time what made the difference was a casual suggestion from Ted to use DebugBar to analyze the request/response Headers. I hadn’t heard of this tool but installed it immediately. A browser restart later I had it open, had reloaded the page and opened up the request and response headers. It was crazy – IE7 was asking for a jpeg (what?!) and we were responding with HTML but saying it was javascript (why!?). With that clue google quickly yielded this post from Hemant Khemani’s blog which clearly describes the exact problem we were having as well as the solution. Essentially IE was requesting a Content-Type of “images/jpeg”, which was not defined in our respond_to do block, so Rails chose our default format – which happens to be the first one you specify.

So the fix was simply to replace:

      respond_to do |format|
        format.js
        format.html {render :template => 'user/expense_only_home'}
      end

With:

      respond_to do |format|
        format.html {render :template => 'user/expense_only_home'}
        format.js
      end

This ensures that your default response header will be HTML. And from now on I will be checking that all of our respond_to blocks for this specific kind of sanity. So in the end, in about 20 minutes we had solved a problem I had previously struggled with for hours and all but given up on. A definite win for pair programming, for DebugBar and for my weekend.

Abstraction Breakdown: Table Aliasing in ActiveRecord

A few things I love about Rails:

  • It strongly encourages you to put code in the right place MVC-wise. This feels good, but even more importantly, it enhances maintainability. (This is true both for other developers that might inherit your code and for three-weeks-from-now you.)
  • The abstractions are powerful, but don’t have too many layers, so if some behavior surprises you, it’s easy to go look at what is actually going on. The one-two punch of open sourciness and simplicity, combined with the inherent readability and conciseness of the Ruby language, makes for productive debugging.
  • Ruby is well-suited to creating DSLs and other forms of metacode that let us as developers solve a problem and then reuse the solution on different data sets with different parameters.

And an involved little problem where these break down:

Within our app, we have lots of places where we need searchable, sortable, configurable views of tabular data. While this is a common enough requirement, it’s one that folks tackle in all sorts of different ways, from pure Javascript implementations to scaffolding generators. Because we wanted the code to remain DRY and maintainable as much as possible, and yet be individually securable via controller/action pairs, we opted to build something somewhere in the middle.

A simplified example declaration, which you might find in WarehouseTypesController:

data_table :warehouse_type,
 [:name,
 :description,
 :created_by,
 {:key => :created_at, :label => "Created Date"},
 {:key => :updated_by, :label => "Last Updated By" },
 {:key => :updated_at, :label => "Last Updated Date"}],
 :find_options => {:include => [:created_by, :updated_by]}

Each table has a base model (in this case, WarehouseType), where each row of the table represents one record in that model’s database table. The array that follows describes the columns to include and/or search in the table, and the find_options are identical to what you’d pass into an ActiveRecord find call. The data_table method itself generates some methods in the host controller that handle rendering, various AJAX actions and CSV export. The :created_by and :updated_by keys refer to associations defined in the model:

class WarehouseType < ActiveRecord::Base
 belongs_to :created_by, :class_name => 'User', :foreign_key => 'created_by'
 belongs_to :updated_by, :class_name => 'User', :foreign_key => 'updated_by'

A call to render_warehouse_type_data_table will produce something like this:

Data Table Example

And the advanced search pictured above leads pretty directly to the following SQL:

SELECT * FROM `warehouse_types`
WHERE (warehouse_types.name LIKE 'storage%')
ORDER BY warehouse_types.name DESC
LIMIT 0, 20

Straightforward, right? Well, it breaks down even in the above example when you try to query on the “updated_by” user, because suddenly we may or may not be joining to the users table twice, and any SQL we generate needs to know what table alias to use in the condition. Internally, ActiveRecord uses a table’s name as the alias the first time a table is joined into a query, and then uses an algorithm involving the relevant association names to generate aliases for subsequent joins. You don’t need to know this as long as you’re using the nested hash version of find(), but that doesn’t currently support any tests other than equality, range, and set inclusion. Consequently, for anything more complicated (pretty much all queries we run), we need to use the SQL fragment / array form of find() to prepare queries. A naive version of a find() call for warehouse types that were last updated by John might look like this:

WarehouseType.find(:all,:conditions => ['users.fullname LIKE ?','John%'],
                        :include => [:created_by,:updated_by]

which would generate something like this:

SELECT [redacted] FROM `warehouse_types`
LEFT OUTER JOIN `users` ON `users`.id = `warehouse_types`.created_by
LEFT OUTER JOIN `users` updated_bies_warehouse_types
 ON `updated_bies_warehouse_types`.id = `warehouse_types`.updated_by
WHERE (users.fullname LIKE 'John%')

This won’t work, though- the WHERE clause is using the wrong alias, and will look at created_by users instead. The find() call would need to look like this:

WarehouseType.find(:all,:conditions => ['updated_bies_warehouse_types.fullname LIKE ?','John%'],
                        :include => [:created_by,:updated_by]

The thing is, though, we don’t really know what alias to use. If there’s a default_scope defined, for example, the user table may already be joined, or the query may already be scoped in some other way. Also, the outer joins may or may not actually get generated in the order specified, especially if they’re included as a Hash rather than an Array. (Like most folks with dependencies on external libraries, we can’t migrate to Ruby 1.9 yet.) Our current solution to this problem is indeed to assume particular scopes and a particular ordering to the joins, leading to a data_table column declaration more like this:

 {:key => :updated_by, :label => "Last Updated By",
  :sql_column => 'updated_bies_warehouse_types.fullname' },

I really don’t like this solution, though- it’s brittle, and it requires too much knowledge of both ActiveRecord’s implementation and any additional factors that may modify the model’s scope. This is not knowledge that something in the controller should have. It’s also tricky to test in a low-level way.

Before getting onto the possible solutions, I should point out that we’re certainly not the only ones to run into or describe this problem- there’s more succinct discussion in Rails tickets 2357 and 2087, including a proposal for and code for potential solution #1 below.

So, what do we do? At the moment, nothing- it happens seldom enough in our table definitions that we can get away with hard-coding the exceptions for now, but it limits functionality we can add and is just ugly. Essentially there are 3 somewhat reasonable approaches to actually fixing the problem:

  1. Introduce a way to query what the expected table alias for a particular situation is going to be
  2. Implement operations other than == in the nested-hash form of find(), as in the relational algebra code that may be merged into AR eventually.
  3. Replace alias references with locally-scoped tokens that represent the joins/includes, and substitute in the correct aliases when merging conditions.

These are all quite intrusive, however; the way that ActiveRecord’s merge_conditions calls work today, it doesn’t know anything structured about the JoinCondition objects, so even something simple would be a nasty hack. Given how central this code is, any change would need to be both well-tested and quite speedy. My personal preference is for #3, and when I have some spare time I may try coding this up sometime soon. My hunter in the Outlands may take precedence, though…