Saturday, February 18, 2012

A Yelp for Developer Platforms?

For my talk on mobile app strategies, I really wanted to give the audience an idea of the options out there, and help them understand the pros and cons of each option. I'd only used a handful of mobile app platforms myself, so for most of them, I had to pore through the website checking out what they offered and where they differed from other platforms. But there's something very important that a website can't tell me: what is it like to actually *be* a developer trying to use that platform? As usual, I took to Twitter to see if anyone among my followers had tried the platforms themselves and whether they liked them. I got a handful of replies — I think most developers are either doing native mobile app development or no mobile app development at all — and it wasn't enough of a sample size for me to reach conclusions about the developer experience for any of the platforms.

That's when I realized we need something like Yelp, but for developer platforms. When you're thinking of going to a restaurant, you can determine that they're close to you and that they offer meals that sound good to you, but you can't be sure you'll actually enjoy the experience of going to the restaurant. What if the service sucks? What if the meals take too long to come out? What if they just don't taste good? It's the same thing with a developer tool. You can check out the documentation and see that their feature list jives with what you need, but you can't know that you'll enjoy working with it. What if the support sucks? What if it takes too long for new versions to come out? What if the API is buggy? When you have a bad restaurant experience, you've only lost a few hours of your life, but when you have a bad developer experience, you've potentially lost a few months. Many platforms seem fine and dandy when you're doing the hello world, but then the problems come out later, after you've committed to the platform and dove deep into it.

Sure, we can continue what we do now — asking our colleagues or Twitter or HackerNews everytime we're thinking of trying a new platform — but it's a shitty state of affairs for every developer to have to do that every time. I propose instead that we have a review site, where we can aggregate opinions on developer platforms in one place. And I don't just want a 1-star to 5-star rating — we've all seen how that turns into 3.5 stars for everything. I'd want actually useful ratings and reviews that would hone in on the stuff that matters in a developer experience. For example, here are some of the questions you'd be asked when reviewing:

  • How many hours have you spent using the platform?
  • How comprehensive is the documentation?
  • How up-to-date is the documentation?
  • How buggy is it?
  • When something goes wrong, how easily/quickly do you get an answer from the support team?
  • How active is the community around it?
  • Would you recommend it to others?
  • What other similar platforms have you used? (And which do you prefer?)

We basically want to figure out how experienced a reviewer is (the Hello World experience is very different from the 6 months experience) and then see what their experience was like. We could weight the ratings based on experience for an aggregate score, or simply present all the reviews and let people figure out whose reviews matter to them. That could all be tweaked, the important thing is to gather the opinions, and for those opinions to be more fine-grained than simply "yay" or "nay."

A developer-focused review site could also include a few more features, like:

  • categories and/or curated lists, so you could see all the mobile app SDK reviews at a glance.
  • automatic mining of the social web with sentiment analysis for gathering tweeted opinions and blog posts
  • automatic analysis of forums and code repositories to show level of activity for a platform

Like any reviews site, there's a big problem: how do you get enough people to use it so that there's sufficient data to see a general trend, and not just a few anecdotes here and there? Well, one way is to do what Yelp does: they get restaurants themselves to display Yelp badges, usually the better restaurants. The better platforms (or the platforms that think they are better) could link to their reviews, and developers will learn that a review site is out there. Most people on the web are content consumers and not content creators, but I unscientifically think that developers tend to be creators more than consumers, and that would help in populating the reviews.

So, there you have it: my wish for a developer-focused review site. I know it's not simple — it would require a lot more than my airplane musing time to really figure out how to make it work well — but I also think it would be a good thing for the developer world at large and I hope someone takes it on someday. What do you think? What do you think?

Friday, February 17, 2012

Picking a Mobile App Strategy (Slides & Links)

One of the main new technologies that I've been working with this year is PhoneGap, as that's the tool I picked to help me make the mobile apps for EatDifferent. So, this week, in addition to the Client-side Storage talk, I also gave two talks about why I chose PhoneGap and how I'm using it — a short version at Jfokus and a longer version at Valtech.

For both the talks, I wanted to make sure the audience had a clear idea of all the options that are out there for making a mobile app, like using a cross-compiler (ParticleCode) or a bundled runtime engine (Titanium). I spent a day researching those options to make sure that I myself had a good idea of them, and I found that there were many more than I knew about. At a certain point, I just had to stop looking because I feared that the results would be never-ending.

It's a good thing that there are so many tools out there for developers to use, but it also means it's that much harder to know which tool is the right one for you. Plus, these tools are all so new, so there are few developers that have attempted to make real, full-functioning apps using multiple tools, which makes it hard to compare the developer experience across them. It makes me wish we had a Yelp for developer tools — but even still, what works for one developer/app may not work for another.

For the longer talk, I went into some detail on my architecture, workflow, and testing process for my PhoneGap apps (much of what I've detailed in blog posts here), and I did a small demo in front of the audience. I didn't have time to talk about debugging, logging, and offline use this time, but hopefully I can expand that part in my talk at Mix-IT in April.

You can view the slides online for the short talk and the long talk. If you want to research more, you can also browse the links to comparisons and reviews below. Keep in mind that these tools are evolving all the time, so you should always check the current documentation before trusting a reviewer (hoping, of course, that one can actually trust the documentation). If you are embarking down the path of making a mobile app, let us know what you pick and how it works out for you. And good luck!


Links:

Wednesday, February 15, 2012

Interactive HTML5 Slides with FathomJS & CodeMirror

When I'm giving a talk about a client-side topic (JavaScript/HTML/CSS), I really like being able to show what I mean via actual live code. But I don't like having to switch back and forth between a slide deck and a browser and a text editor. So, lately, I've been using a combination of FathomJS, a jQuery slides library, and CodeMirror, an in-browser code editing library, so that I can have slides with actual runnable code in them. Sometimes I want to run some JavaScript, sometimes I want to append a SCRIPT tag to the page, sometimes I want to render some HTML — and I can do all of that with CodeMirror.

To see it in action, you can check out this example deck. It also shows a few other "features": showing speaker notes (something I do while both practicing and sharing), rendering JS results in an overlay (easier than making room on the slide), and using fun CSS3 backgrounds. That deck is deployed to Google App Engine, as I find they're the easiest host for static content. You can also check out other actual decks that I've done with this combo: Client-side APIs, Client-side Storage, and Mobile app options.

Want to use it for your next presentation? Fork it on Github, baby!

Client-side Storage (Slides & Links)

I was invited to give a talk at Jfokus, a Swedish conference for Java developers. Since I don't do any Java development myself (I burned out on it as a kid :), I proposed a topic that I thought would be relevant to Java developers doing frontend web development: client-side storage using HTML5 technologies. I find myself using client-side storage (localStorage, specifically) in almost everything I do — browser extensions, mashups, mobile apps, websites — and I realized that it's not something that a lot of people talk about.

So I prepared a talk about the different HTML5 technologies for client-side storage (localStorage, IndexedDB, and the File API), and then focused in on localStorage with real-world examples of ways to use it to improve your apps. I'd never actually used IndexedDB or the File API before this talk, so I spent a day researching those and had fun learning the APIs. Once they're more ready for primetime, I will probably migrate some of my usage over to them. I also took the opportunity to research the performance concerns around localStorage, and now feel better prepared to measure the performance of my lscache library. It was a fun talk to prepare and give, and hopefully it gets a few developers experimenting more with client-side storage and spreading the good word.

You can go through the slides online — they're interactive, so you can actually tinker with the code and run it inside the slides. You can also click "show notes" to see roughly what I said. There was also a video recorded, and I'll link to that once it's available. If you want to learn even more, you can look through the links that I collected while putting together the slides.

cookies:




IndexedDB:


Specs & support:

Articles/examples:

Libraries:



File API:


Specs & Support:

Articles/examples:

Libraries:



localStorage:


Specs & Support:

Articles/examples:

Performance Tests:

Tools:

Libraries:

And finally, a link on all of them:

Client-side Storage Comparison

Tuesday, February 7, 2012

Datepicker for Twitter Bootstrap & Zepto/jQuery

When I ported my Phonegap code over to using Zepto instead of jQuery, I struggled the most with porting the jQuery tools DateInput plugin. I eventually got it to work, but man, it required a lot of little hacks and just didn't feel so great. I ran with it for a little bit, but then decided to see if there was a better alternative standalone datepicker, and I found this pull request to Twitter Bootstrap for a input-triggered datepicker. I took the code attached to the request and modified it to be standalone, and it totally worked, both with Zepto and jQuery. Here's what it looks like:

You can play with a demo of my modified standalone version here. That demo also shows using the date.format.js library for pretty printing the date. If you want to use it yourself, you can grab the JS/CSS from that demo, or the JS from this gist and the SaSS from this gist. Or if you don't need a standalone version, check out the demo for the original datepicker and grab his code from the pull request.

Hopefully the datepicker gets pulled into Bootstrap and becomes an official plugin. It's something that many websites need and the HTML5 input type="data" isn't giving us what we need yet, not even in the modern browsers.

Theming Twitter Bootstrap (Without Less)

As I've mentioned in previous posts, I use Twitter Bootstrap as the base for my CSS for EatDifferent, both on the web and mobile versions. I like it because it's pretty by default — the way I think the web should be, but it started off ugly and must stay backwards compatible, so it's too late for that dream

I also like it because it's easy to customize. The Bootstrap CSS is built using LESS, a stylesheet language like SASS, so if you use LESS, then you can actually modify the variables in the LESS files themselves and re-compile the CSS. However, I use SaSS instead of LESS, so for me to customize the Bootstrap CSS, I have to override their generated rules. I started off by tinkering with the CSS in Chrome DOM inspector, but then I realized I could more effectively and cleanly override their rules by basing my overrides on the rules in the original LESS files. I thought I'd share my overrides here in case others are going down the same path as me.

Colors

Much of my Bootstrap customization is just changing the colors and background colors of elements. Bootstrap defaults to a fairly generic color scheme- white background, dark grey text, blue links, and a black nav bar. For EatDifferent, I wanted a slightly brighter color scheme and a different blue. Since I use SASS, I set up various color variables at the top of my .scss file that I use throughout my CSS overrides.

$white: #ffffff;
$black: #000000;
$darkblue: #339bb9;
$darkestblue: #257085;
$blue-gradient: linear-gradient(top, $lightblue, $darkblue);

Links & Buttons (buttons.less)

The standard .btn button fits fine into my scheme — it's just a muted grey — but the btn-primary class is a blue, so I needed to change that over to my scheme's blue.

The SASS:

a {
  color: $darkblue;
  &:active, &:link, &:hover {
    color: $darkestblue;
  }
  &.btn-primary {
    color: $white;
  }
}

.btn-primary {
  @include background-image($blue-gradient);
  background-color: $darkblue;
  border-color: $darkblue;
  &:hover, &:active, &.active, &.disabled, &[disabled] {
    background-color: $darkblue;
  }
}

The result:


Navigation (navs.less)

Bootstrap offers two navigation elements, pills and tabs. The tabs don't require customization as they're just links with a grey border, but the pills have a hover and active background that must be overridden.

The SASS:

 
.nav-pills {
  .active > a, .active > a:hover {
    background: $darkblue;
  }
}

The result:


Tooltips (tooltip.less)

Bootstrap tooltips default to black background and white text, so I changed the background to my blue instead.

The SASS:

.tooltip {
  font-size: 13px;
  @include opacity(1.0);

  .tooltip-inner {
    background: $darkblue;
    color: white;
  }
}

The result:


Header (navbar.less)

Bootstrap provides a navigation bar (.navbar) for creating the typical top bar with site navigation, and this navigation bar is probably what most people recognize when they see a Bootstrap site. It defaults to a black background with white/grey links, and that was just way too morbid for my tastes. I changed the backgrounds to my blue, and added a logo and login area. There's a bit more to override here because the navbar also includes a dropdown menu.

The HTML:

  <div id="header" class="navbar navbar-fixed-top">
    <div class="navbar-inner">
      <div class="container">
        <a id="header-logo" class="pull-left">      
        </a>
          <ul id="header-login" class="nav pull-right">
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                <img src="pamelafox.png">
                <span id="header-login-name">Pamela Fox</span>
                <b class="caret"></b>
              </a>

              <ul class="dropdown-menu">
                <li><a href="{{ url_for('settings') }}">Settings</a></li>
                <li><a href="#" id="logout-button">Logout</a></li>
              </ul>
            </li>
          </ul>
      </div>
    </div>
  </div>

The SASS:

.navbar {
  .navbar-inner {
    background-color: $darkblue;
    @include background-image($blue-gradient);
  }
  div > ul a, .nav a {
   font-size: 14px;
   font-weight: bold;
   color: $white;
   &:hover {
      background-color: $darkblue;
   }
  }
  div > ul .active a, .nav .active a {
    background-color: white;
    color: $darkblue;
  }
  .dropdown-menu {
    &:before {
      border-bottom-color: #ccc;
    }
    &:after {
      border-bottom-color: $darkblue;
    }
  }
  div > ul .menu-dropdown, .nav .menu-dropdown, .topbar div > ul .dropdown-menu, .nav .dropdown-menu {
    background-color: $darkblue;
    li {
      a {
        background: $darkblue !important;
        color: white;
        &:hover {
          background-color: $darkblue;
        }
      }
    }
  }
}

#header {
  #header-logo {
    background-image: url('images/logo_long_trans.png');
    width: 138px;
    height: 40px;
  }
  #header-login {
    img {
      width: 16px;
      height: 16px;
      vertical-align: text-bottom;
      border: 1px solid #CCCCCC;
    }
  }
}

The result:

Wednesday, February 1, 2012

Delayed Image Loading on Long Pages

On EatDifferent, we let users track their meals with both text and pictures, and now that we have iPhone and Android apps, more and more users are using the mobile apps to quickly snap photos of their meals during the day. That means that the stream of updates on the home page includes a lot of images — a thumbnail for each user updating or commenting, and a thumbnail of each meal (and one user even takes photos of their blood glucose meter before each meal!). Here's a highly scientific diagram of that:

I realized that users were downloading all of these images for the stream, even though many of them were offscreen, and well, I felt bad for unnecessarily using their bandwidth and for making their experience slower. So, I decided to only load the images that were visible (or, nearly visible). Here's how I did it:

The HTML

Normally, when you write an IMG into your HTML, you assign the URL to the src attribute. I didn't want to do that anymore, though, since the browser would then load that URL, so I instead assign the URL to a data-src attribute — something the browser will ignore, but that my JavaScript can pick up. Here's what my template looks like:

<div class="stream-user-img">
 <a href="{{=log.user.profileUrl}}"><img data-src="{{=log.user.photoUrl}}"></a>
</div>

The JavaScript

Now that I have src-less IMG tags, I need a function to decide which IMGs to src-ify. Here's what the function needs to verify:

  • The IMG must have an empty src and a non-empty data-src attribute.
  • The IMG must be visible per CSS - not visibility:hidden or display:none.
  • The IMG must be user-visible, within the viewport. Or, as I realized, it should be nearly visible, so that users don't see the IMGs loading most of the time. I decided on 500 pixels as a reasonable near threshold, just by playing and seeing what felt right.

Here's what that looks like using jQuery:

function loadVisibleImages() {
  $('img').each(function() {
    if (this.src === '' && $(this).attr('data-src') && $(this).is(':visible'))) {
      if (inView($(this), 300)) {
        this.src = $(this).attr('data-src');
      }
    }
   });
}

function inView(elem, nearThreshold) {
  var viewportHeight = getViewportHeight();
  var scrollTop = (document.documentElement.scrollTop ?
          document.documentElement.scrollTop :
          document.body.scrollTop);
  var elemTop    = elem.offset().top;
  var elemHeight = elem.height();
  nearThreshold = nearThreshold || 0;
  if ((scrollTop + viewportHeight + nearThreshold) > (elemTop + elemHeight)) {
    return true;
  }
  return false;
}

Okay, now that we have that function, we need to call it at the appropriate times. It should be called when:

  • New IMG tags are written into the page (after data loading and template rendering, for example).
  • IMG tags go from hidden to visible.
  • IMG tags come into view after a user scroll.

I didn't find an easy way of programmatically detecting those first two cases, so I simply call loadVisibleImages() after my template rendering and visibility toggling (which only happens in two places), and then for the last case, I need to listen to the window scroll event. Unfortunately, I can't simply attach my callback to that event, as some browsers will fire it a lot and executing code after each event will slow down the page scrolling. Instead, I use Ben Alman's throttle library to only call my function every 500 milliseconds during the scroll event. Here's what that looks like:

$(window).on('scroll', $.throttle(500, loadVisibleImages));

The Result

After implementing this viewport-based image loading, my home stream went from loading 120 images to loading just 12 images — a much better experience for the browser and users. There are various improvements that could be made to this technique, like setting appropriate filler images for the different image types (a blank user pic for thumbnails, a meal icon for meals, etc) and maybe even using data URIs for those... but this works well enough for now. ☺