Pages

Friday, 15 April 2011

Making 3rd-party API Libraries more Robust

In this post we'll be combining Generics, Functions and Actions with the Decorator Pattern to solve the following problem:

"Network requests are flaky"

Every time you make a call "across the wire" you've got to provision for errors. Everything from gateway timeouts, server faults right down to your toddler pulling the lan cable out. If it can happen, at some point, it probably will.

Most solutions are very ad-hoc and resemble this:
using (var wc = new WebClient())
{
  for (int i = 0; i < 5; i++)
  {
    try
    {
      wc.DownloadFile(source, target);
      break;
    }
    catch (Exception e)
    {
      Thread.Wait(200);
    }
  }
}

This approach is fine if you only have a couple of calls in your application. In today's hyper-connected world, however, it's more likely that you'll be making lots of these requests with wildly differing signatures. Very quickly your code will become unreadable as you battle to trap all the possible exceptions and manage the retries. What we need is a pattern for easily applying this retry mechanism to any call whilst keeping our client code clean.

Real-world Example

There are a myriad of 3rd party libraries that allow you to target platform APIs e.g. Twitter, Facebook etc. At PhotoPivot.com we rely heavily on Sam and Tim's great FlickrNet library to perform an enormous number of calls to Flickr. Here's a greatly simplified bit of production code from our server-side application:
public class FlickrPhotoDataDownloadService
{
  private readonly IFlickr _flickr;

  public FlickrPhotoDataDownloadService(IFlickr flickr)
  {
    _flickr = flickr;
  }

  public void GetPhotoDataForUser(string userId)
  {
    var photoCollection = _flickr.PhotosGetAllNotInSet();

    foreach (var photo in photoCollection)
    {
      var photoInfo = _flickr.PhotosGetInfo(photo.PhotoId);
      var exifs = _flickr.PhotosGetExif(photo.PhotoId);

      // Do something with the data...
    }
  }
}
Potentially, any of the calls to the IFlickr interface can result in a sporadic network error. This is a serious pain if it happens inside the foreach loop when the routine is 90% through a list of 10,000 photos. If we implemented the ad-hoc solution at the start of this post the GetPhotoDataForUser method would become very untidy and a developer's ability to read it would lessen. So, let's look at some of the related classes. Below you can see the interface that the service uses to describe its requirements. We then used the Adapter Pattern to "bring in" the interface to the main FlickrNet class.
public interface IFlickr
{
  PhotoCollection PhotosGetAllNotInSet();
  PhotoInfo PhotosGetInfo(photo.PhotoId);
  ExifTagCollection PhotosGetExif(photo.PhotoId);
}
public class FlickrEx : FlickrNet.Flickr, IFlickr
{
}
The client code that uses the service looks like this:
var flickr = new FlickrEx();
var service = new FlickrPhotoDataDownloadService(flickr);
service.GetPhotoDataForUser(userId);
So, how do we encapsulate all the methods, or at least the methods we need, in the main Flickr class? We can't rely on just extending the class as it, or its methods, might be sealed. Let's start by creating a class that can keep track on failed attempts and whether a retry is possible.
using System;
using System.Threading;

internal abstract class RetryableBase
{
  private readonly int _maxAttempts;
  private readonly TimeSpan _delay;
  private int _attempts;

  internal RetryableBase(int maxAttempts, TimeSpan delay)
  {
    _maxAttempts = maxAttempts;
    _delay = delay;
  }

  protected bool CanRetry(Exception e)
  {
    _attempts++;

    Thread.Sleep(_delay);

    return (_attempts < _maxAttempts);
  }
}
Next we'll extend this and, by leveraging C#'s Func<> capabilities, create a couple of classes that can wrap a function in some standardised retry routines.
using System;

internal class RetryableFunc<TResult> : RetryableBase
{
  private readonly Func<TResult> _func;

  public RetryableFunc(Func<TResult> func, int maxAttempts, TimeSpan delay)
  : base(maxAttempts, delay)
  {
    _func = func;
  }

  public TResult Call()
  {
    TResult result = default(TResult);

    bool success = false;

    while (!success)
    {
      try
      {
        result = _func();
        success = true;
      }
      catch (Exception e)
      {
        if (!CanRetry(e))
          throw;
      }
    }

    return result;
  }
}

internal class RetryableFunc<T, TResult> : RetryableBase
{
  private readonly Func<T, TResult> _func;

  public RetryableFunc(Func<T, TResult> func, int maxAttempts, TimeSpan delay)
  : base(maxAttempts, delay)
  {
    _func = func;
  }

  public TResult Call(T t)
  {
    TResult result = default(TResult);

    bool success = false;

    while (!success)
    {
      try
      {
        result = _func(t);
        success = true;
      }
      catch (Exception e)
      {
        if (!CanRetry(e))
          throw;
      }
    }

    return result;
  }
}

You will, unfortunately, need to create a new class for each signature function call needed. For instance, the one's above will work for calls that match Func<TResult> or Func<T, TResult> only. Func<T1, T2, TResult> will need another class. Functions that don't return anything are Actions<>. You'll need separate classes for these also. Once done, though, the ROI will be huge.

We now have the capability to make calls robust but how do we 'invisibly' add this to someone else's class? We'll use the Decorator Pattern to create a new class that "is a" IFlickr but also "has a" IFlickr. By initially abstracting our service's needs to an interface we have enabled ourselves to do this very easily.
public class RetryableFlickrEx : IFlickr
{
  private readonly IFlickr _flickr;

  public RetryableFlickrEx(IFlickr flickr)
  {
    _flickr = flickr;
  }

  public PhotoCollection PhotosGetAllNotInSet()
  {
    var retry = new RetryableFunc<PhotoCollection>(_flickr.PhotosGetAllNotInSet);

    return retry.Call();
  }
  public PhotoInfo PhotosGetInfo(string photoId)
  {
    var retry = new RetryableFunc<string, PhotoInfo>(_flickr.PhotosGetInfo);

    return retry.Call(photoId);
  }
  public ExifTagCollection PhotosGetExif(string photoId)
  {
    var retry = new RetryableFunc<string, ExifTagCollection>(_flickr.PhotosGetExif);

    return retry.Call(photoId);
  }
}
Now that we have all our pieces in place the ONLY change we need to make to our entire application is one line in the client. The service still gets the IFlickr it requires and simply doesn't care about the implementation:
var flickrBase = new FlickrEx();

// New line: Use the Decorator Pattern to extend functionality at runtime...
var flickr = new RetryableFlickrEx(flickrBase);

var service = new FlickrPhotoDataDownloadService(flickr);
service.GetPhotoDataForUser(userId);

Please leave comments if you can see a way of enhancing this idea.

Friday, 14 January 2011

The Biggest Challenge Faced by Technical Founders

When someone technical decides to step off the cliff and become a founder of a startup there are always going to be challenges. Most programmers and developers believe that these will come from areas outside of their comfort zone - sales, marketing, taxes, fund-raising, design and even just networking! This is why the standard co-founders model partners someone technical with someone more 'businessy'. The reality, however, is very different.

For me the biggest challenge I face as a technical founder is programming. To be more specific: stopping programming. For years I have measured my progress by ticking off features delivered, bugs fixed or user stories completed. It's a well understood system and it's easy to track, but that's not what I should be doing. My job as a founder is to find a repeatable business model that will make money, and the only route to achieving this is via validated learning.

Here's an example:

Earlier this week I was building some new server functionality for @photoPivot. I found myself working through the options of how and where to host a service and what type of communications medium was going to be the absolute best for this. Eventually I realised "Who cares? It doesn't matter!". All I needed to do was test my idea in the quickest way possible. Just stick it in a console app and off we go (sneakernet style!).

Now, this doesn't mean that you should write bad, unmanageable code. You can still test your ideas in a way that isn't going to back you into a nasty corner in the future. Just be a little pragmatic. Do enough to get it working, then step away. I know how difficult it is to know that you could make something better - it's very hard to resist being the perfect craftsman. Once you've proven your idea then you can make it elegant; but why waste your time polishing something that you don't know people want?

For technical founders, programming is chicken soup. On difficult days we find comfort there and, by the evening, we've convinced ourselves that, yes, we made good progress today. Don't be fooled by these false metrics. Test your ideas quickly and build your company.

Monday, 20 December 2010

Time to Move On

For the last 9 months I have been a part-time entrepreneur. From 1st January it'll be full-time.

I have now worked at the same software company for 10 years. I started as a developer and worked my way up to become the Development Director. Over the past 2 years I have transformed the company's development department with changes that will continue to help it deliver working software long after my departure. I moved us from a classic waterfall model, with manual deployment, to an Agile process (Scrum) incorporating SOLID principles, unit tests, continuous integration & build servers and, nearly, fully automated deployment of a dozen commercial websites. I am very proud of the changes I have made and I leave a company which now scores 9/12 on the Joel Test.

I'm giving all that up.

My own, personal, transformation didn't stop there though. Over the last year I have become more and more excited by the business side of software development. I spent countless evenings and weekends consuming all I could find from such luminaries as Dharmesh Shah, Seth Godin, Geoffrey A. Moore, Eric Ries etc. The possibility that a 'simple developer' could expand his horizons and create something more than just an application was exhilarating. During 2010 I had, and still have, a hat-full of ideas; one of which bubbled to the top of the stack quite effortlessly. After the creation of an MVP, a few public demos & pitches and some incredible user feedback I'm now ready to go for it. I've learnt all I can without taking that final step off the cliff.

In 2011 I'll be looking for more people to come join me in helping change the way people interact with social media - starting with TweetPivot. If you want to know what that involves you'll have to follow me on twitter or even just come talk with me!

Finally, I'd like to thank some local groups (e.g. Pitch and Mix), some global groups (e.g. Microsoft's BizSpark program) and an innumerable number of individuals who have supported and encouraged me thus far. Almost any hour of the day can feel like a lonely 3am when you're an entrepreneur and having people that can help you refocus is priceless.

So, wish me luck. 2011 is going to be scary-as-hell but, wow, what a sweet looking ride!

Thanks,

Chris

Saturday, 16 October 2010

What are you scared of?

Finding yourself stood in front of Eric Ries can be somewhat daunting. You have the opportunity to ask a question to, arguably, the leading light in the field of Lean Startup. As soon as you start speaking, though, the guy stood behind you in the queue starts getting restless. So, you better make it a good one!

I was unable to attend the Business of Software in Boston last month so was excited to see that Eric was doing a whistle-stop talk last night at TechHub, London. After a, not too inconvenient, train and tube journey I found myself in a vibrant room packed with budding entrepreneurs. The 'formal' part of Eric's talk was mostly content that I'd already seen online, so no major surprises there. What always makes these events unique, though, is the interaction with the people that turn up. The Q and A hand-raising session was very busy but I did manage to talk with Eric afterwards. Here's what I asked him:

Imagine you're back at IMVU circa 2004, but this time you know what you know now. You've created a Minimum Viable Product for just one Instant Messaging network that's already getting good traction. Geoffrey Moore would have probably suggested that you capture your beachhead in that first IM. Would you agree with that or would your next move be to create an MVP for each IM?
As is often the case in this field there was no short, definitive answer! What follows is paraphrased.

Eric: "What are you scared of?"
Me: "Uh, nothing. I'm an entrepreneur creating a startup - by definition am I not fearless?"
Eric: "No. What's your biggest fear?"
Without any prepared answer for this one I said "Well, probably someone else copying us". Eric rocked back, head shaking "No." I got the impression that he hears this 'wrong answer' often.

The gist of Eric's response was that I needed to look at my assumptions. I'd assumed that because my product 'works' for one social network that it will automatically be greeted in the same manner on all the other networks. The way you reassess these assumptions is to do more measuring against hard targets e.g. I'm currently at 10, I think I can get to 20 by time t. If the increments are 10, 12, 14... you're probably gonna make it, but if they're 10, 11, 11.5... you might have a problem. Testing your worst fears early is key.

MBAs are seen as a hindrance in the startup world, but the remainder of Eric's lesson to me did seem to cross a chasm into this territory. The basics of the Lean Startup are pretty straight forward: iterate through 'the loop' as quickly as you can and learn what works. Simple. Once you get into the guts of it, by talking with Eric, it starts sounding a whole lot more complicated. If I wanted an MBA I would have studied for one. I like my vision of the startup as having less politics, less overheard and, by extension, less rigor; but, again, this is an assumption.

I am, however, extremely grateful for the way Eric makes you question all of your assumptions - sometimes to the point of your own existence!

So, what is my biggest fear? After some considerable thought I think it's that people won't like what I do. I fully agree with Eric's observation that the best way to utterly deject developers is to have them spend time and energy creating something only for it to never see the light of day. Lean helps us avoid wasting everyone's time and for that, alone, it is worth pursuing.

Tuesday, 12 October 2010

Standardising String Cryptography

I was writing the new Twitter Client for TweetPivot when I hit a problem. I needed to store a small amount of information on our users' machines; specifically in isolated storage. Most of this would be fine as plain text but one field needed to be encrypted. This meant I had to solve a problem I seem to come back to again and again - simple string cryptography.

Crypto Rule #1 seems to be "Don't roll your own.". OK fine, so give me something pre-rolled. C# / .Net doesn't contain the method I was looking for:

string Encrypt(string plainText, string password, string salt);

Luckily, @blowdart came to my rescue and posted this code. Now, there's a few things worth pointing out about this:
  1. Barry's book sales suggest he might actually know what he's doing, but who else amongst us has the confidence and knowledge to say that it's safe and reliable?
  2. Did anyone read the code and think "actually, TripleDES would suit my needs, not Rijndael"?
Answer: probably no.

So, given that a) it's hard to do correctly and b) this standard method would probably suit 99% of developers 99% of the time why, then, do we not find this in the BCL? If I want to open a file I can use System.IO.File.OpenRead(string path). If I need to take more control over the process I can use a StreamReader and dictate encodings etc.

I've included my final code below (you deserve something for reading this far!). Porting it into Silverlight simplifies matters - mainly because there's only one implementation of SymmetricAlgorithm. "But Barry defaulted to Rijndael. Does this mean that Silverlight is less secure than standard .Net applications?" Another crypto conundrum!

I do feel that this functionality needs abstracting behind a simplified facade. I genuinely don't care how long my initialisation vector is or whether I'm using DES, DoubleDES or even TripleDES. Cryptography is simply a tool for hiding stuff and it should be easier, safer and more confidence-imspiring than it currently is. @blowdart's subsequent tweet kinda sums up the complexity of the subject. Signatures, really? You might just as well have asked me if I wanted tweeters with it! #referenceToOldJoke
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public static class CryptographicExtensions
{
  public static string Encrypt(this string plainText, string password, string salt)
  {
    var algorithm = new AesManaged();

    algorithm.SetKeyAndIV(password, salt);

    return Encrypt(algorithm, plainText);
  }

  public static string Encrypt(this string plainText, ref byte[] key, ref byte[] iv)
  {
    if (string.IsNullOrEmpty(plainText))
      return plainText;

    var algorithm = new AesManaged();

    if (key != null)
      algorithm.Key = key;
    else
      key = algorithm.Key;

    if (iv != null)
      algorithm.IV = iv;
    else
      iv = algorithm.IV;

    return Encrypt(algorithm, plainText);
  }

  private static string Encrypt(SymmetricAlgorithm algorithm, string plainText)
  {
    var plainTextAsBytes = Encoding.UTF8.GetBytes(plainText);

    using (ICryptoTransform transform = algorithm.CreateEncryptor())
    {
      using (var outputStream = new MemoryStream())
      {
        using (var inputStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write))
        {
          inputStream.Write(plainTextAsBytes, 0, plainText.Length);
          inputStream.FlushFinalBlock();
          var cipherText = outputStream.ToArray();
          return Convert.ToBase64String(cipherText);
        }
      }
    }
  }

  public static string Decrypt(this string cypherText, string password, string salt)
  {
    var algorithm = new AesManaged();

    algorithm.SetKeyAndIV(password, salt);

    return Decrypt(algorithm, cypherText);
  }

  public static string Decrypt(this string cypherText, byte[] key, byte[] iv)
  {
    if (string.IsNullOrEmpty(cypherText))
      return cypherText;

    if (key == null)
      throw new ArgumentNullException("key");

    if (iv == null)
      throw new ArgumentNullException("iv");

    var algorithm = new AesManaged() { Key = key, IV = iv };

    return Decrypt(algorithm, cypherText);
  }

  private static string Decrypt(SymmetricAlgorithm algorithm, string cypherText)
  {
    var cipherTextAsBytes = Convert.FromBase64String(cypherText);

    using (ICryptoTransform transform = algorithm.CreateDecryptor())
    {
      using (var outputStream = new MemoryStream())
      {
        using (var inputStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write))
        {
          inputStream.Write(cipherTextAsBytes, 0, cipherTextAsBytes.Length);
          inputStream.FlushFinalBlock();
          var plainTextAsBytes = outputStream.ToArray();

          return Encoding.UTF8.GetString(plainTextAsBytes, 0, plainTextAsBytes.Length);
        }
      }
    }
  }

  public static void SetKeyAndIV(this SymmetricAlgorithm algorithm, string password, string salt)
  {
    string paddedSalt = salt;

    while (paddedSalt.Length < 8)
      paddedSalt += salt;

    var rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(paddedSalt));

    algorithm.Key = rfc2898.GetBytes(algorithm.KeySize / 8);
    algorithm.IV = rfc2898.GetBytes(algorithm.BlockSize / 8);
  }
}

Tuesday, 21 September 2010

Great, another Pivot!

The word 'Pivot' seems to be somewhat of a favourite amongst the tech community these days. It all started with Pivot tables back in the late 80's. In the past few years we've added 3 new pivots to the list:


If that wasn't enough we now have a fifth! The final Windows Phone 7 SDK launched a couple of days ago and contains, wait for it, a Windows Phone 7 Pivot Application template.

I work with two of these (the Lean Startup and Live Labs) and it's already confusing trying to explain to potential customers which pivots our business uses.

Seriously, no more. Please?

Maybe this is a compelling reason for companies to trademark their products?

Wednesday, 18 August 2010

A Rebuke of Twifficiency

Unless you've been asleep under a stone for the past 48 hours you couldn't have failed to notice the Twitter phenomenon that is Twifficiency. Created by James Cunningham it generated an efficiency score based on your Twitter usage. In order to access the service, though, you had to grant the application access to your Twitter account.

Unwittingly, or not, James had violated one of Twitter's codes of practice re: abuse of privileged access to accounts. Specifically, the application automatically tweeted a message from the user's account in the format:

My Twifficiency score is x%. What's yours? http://twifficiency.com

Generally, this annoyed people but, because of its viral nature, the term "Twifficiency" started trending worldwide.

Now, can we please get some perspective on this?

Yes, it trended. Yes, it spread like wildfire. Yes, everyone's talking about it; but so what? This is the Twitter version of "Britain's Got Talented X-Factor for me Nan". It was hugely popular but, ultimately, worthless. Shouldn't we be discouraging this kind of sensationalism and, instead, be encouraging the trending of remarkable products (to quote Seth Godin). The mere fact that it trended says nothing about its worth - because it trended itself automatically, virally. This is the complete opposite of something that trends because users want to tell their friends about it. In fact self-promotion obfuscates this valuable measure.

James is, undoubtedly, a very nice chap who was just experimenting with an idea, but for some people to be considering offering him a job! Seriously, no. Stop.

Tech Crunch hits the nail on the head in their blog post and I would like to add to that by pointing out that Stephen Fry only scored 3%!!!

Now, if James had created the site with an opt-in option for re-tweeting (as opposed to its new opt-out option) and it had still trended; then, wow! That would have been a truly remarkable feat. The fact is, though, that you've removed any meaningful way of measuring your success.

I have no doubt that James will go on to great things but, I'm afraid, I think he should be infamous, not famous, for Twifficiency.