Your browser is no longer supported! Please upgrade your web browser now.

Behind the New Invoices Overview

Ever wonder about the inner-workings that make Harvest tick? There are a lot of continual updates to our codebase that our users never see. In the lull before our next update to Invoices Overview, we thought it a good chance to give you a sneak peek into the behind-the-scenes work our developers do to make Harvest run better and faster. In this post, our developer Pez gives you a first-hand glimpse into his work to improve the code for Invoices Overview.

Here at Harvest, we like to ship new features, but also take great care to continually improve existing functionality.

We launched Harvest back in 2005, before Rails had hit version 1.0 and Ruby was still at version 1.8. That means we have our fair share of legacy code. Recently some of the legacy code had started to become a blocker to our aim of continual improvement.

When we decided to update the Invoices Overview screen, we had a choice: develop new functionality on top of the existing code, potentially making the problem worse, or rewrite our code.

We decided to take a step back and re-architect this section of Harvest. This meant we could bring the area up to modern standards with feature parity, and then start implementing a few new features to make the section even more useful.

What’s the Problem with Old Code?

Why is legacy code such a bad thing? If it isn’t broken don’t fix it, right?

While it’s true that features written in legacy code will work, legacy code can eventually become a barrier for improving existing functionality for a few reasons:

  • It often has less test coverage, meaning it’s easy to break things without realizing.
  • It’s usually harder to understand, having drifted from the originally engineered design.
  • The additional complexity makes it harder to debug and more complex to add functionality to.

But why does “legacy code” exist in the first place? 

Harvest is an evolving app, whether we’re adding new features, tweaking old ones, or just improving the design. As we evolve, we depart from the original design. Assumptions previously made become invalidated, bugs may be introduced, and the foundations that were originally put in place often end up being used for something other than their original intent. This compounds the complexity and introduces potential problems. Like the one we ran into when we decided to improve our Invoices Overview.

As Lehman wrote in The Law of Increasing Complexity (and referenced by Lehman’s Laws):

As an evolving program is continually changed, its complexity, reflecting deteriorating structure, increases unless work is done to maintain or reduce it. (Meir Manny Lehman, 1980)

Tackling Invoices Overview

When we started working on Invoices Overview, we knew two things: as the gateway into the Invoices section, the Overview had great potential to offer easy access to your invoice data, and that legacy code made it difficult to improve many of the things we wanted to. It was time to do some rewriting.

A Single Page Web App

In a traditional web app, and in some sections of Harvest, the server renders the page and sends the HTML to the user’s browser. It’s then the browser’s job to display the page to you, the user. Javascript is often used to improve the user interface of an application, for example adding an error message if you don’t fill in the necessary items on a form or to provide animation. But any time the information on the page needs to be changed, a full page reload is required, rendering the next page on the server and completely replacing the previous page.

Recently, however, it’s more common for Javascript to be used to make an asynchronous request to update a small part of the page, although changing all information on a page still requires a full page reload.

While web apps enhanced by JS perform most of the rendering on the server side, returning the HTML to the browser, single page web apps do most of their rendering on the client side, in the browser. After the page loads for the first time, any updates in the page are made using Javascript, triggering a re-render on the client side without any additional server requests.

The new Invoices Overview is effectively a stand-alone single page web app within the traditional web app of Harvest—it’s doing most of its rendering in the browser. When we went about removing the legacy code, we decided to rewrite this section using the Backbone.js framework. This framework, per its documentation, gives structure to web applications providing abstractions for models, collections, and views, all of which provide event handling.

We chose Backbone.js for two key reasons. Firstly, it provides a formal structure for building a single page application, and helps prevent the application from turning into an unorganised pile of callbacks tied to concrete DOM elements.

Secondly, it natively supports communication with a RESTful API. This kind of communication helps us separate our new code from the rest of Harvest, and makes server-side changes through a single API endpoint. This additional and almost compulsory structure, with the additional separation of the user interface and database access, will help limit the the speed at which our new code becomes legacy code.

You can find much more about Backbone.js as well comparisons between it and common alternatives online.

So How Does It Work?

When you first load Invoices Overview, the invoices matching your current filters are saved into the page itself in what we call a data-island. This data-island is essentially a block of JSON-encoded data. This is the only part of the UI (apart from the header and footer) that is rendered server side.

As soon as the page has loaded we create a Backbone Collection for all of the invoices held. You can think of this collection as a list of invoices that you can add to or remove from. Then in your browser, we render a row for each invoice in the collection, and configure it to re-render the table whenever the collection’s contents are changed.

Making use of the (as yet unreleased) second version of Harvest’s API, when the tabs or filters are changed, we send a RESTful request to the API to get all invoices that match the current filters. The API searches the database, returning any matching invoices as a JSON encoded array, which replaces the contents of the current Invoices Collection.

Bringing It All Together

Legacy code is just part of the natural evolution of an application. As Harvest has grown, so has our codebase and its complications. With careful design, and by making use of well understood scaffolding such as Backbone.js (MVC) and RESTful API’s, we can ensure that Harvest works its best even in the places that a user might never see. And for you, these under-the-hood improvements set the foundation so we can spend more time building the features we know you’ll love, rather than maintaining our existing code.

Thoughts or questions about this post? Need some help?
Get in touch →

This was posted in Behind-the-Scenes, Online Invoicing, Product News.
  • I’m a 5+ year Harvest user and SMB owner. Also an architect (as in buildings). You said, “We decided to take a step back and re-architect this section of Harvest.” Oh my – the verb use of the word “architect” just makes me crazy. Why not say recode, rework or rewrite?

    Thanks for keeping things current. Love the product.

  • As a designer, I feel you! Everything is designed nowadays

  • Fantastic overview. I also am (was) a developer and now run a small consulting company (about 20 .NET contractors and employees). I love the changes and we use this app heavily. We have 100’s of invoices to manage so the improvements are welcomed. I’m also impressed how well Harvest integrate with QuickBooks. Keep up the great work!

  • Re. David comment.
    It is really interesting comment done from the point of view of the profesional in the industry that originally created a given term.

    This is what happens with language – terms change their use to provide an explanation to new concepts using something familiar.

    An example – one doesn’t think twice what ‘Broadcast’ means today – most of the people will interpret it as mass distribution of some signal (Radio/TV etc.) instead of the original meaning of planting seeds by throwing them from a basket around the person holding it – hence the Broad and Cast method.

    The Software industry differs from the Building industry mainly by the number of possible options available to ‘build’ something.
    A requirement for a bridge are simple: Connect point A to point B over this obstacle. The connection should not fall under earthquake or due to resonance due to wind.
    It should have 2 opposite lines for traffic, human side walk 2m wide and withstand 100 tons of load.
    With that a bridge can more or less be designed and build using existing methods.

    The requirements for Similar in size, software project (in terms of effort ) will be hundreds if not thousands of pages long. That is where the main difference is.

    The complexity of a modern system (especially with Browser interface) is of order of magnitude bigger than a building project with few possible options which implementation is tried and documented in detail. The complexity is tackled by abstraction layer(s) that brings the construction complexity to manageable level.

    We don’t code in assembler GUIs this days (like in Apple ][ time) because the problem has been solved already and can be handled by the OS, Drivers and Hardware that didn’t exist once upon the time. Like moving from classic sun dried bricks to baked bricks to aerated concrete blocks etc.

    In Software there is a substantial try/test/chose effort until the initial architecture is chosen.
    However – deadlines and other constraints sometimes do force the Software dev. team to use what they have in place to achieve the task at hand quickly- not always in the best from architecture point of view way.

    The business requirements for a Bridge do not change so fast in time. There will be years of use before a bridge designed for horse carts is required to handle something else (cars, trains, etc.)

    It may be possible to modify an old bridge to handle one car at a time – but as soon as the fundamental method in which the bridge foundations have been build when only horse cars had to use it becomes inadequate to handle the new required load – it has to be changed.
    This is what triggers the re-architecting in software. For small not changing the fundamental approach changes – re factoring can be used as a term but if what you have at hand to work with is not suitable any longer – re architecturing the underlying layers of abstraction is the only choice.

  • @TS This is a forum a SAAS product for people who invoice for their time.

    What are you doing here?

  • @MA I think you’ll find **this is a blog *by* the people** who build the SAAS for people who invoice for their time. What indeed.

  • @Will Oller @TS Anyone who can afford the time to wax so poetic, so tangentially to the topic, doesn’t strike me as an owner, more an employee. If TS is employed by Harvest, let’s chat about my fees.

  • Thank you for the clarification Will, really spot on.
    I’m NOT a Harvest employee, just a Harvest user who is running a software business.

    That, I hope, will clarify the level of appreciation for the really excellent Blog post from a Harvest user and software builder point of view.

    Someone who’s work I use and pay for- wrote a wonderful blog and gave us a really appreciated glimpse on the software crafting process that brings us the Harvest we use and love.

    And after all this… the comment he got was re. the use of a term that have dual meaning depending on the domain in which they are used..
    Given the situation I was in – I just felt the author deserves my support in front of the construction and design industry. :)

    No poetic agenda or any deep thoughts, etc. I was just in the office around 6am in the morning and the network authentication had some problem – keeping me off the keyboard for about 20 minutes.

    I’m sorry for the false expectations created, but it seems no rates will change because of me. :)

Comments have been closed for this post.
Still have questions? Contact our support team →