<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Coupa Cabana&#187; Staying Agile in Customer Support &amp; Development</title>
	<atom:link href="http://www.coupa.com/category/devblog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.coupa.com</link>
	<description></description>
	<lastBuildDate>Tue, 16 Mar 2010 22:54:17 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Staying Agile in Customer Support &amp; Development</title>
		<link>http://www.coupa.com/devblog/staying-agile-in-customer-support-development/</link>
		<comments>http://www.coupa.com/devblog/staying-agile-in-customer-support-development/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 16:17:24 +0000</pubDate>
		<dc:creator>Noah Eisner</dc:creator>
				<category><![CDATA[Developer Blog Post]]></category>

		<guid isPermaLink="false">http://www.coupa.com/?p=11492</guid>
		<description><![CDATA[2-plus years ago, when Coupa&#8217;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&#8217;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 [...]]]></description>
			<content:encoded><![CDATA[<p>2-plus years ago, when Coupa&#8217;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&#8217;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&#8217;s the support you get when paying 22% annually of your license fees.</p>
<p>At Coupa, we decided to go with a single system for help desk and issue/bug tracking.  Sometimes we don&#8217;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&#8217;t hate the system, but it wasn&#8217;t exactly user-friendly. Our development team detested it.  It slowed us down and that just couldn&#8217;t happen.  It was un-agile.</p>
<blockquote><p>Graham, our development manager said (I&#8217;ll paraphrase), &#8220;It just blows&#8230;.it&#8217;s fundamentally unintuitive.&#8221;</p></blockquote>
<p>So one of our guys scanned the world via Google one night and stumbled upon a tiny company from Copenhagen named <a href="http://www.zendesk.com" target="_blank">Zendesk</a>.  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:</p>
<blockquote><p>From Ravi&#8217;s email&#8230;&#8221;this product is almost as cool as our app &#8211; freakin amazing&#8221;</p></blockquote>
<p>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 <a href="http://blog.zendesk.com/blog/2008/06/zendesk-secures.html" target="_blank">seed investment press release</a>.   It was great to see another Rails company succeeding.</p>
<p>Fast-forward to the present and Zendesk has grown significantly.  Now, headquartered in San Francisco, they have other great customers like Twitter, MSNBC, Scrib&#8217;d, IDEO and plenty others.   Most importantly, their solution is part of the collaboration that makes <a href="http://www.coupa.com/customers" target="_blank">our customers</a> successful and our development team agile.  The team doesn&#8217;t waste time on perfect classification of a ticket.  Instead, we get to work on it.  Besides that, it&#8217;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&#8217;re done.</p>
<p>Without agile development, the cloud, Rails, and open source, I&#8217;m pretty sure that companies like Coupa and Zendesk wouldn&#8217;t have a fighting chance.  But with them, we&#8217;re changing the face of applications for businesses.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coupa.com/devblog/staying-agile-in-customer-support-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Don&#8217;t let IE7 ruin your weekend</title>
		<link>http://www.coupa.com/devblog/dont-let-ie7-ruin-your-weekend/</link>
		<comments>http://www.coupa.com/devblog/dont-let-ie7-ruin-your-weekend/#comments</comments>
		<pubDate>Tue, 02 Feb 2010 05:13:09 +0000</pubDate>
		<dc:creator>Graham</dc:creator>
				<category><![CDATA[Developer Blog Post]]></category>

		<guid isPermaLink="false">http://www.coupa.com/?p=11752</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>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 &#8211; surprise! Instead they are prompted with a &#8220;File Download &#8211; Security Warning&#8221; dialog box and some text about &#8220;Type: Unknown File Type&#8221;. 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.</p>
<p>My first thought had been &#8211; 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&#8217;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&#8217;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 &#8211; 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 &#8220;.html&#8221; in the URL and &#8211; viola &#8211; 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.</p>
<p>This time what made the difference was a casual suggestion from Ted to use <a href="http://www.debugbar.com/">DebugBar</a> to analyze the request/response Headers. I hadn&#8217;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 &#8211; 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 <a href="http://agilerails.wordpress.com/2009/05/27/ie-throws-file-download-security-warning-on-form-submit/">this post from Hemant Khemani&#8217;s blog</a> which clearly describes the exact problem we were having as well as the solution. Essentially IE was requesting a Content-Type of &#8220;images/jpeg&#8221;, which was not defined in our respond_to do block, so Rails chose our default format &#8211; which happens to be the first one you specify.</p>
<p>So the fix was simply to replace:</p>
<pre class="brush:rails">      respond_to do |format|
        format.js
        format.html {render :template =&gt; 'user/expense_only_home'}
      end</pre>
<p>With:</p>
<pre class="brush:rails">      respond_to do |format|
        format.html {render :template =&gt; 'user/expense_only_home'}
        format.js
      end</pre>
<p>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.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coupa.com/devblog/dont-let-ie7-ruin-your-weekend/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Abstraction Breakdown: Table Aliasing in ActiveRecord</title>
		<link>http://www.coupa.com/devblog/abstraction-breakdown-table-aliasing-in-activerecord/</link>
		<comments>http://www.coupa.com/devblog/abstraction-breakdown-table-aliasing-in-activerecord/#comments</comments>
		<pubDate>Fri, 29 Jan 2010 23:43:40 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[Developer Blog Post]]></category>

		<guid isPermaLink="false">http://www.coupa.com/?p=11722</guid>
		<description><![CDATA[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&#8217;t have too many [...]]]></description>
			<content:encoded><![CDATA[<p>A few things I love about Rails:</p>
<ul>
<li>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.)</li>
<li>The abstractions are powerful, but don&#8217;t have too many layers, so if  some behavior surprises you, it&#8217;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.</li>
<li>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.</li>
</ul>
<p>And an involved little problem where these break down:</p>
<p>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&#8217;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.</p>
<p>A simplified example declaration, which you might find in  WarehouseTypesController:</p>
<pre class="brush:ruby">data_table :warehouse_type,
 [:name,
 :description,
 :created_by,
 {:key =&gt; :created_at, :label =&gt; "Created Date"},
 {:key =&gt; :updated_by, :label =&gt; "Last Updated By" },
 {:key =&gt; :updated_at, :label =&gt; "Last Updated Date"}],
 :find_options =&gt; {:include =&gt; [:created_by, :updated_by]}</pre>
<p>Each table has a base model (in this case, WarehouseType), where each  row of the table represents one record in that model&#8217;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&#8217;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:</p>
<pre class="brush:ruby">class WarehouseType &lt; ActiveRecord::Base
 belongs_to :created_by, :class_name =&gt; 'User', :foreign_key =&gt; 'created_by'
 belongs_to :updated_by, :class_name =&gt; 'User', :foreign_key =&gt; 'updated_by'</pre>
<p>A call to render_warehouse_type_data_table will produce something  like this:</p>
<p style="text-align: center;"><img class="alignnone size-full wp-image-11726" title="Data Table Example" src="http://www.coupa.com/images/data_table_ex.png" alt="Data Table Example" width="702" height="243" /></p>
<p>And the advanced search pictured above leads pretty directly to the  following SQL:</p>
<pre class="brush:sql">SELECT * FROM `warehouse_types`
WHERE (warehouse_types.name LIKE 'storage%')
ORDER BY warehouse_types.name DESC
LIMIT 0, 20</pre>
<p>Straightforward, right? Well, it breaks down even in the above  example when you try to query on the &#8220;updated_by&#8221; 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&#8217;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&#8217;t need to know this as long as you&#8217;re using the nested hash  version of find(), but that doesn&#8217;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:</p>
<pre class="brush:ruby">WarehouseType.find(:all,:conditions =&gt; ['users.fullname LIKE ?','John%'],
                        :include =&gt; [:created_by,:updated_by]</pre>
<p>which would generate something like this:</p>
<pre class="brush:sql">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%')</pre>
<p>This won&#8217;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:</p>
<pre class="brush:ruby">WarehouseType.find(:all,:conditions =&gt; ['updated_bies_warehouse_types.fullname LIKE ?','John%'],
                        :include =&gt; [:created_by,:updated_by]</pre>
<p>The thing is, though, we don&#8217;t really know what alias to use. If  there&#8217;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&#8217;re included as a Hash rather than an  Array. (Like most folks with dependencies on external libraries, we  can&#8217;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:</p>
<pre class="brush:ruby"> {:key =&gt; :updated_by, :label =&gt; "Last Updated By",
  :sql_column =&gt; 'updated_bies_warehouse_types.fullname' },</pre>
<p>I really don&#8217;t like this solution, though- it&#8217;s brittle, and it  requires too much knowledge of both ActiveRecord&#8217;s implementation and  any additional factors that may modify the model&#8217;s scope. This is not  knowledge that something in the controller should have. It&#8217;s also tricky  to test in a low-level way.</p>
<p>Before getting onto the possible solutions, I should point out that  we&#8217;re certainly not the only ones to run into or describe this problem-  there&#8217;s more  succinct discussion in Rails tickets <a href="https://rails.lighthouseapp.com/projects/8994/tickets/2357-predictable-table-aliases-in-joins" target="_blank">2357</a> and <a href="https://rails.lighthouseapp.com/projects/8994/tickets/2087" target="_blank">2087</a>,  including a proposal for and code for  potential solution #1 below.</p>
<p>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:</p>
<ol>
<li>Introduce a way to query what the expected table alias for a  particular situation is going to be</li>
<li>Implement operations other than == in the nested-hash form of  find(), as in the relational algebra code that may be merged into AR eventually.</li>
<li>Replace alias references with locally-scoped tokens that represent  the joins/includes, and substitute in the correct aliases when merging  conditions.</li>
</ol>
<p>These are all quite intrusive, however; the way that ActiveRecord&#8217;s  merge_conditions calls work today, it doesn&#8217;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&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coupa.com/devblog/abstraction-breakdown-table-aliasing-in-activerecord/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Skeletons in /lib: Setting auto_increment in MySQL</title>
		<link>http://www.coupa.com/devblog/skeletons-in-lib-setting-auto_increment-in-mysql/</link>
		<comments>http://www.coupa.com/devblog/skeletons-in-lib-setting-auto_increment-in-mysql/#comments</comments>
		<pubDate>Thu, 28 Jan 2010 21:19:07 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[Developer Blog Post]]></category>

		<guid isPermaLink="false">http://www.coupa.com/?p=11629</guid>
		<description><![CDATA[Skeletons in /lib is explores the underbelly of Rails apps- the  aspects, hacks, and extensions that don&#8217;t fit elsewhere, confound new  developers, and make each beast unique.
There are a number of business requirements that almost (but don&#8217;t  quite) map to simple technical implementations. In those cases, we&#8217;re  often faced with a [...]]]></description>
			<content:encoded><![CDATA[<p><em>Skeletons in /lib is explores the underbelly of Rails apps- the  aspects, hacks, and extensions that don&#8217;t fit elsewhere, confound new  developers, and make each beast unique.</em></p>
<p>There are a number of business requirements that almost (but don&#8217;t  quite) map to simple technical implementations. In those cases, we&#8217;re  often faced with a choice- do we write a separate, more complicated  implementation, or do we force a fit with a hack?</p>
<p>Here&#8217;s a straightforward one: purchase orders (POs) are a key  business document in our system, and when companies issue them they tend  to refer to them by number. (&#8221;Hey, Apple, when are you planning to ship  PO #453?&#8221;) As such, they&#8217;re a minor but real aspect of the face that  our customers present to their suppliers, and some of them want more  control over the range. Basically, they&#8217;d much rather start issuing POs  at e.g. 10,001 than at 1, much like many folks don&#8217;t want their  checkbooks to start at 1. Technically, though, the PO number is about  what you&#8217;d expect it to be in a Rails app- the order_headers.id column.  (I&#8217;m leaving out revisions here- that&#8217;s a topic for another time.)</p>
<p>So, how do we give users control over where the sequence starts?  Simple, although it&#8217;s one of the very few places where we&#8217;re not  database agnostic:</p>
<pre class="brush:ruby"># Include on ActiveRecord models to get additional MySQL-specific
# functionality.
module CoupaLib
 module MySQLHelpers
 def self.included(base)
 base.extend ClassMethods
 end

 module ClassMethods
 # Returns a string containing the auto increment value (the next insert
 # id) on this table.
 def db_auto_increment
   result = ActiveRecord::Base.connection.execute &lt;&lt;-SQL
   SHOW TABLE STATUS WHERE name = '#{self.table_name}';
   SQL
   result.fetch_hash['Auto_increment'].to_i
 end

 # Sets the auto increment value to the argument.  NOTE: it is the
 # responsibility of the user to make sure the value isn't already in
 # use (will potentially cause duplicate id errors)
 def db_auto_increment=(num)
   ActiveRecord::Base.connection.execute &lt;&lt;-SQL
   ALTER TABLE #{self.table_name}  AUTO_INCREMENT = #{num};
   SQL
   end
 end # Class Methods
 end  # MySQLHelpers
end  # CoupaLib</pre>
<p>Now we can inquire what the next ID will be, and set it, in a few  interesting places within our app:</p>
<ul>
<li>During customers&#8217; initial self-guided setup, before any documents  are created.</li>
<li>During automated transactional &#8220;cleanup&#8221; when moving a test  configuration into a production environment. Existing test documents are  destroyed, and the sequence is reset to the initial custom value.</li>
<li>During merging of potentially-updated Coupa seed data and customer  data that needs to be preserved. (That&#8217;s another interesting  code-meets-real-world problem to look at later.)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.coupa.com/devblog/skeletons-in-lib-setting-auto_increment-in-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>User Experience and Development &#8211; let&#8217;s make a sandwich.</title>
		<link>http://www.coupa.com/devblog/ux-development-sandwich/</link>
		<comments>http://www.coupa.com/devblog/ux-development-sandwich/#comments</comments>
		<pubDate>Tue, 26 Jan 2010 00:55:42 +0000</pubDate>
		<dc:creator>Casey</dc:creator>
				<category><![CDATA[Developer Blog Post]]></category>
		<category><![CDATA[User Experience]]></category>

		<guid isPermaLink="false">http://www.coupa.com/?p=11560</guid>
		<description><![CDATA[
When speaking of the disciplines of design and development, people tend to see them as different entities. As oil and water, rather than peanut butter and jelly. But we have some things in common &#8211; we both need to create new things, we both need a ton of creativity to get our work done, we [...]]]></description>
			<content:encoded><![CDATA[<p>
When speaking of the disciplines of design and development, people tend to see them as different entities. As oil and water, rather than peanut butter and jelly. But we have some things in common &#8211; we both need to create new things, we both need a ton of creativity to get our work done, we both love finding awesome new solutions, we both love making gorgeous deliverables. We&#8217;re closer than you&#8217;d think. We could stand to learn a few things from each other.
</p>
<p>
<strong>Designers: become more technical.</strong><br />
Learn why things are or are not feasible before the developer even gets involved. Jump in and code some front-end interactions yourself &#8211; move the cycle along quicker and get your intentions across easier. Jump into some of the newer (awesomer) frameworks that make it easier to do the front end side of things. My favorites, jQuery and Ruby on Rails, can really make you feel like you actually know what you&#8217;re doing (and sometimes makes others think you know what you&#8217;re doing too). Once you see some of the amazing things that jQuery can do &#8211; it will open your eyes to tons of new interaction possibilities. And, in particular, jQuery will make you change the way you look at javascript (and use it).
</p>
<p>
<strong>Developers: learn UX methods.</strong><br />
Really, designers don&#8217;t bite, and we have some pretty awesome ways of figuring out problems. If you&#8217;ve run into an issue, sketch it out. What are the different possibilities? Visualize the solutions by sketching them out and find which one makes sense. Use that napkin that came with your take out or the whiteboard across the room. Don&#8217;t be afraid to come up with something stupid&#8230; it&#8217;s just as easy to toss it out if it doesn&#8217;t work &#8211; the only person that will know is the janitor, and she&#8217;s cool anyways. Remember Ockham&#8217;s Razor &#8211; all things being equal, the simplest solution is most likely the best solution. Less is more &#8211; just because you can do that extra feature, doesn&#8217;t mean its going to make the product more usable (most times its actually the opposite). Know that the best User Experience is one that the user doesn&#8217;t notice. If the user doesn&#8217;t even have to give the UI a second though, we&#8217;ve all done our jobs.
</p>
<p>
The better we understand each other, the quicker and more cohesively we can all work together to make a kickass product.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coupa.com/devblog/ux-development-sandwich/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
