Friday, July 19, 2013

Mail Chimp 2.0 API Interface

As part of my position I have opportunity periodically to suggest solutions that will help the marketing and communications team streamline their operations.  I had such an opportunity to recently when I made a pitch to use third party to manage our customer communications.  I looked at two solutions Constant Contact and Mail Chimp.

Both of these solutions do essentially the same thing - they provide the ability to manage emails in different lists and provide the recipients the ability manage their subscriptions.  The customer base would be initially subscribed to four lists - System Outages, Product Updates, New Product Sales, and Event Notifications.  One of the features the marketing team wanted was the ability to target specific users within each mailing list.  For instance, having the ability to target a group of customers in the Product Updates list that currently have an earlier release of the product and are now eligible for reduced rate upgrade.  Sending this notification to the entire list of customers would have adverse results on the company's bottom line.  The ability to segment the mailing list into smaller and more specific groups is a feature that allow users from having to maintain and manage a myriad of lists for specific customer segments.  Imagine a new mailing list having to be created for each segment of the customer population and having to merge and maintain the "opt out" lists between them.  It will also prevent the customer from having to navigate (potentially) a huge number of mailing lists and unsubscribing them - all the while being added to more as they fall into newly created segments.

The intention of this entry isn't necessarily to point out the weaknesses or strengths of the Mail Chimp and Constant Contact products (although it comes up a bit).  Rather it is to partially illustrate the solution that was created for the interface to the production was selected for a deeper analysis.  After some research and playing with the Mail Chimp and Constant Contact products I settled on Mail Chimp for a deeper analysis.  This was because of one primary reason - they have list segmentation built into their tool.  

My first attempt at the API didn't give me much hope that final product would be easy to maintain.  It also had the side affect of having duplicated code within every method.  As illustrated below in the Ping and Account Details methods located the "helper" section of Mail Chimp's "REST-like" interface.

 public AccountDetail AccountDetails()  
 {  
   string jsonObject = this.PostRequest("/helper/account-details", this.JsonApiKey);  
     
   AccountDetail accountDetails = JsonConvert.DerializeObject<AccountDetail>(jsonObject);  
   
   return accountDetails;  
 }  
   
 public string Ping()  
 {  
   string jsonObject = this.PostrRequest("/helper/ping", this.JsonApiKey);  
   
   Ping ping = JsonConvert.DeserializeObject<Ping>(jsonObject);  
   
   return ping.Message;  
 }  
  

From this small example you can see that with each method I'd be having to serialize the parameters within the PostRequest method.  I'd also have to produce several PostRequest methods handling all cases of the different parameters or worse modify the method to handle dynamic parameters.  Not to mention that with each method call I'd be having to deserialize and handle exceptions in the Json return.  So I made my first adjustment to the PostRequest method which provided some relief to these dilemmas.

 protected T PostRequest<T>(string method, string payload)  
 {  
   Type type = typeof(T);  
   
   if ( !type.IsDefined(typeof(JsonObjectAttribute), false) )  
   {  
    throw new InvalidOperationException("Object type invalid");  
   }  
   
   string jsonObject = PostRequest(method, payload);  
   
   return JsonConvert.DeserializeObject<T>(jsonObject);  
 }  

So now I can simplify the Ping method and remove the need to deserialize the data being returned from the Mail Chimp methods.

 public string Ping()  
 {  
   Ping ping = PostRequest<Ping>("/helper/ping", this.JsonApiKey);  
   
   return ping.Message;  
 }  

This doesn't completely solve my problem, as I discovered later, as I needed to send over more than just the API key in all but the most simple of methods.  This required another change to PostRequest method.

 protected T PostRequest<T>(string method, Object jsonParam)  
 {  
   Type type = typeof(T);   
     
   if ( !type.IsDefined(typeof(JsonObjectAttribute), false) )   
   {   
    throw new InvalidOperationException("Object type invalid");   
   }  
   
   Type parameter = jsonParam.GetType();  
   
   if ( !parameter.IsDefined(typeof(JsonObjectAttribute), false) )   
   {   
    throw new InvalidOperationException("Parameter Object type invalid");   
   }  
   
   string jsonParameterString = JsonConvert.SerializeObject(jsonParam);  
   
   string jsonObject = PostRequest(method, jsonParameterString);  
   
   return JsonConvert.DeserializeObject<T>(jsonObject);  
 }  

This change would allow the users of the API interface to pass any object with a JsonObject attribute (I used the Json.NET assembly by Mr. Newton-King) into the PostRequest method.  PostRequest will check to ensure the object is valid, serialize the object to a string, and pass it over the wire to Mail Chimp.

Please note I realize the PostRequest still needs a lot of work - and there has been a lot of work to it and the interface over all during the evaluation period.  The final method also implements more error checking and reflection in order to make the interface work.

I had originally considered providing this code over at CodePlex.  A couple of things happened before I did this however.  First, I was working against the (at the time) BETA version of the 2.0 API.  It was clear the API needed more work and polish before I could consider it ready for prime time.  Second, the developers over at Mail Chimp, as was necessary for a pre-release product, would make breaking changes to the API.  Notifications would be made - but only within the various threads of their forums.  I wasn't a regular scanner of the forums and didn't read every thread over to catch them and thereby had to react once something broke in my unit tests.  Additionally the breaking changes wouldn't be documented quickly or at times at all -meaning that I'd trip across something and not find it on the forums anyplace.  This may sound a bit critical, which isn't the intention, but for a BETA product that was released to the general public it seemed a bit immature to create a semi-stable solution for evaluation.

The final issue was more of a design challenge within Mail Chimp that didn't fit into our requirements.  It seems that when a customer opts out from a list they are completely deleted from the database within Mail Chimp.  With Constant Contact the customer is retained and marked in a fashion that prevents the campaigns from sending out any more correspondence.  Our desire was to continually refresh the lists with customer information - we didn't want to give any thought to who was already added, who opted out, and was never added before.  We also wanted to continually update the properties of a customer so that segments could be dynamic within each of the lists.  As it stood at the time of this writing I was able to re-add a customer to a list where they had previously opted out.  Once they were re-added I was successful in sending emails to that customer again.  This essentially would put us into the position of having to maintain our list of customers that have opted out - something that is a core feature of any tool that we decided to use.