• Exceptions are Part of Your API

    The adapter pattern is great for wrapping gems that communicate with an external service. For instance, we use the databasedotcom gem to integrate with Salesforce. It's always a good idea to wrap something like this in our own interface. That way, if the gem changes - or we want to change gems - we can make our changes in one place, instead of all over our code.

    When we do this properly, our app shouldn't know anything about how the gem works, and just use the interface. It's easy to forget about exceptions, though, and end up with your app having to rescue Databasedotcom::SalesForceError all over the place.

    This is a leaky abstraction, because details of the implementation 'leak' through to our app. In this case, the leak is the exception.

    Preventing this is simple. First, let's implement a custom error that the rest of our app will be aware of.

    class OurOwnCustomError < StandardError

    Next, catch the gem's errors in our interface, and either handle them or throw our custom error.

    def authenticate_salesforce(options)
      rescue Databasedotcom::SalesForceError => e
        raise OurOwnCustomError, "Could not authenticate with salesforce"

    Now, our app only has to know about the error we implement. Should the gem change, or we change to a different one, we don't have to change code all over our app.

  • Slow is Smooth

    Everybody knows that you have to work fast as a company to survive. Time is not infinite. Money is not infinite. And your customers will go somewhere else to get what they need. It's critical, though, to consider your long term speed, or ability to produce results quickly.

    An old Army saying sums this up: "Slow is smooth and smooth is fast." Activities must be performed with the proper amount of both urgency and diligence. Going too fast and making mistakes really slows you down over the long run. Rush while putting on your gas-mask, and it'll take you twice as long to finish. Trust me, you really don't want that.

    When working on a new feature, you absolutely have to ship it, even if it isn't perfect. However, taking an extra week to make sure that your code is clean and easily understood, that your tests are well-written, and that you're using good practices will save you time later. Write good code - not perfect code - the first time, and you'll deliver much faster overall.

    Finding that balance is hard. Really hard. While I'm way too averse to putting out code that's not perfect, it's also way too easy to justify putting out sub-par work in the name of "moving fast." We have to consider the future time-cost of "breaking things."

  • Searching for Love... on the Client and Server

    In a web app, searching through a list of things is very common. Backbone makes it really easy to filter a collection on the client, or fetch data from the server. When letting a user search through their list of widgets, which do you use?

    Searching on the client is fast and snappy, when there aren't many records. Get a couple thousand widgets, though, and the client side rendering will slow way down. This forces you to search on the server.

    You could always search on the server to avoid this, but why make the server round-trip when there aren't many records?

    Conditionally do both. If there are less than some arbitrary number of records, such as 200, load everything onto the client and do your searching / filtering there. Otherwise, make requests to the server.

    We've implemented this by creating separate client-side and server-side search objects which implement the same api and return a promise. That way, whatever view needs to filter your collection doesn't need to know nor care where the data is coming from.

    Rig up a factory function to return the correct searcher, and you're good to go:

    searcherFactory = (collection) ->
      if collection.state.totalRecords > 200
        new ServerSearcher(collection)
        new ClientSearcher(collection)

    Then, just use it:

    @searcher = searcherFactory(@saxophonists)
    @searcher.search("George Michael").then (filteredSaxophonists) ->
      # Do whatever with your filtered list.
  • Waiting to Capybara

    We recently upgraded to Capybara 2 for our integration tests (I know, I know, only two years late). Doing this upgrade forced us to refactor a lot of our tests so we wouldn’t have to rely on timing as much. However, one issue gave us a lot of problems, but was thankfully pretty simple to take care of once we figured it out.

    The issue we encountered was around testing to see if certain elements get removed from the page in response to an ajax action. Capybara will automatically wait for elements to appear, but it's not as obvious how to wait for something to disappear.

    The code was something like this:

    page.find(".list").should_not have_content(@title)

    As you can see, we're performing a search which fetches its results from the server, and then asserting that a particular title is not on the page. The @title starts off on the page, but is removed after the search.

    Turns out, when Capybara got to that assertion, the ajax request had not yet returned. The title was still on the page, even though everything was working. Seems that should_not have_content was not waiting for the title to be removed.

    So how do we do that?

    Well, it's really easy, although it took me an embarrassingly long time to figure out.

    Change the assertion to:

    page.find(".list").should have_no_content(@second_title)

    The have_no_content matcher will wait for the content to be removed from the page, which is exactly what we want. Taking this a step further, if we need Capybara to wait for something to happen, we should always use positive assertions - should have_no_whatever instead of should_not have_whatever.

  • Making the Move to Inline SVG

    I recently wrote and article for css-tricks.com titled 5 Gotchas You're Gonna Face Getting Inline SVG Into Production that's very much inspired by learnings here at Mavenlink porting our icons to inline SVG (an ongoing effort that will continue through 2015 until we can prune every last sprite from our system).


    The tl;dr of the article can be summarized as follows:

    • Hitting the target in IE when using the svg4everybody library requires that you absolutely never style CSS to the use element
    • Same rule above applies for JavaScript manipulations
    • Designer should almost always export the artworks as black fills with transparent strokes or paths only
    • Post-processing library grunt-svgstore now offers some options to achieve more flexible color manipulations such as preserve-- attributes and fill="currentColor" never getting removed
    • jQuery Throws errors if you click directly on the svg use element unless you define svg { pointer-events: none;}
    • GitHub now provides a svg viewing feature which allows you to toggle a view of the blob

    There's also a full blown example provided on the css-tricks article so do be sure to check that out if you're thinking of implementing inline SVG icons for your application.

    One other thing which wasn't mentioned on that article, but Mavenlink has found quite helpful in making this move, is to define an svg_icon helper. We did so for both our .jst.eco templates, as well as our .erb files. Here's a slightly simplified version of the coffeescript helper we use for our .jst.eco templates when placing an SVG clone instance in one of our templates:

      svg_icon: (iconName, iconClasses) ->
        iconClasses = if iconClasses then ' ' + iconClasses else ''
        @safe("<svg class='icon-new #{iconClasses}'><use xlink:href='#{Mavenlink.svgImagePath}##{iconName}'></use></svg>")


    So far, I'm quite happy with our decision to implement inline SVG for our icons. It hasn't necessarily been “easy peasy”, but the gains are worth the effort. With more and more demands from the various form-factors in the wild, it's great that browsers have finally got to the point where we have some decent support for SVG. It will definitely make designer's and developer's lives much easier!

subscribe via RSS