Automating git flow hotfixes with git lava!

At WebEngage, we use git flow as the branching model for our code repositories. It is a very helpful git workflow for collaborative development (read more).

How we use git flow at WebEngage

Whenever we need something fixed quickly on production we start on a git flow hotfix. This involves a bunch of steps:

  • update master and develop branches from origin (we deploy from the master branch on production)
  • look for the last hotfix version in git log to determine the next version
  • create a hotfix branch from master
  • fix the issue and commit changes in the hotfix branch
  • complete the hotfix by merging commits in master and develop
  • create a tag pointing at the merge commit in master
  • push the commits to remote master and develop branches
  • push the tag to remote

To aid in creating hotfixes, some of us use the command line git extensions (cheatsheet) while others prefer Atlassian SourceTree with its built in git flow support.

Even then the process is rather time consuming (especially when we are fire-fighting) and error prone with people creating hotfixes with same version every now and then.

Say hello to git lava

An anecdote from NARKOZ/hacker-scripts
“If something – anything – requires more than 90 seconds of his time, he writes a script to automate that”.

Hence, we wrote git lava – a small automation script over git flow for creating hotfixes.

With git lava we simply checkout the master branch, make code changes for the fix, test it out locally and then execute ..

git lava "commit_message" 

git lava scans git log and finds the next hotfix version itself, starts hotfix, commits the changes, completes hotfix and pushes the commits and tag to remote.

It even has a rollback mechanism in case of errors.

Here’s a demo of how it works …
git lava demo ...

git lava is on GitHub. Please feel free to use and contribute.

Before you go, lemme tell you why we named it git lava?
Well, because it automates HOTfixes with git FLOW → HOT FLOW → lava :clap:

Thanks for stopping by.

Hack with your soul – notes from the latest team hackathon (Oct 3, 2014)

As we dig deeper into building our product, it happens with time and fatigue that we pile up our list of to-dos. More often that not, they fail to get our attention. The engineering team decided to rejuvenate itself by delving into an energized session of a day long hackathon with intense code-love, music [yeah, Pink Floyd], and pizza on the house. The theme of this hackathon was hack with your soul!

To begin with, we proposed a set of modules on which smaller groups could devote their day hacking into, for a solution. While most were figuring out that one thing to work on, our code monkey, Nitin set out to work on two! One is a plugin for Chrome and Firefox to help sales team give live demo to prospects. The other, an even more exciting feature of search functionality based on lexical structure – it is like this universal gateway into all the functions and features of the WebEngage suite. Nitin also set out on his third hack towards the end of the day!

Vikas and Aayush teamed up to explore the feasibility of using actions in email. With the proposed hack, a call-to-action text can be provided in the subject line of an email using which a parser can do something pre-defined with the content in that email. This, incidentally, also served as an opportunity to do the much needed re-factoring of our email templating system [yeah, we use a lot of it in our system].

A quick hack by Ankit was to integrate WebEngage with Pipedrive. The most interesting twist to this one was that now, when you are using our on-site surveys for lead generation, the responses (leads) can be directly pushed into your sales CRM via our WebHooks – and, with this hack, now into Pipedrive!

Prashant, Rakesh, Manish, Radhika and Siya  teamed up for a very ambitious cause. Together they set out for developing an error capturing and visualization platform. A platform that would log JavaScript errors (in our integration code on third party sites) and create a statistical visualization for dev team to get more insights. It was almost impossible to do this in a day but a focused team effort helped the group pull this off with ease. We are super excited to roll out this hack in production.

And yes, we had support and participation from non-developers in the team too!

It has been a wonderful hack day and with loads of beer, Pink Floyd, pizza, hot tea, and code. Expect a some posts in the next couple of weeks with finer details on those hacks.

Love what we do? Come, hack with us. We are looking for you.

WebEngage Notification JavaScript Callbacks – Building A Realtime Conversion Engine On Your Website

Note: Breaking news first – we recently released our enhanced integration code which now supports a wide variety of JavaScript callbacks. If you are using WebEngage on your website, we recommend upgrading to the new code.

If you are new to WebEngage, take a quick live demo first – you’d love it!

Last week, we posted about our new integration code and how the callbacks can be put to some great use to build a “listening post” on your website for customers submitting feedback. Take a look at that here. This post focuses on how you can build some insanely beautiful interactions on your website using properties and callbacks in our Notification JavaScript API.

JS API awesomeness in Notification product

Our Notification product is a simple push messaging tool for your website. It allows you to display offers, run promotions and pop system alerts on your site from the comfort of WebEngage Dashboard – without changing any code on your site! Moreover, these messages can be “targeted” based on a bunch of rules. E.g. you can choose to display an offer ONLY to users “who are currently on the cart page AND haven’t checked out in 60 seconds AND have added atleast goods worth $200 or more”. Here’s more on targeting capabilities. You love us already? Wait, we haven’t even gotten started yet :-)

Notification is an out-of-the-box product wherein all UI/targeting level customizations can be applied from the WebEngage Dashboard. That said, using our brand new JavaScript API, you can do a whole lot more – making the notification content personalized, building real-time interactions with users on a particular offer/deal etc. Take a look at the samples below.

The low hanging fruits first …
By default, a notification doesn’t get displayed to the same user once (s)he has closed the notification or clicked on any link/call-to-action in the notification. Now, by setting a property forcedRender to true, you can override this behavior. Take a look at the code sample and documentation below for other such properties. Add the corresponding properties to your integration code and see the magic unfold!

// by default, a notification doesn't get displayed to the 
// user once (s)he has closed it or clicked on any link/call-to-action 
// button in the notification. set this property to true
// if you want to override this behavior
_weq["webengage.notification.forcedRender"] = true;
// the WebEngage integration code pops a notification on 
// your website based on preset rules in the dashboard. no
// invocation is needed; however, if you'd like to manually 
// invoke a notification, you should set defaultRender to false
_weq["webengage.notification.defaultRender"] = false;
// render a particular notification by specifiying its ID
// you can get the ID of a notification from your notifications
// list page in the dashboard. Also, a notification gets
// rendered based on rules in the targeting section; if you'd
// like to skip rule execution, you'll need to additionally
// set skipRules to true 
_weq["webengage.notification.skipRules"] = true;
_weq["webengage.notification.notificationId"] = "~10cb5811a";

Creating hyper personalized messages for your users
We support tokens, which essentially is a way to display dynamic content inside notifications. Take a look at the notification below, as seen in the notification editor.

You can use any number of [[tokens]] in the editable fields – title, description, call-to-action text and URL. Through our JavaScript API, you can replace these tokens with realized values are shown underneath.

// to replace the tokens with their corresponding values, you'll need to 
// create a JSON key/value pair for all the tokens present in your 
// notification (across fields)
_weq["webengage.notification.tokens"] = { 
  userName: "John Doe"
// let's make this truly dynamic by asking you for a name which can be 
// dynamically replaced inside the notification (shown in pic above). 
// Underneath is a code. Give it a try by clicking on the "try me" 
// button below.
$("a.try", $(".notification-tokens")).click(function(){
  var name = prompt("Please enter your name", "John Doe");
  if(name != null){
    name = (name == "") ? "John Doe" : name;
    _weq["webengage.notification.tokens"] = { 
      "userName": name
    // we don't want WebEngage widget to automatically render an 
    // applicable notification as per the rules set in the dashboard
    _weq["webengage.notification.defaultRender"] = false;
    _weq["webengage.notification.skipRules"] = true;
    // we want to show the notification everytime you do a "try now" - 
    // irrespective of whether you have closed the notification once or 
    // have clicked on the call-to-action button; by default we don't 
    // show the same notification to a user once an action has been 
    // taken; this let's us override the behavior
    _weq["webengage.notification.forcedRender"] = true;
    // this is THE notification we want to render on this page
    _weq["webengage.notification.notificationId"] = "3176124c";
    // finally, render the notification!
  return false;

Implementing custom targeting rules & callback events
Custom targeting is an interesting way to display a notification (an offer / discount / recommendation etc) based on application specific criterion. E.g. Let’s say, you sell insurances online and you’d like to push a “child education plan” to customers while they are using your product online. However, you’d like to display this message ONLY to customers who have a “family group insurance”, have “atleast 1 kid” and have an “annual family income of more than $60,000″. Sounds cool? Here’s how you create such a notification and set-up its targeting criteria in your dashboard:

/* Implementing Custom Rules */
// for the custom rules, as shown above, to take effect on your website
// you'd need to pass the actual values of parameters hasGroupInsurance, 
// numberOfKids and annualFamilyIncome for logged in users.
_weq['webengage.notification.ruleData'] = {
  hasGroupInsurance: true,
  numberOfKids: 2,
  annualFamilyIncome: 75000
// with just three lines of the code above on your website, you can now
// target your notifications based any or all of the above mentioned
// customer attributes. Click the "try this example" button to see how
// this works with token data as underneath
_weq['webengage.notification.tokens'] = {
  userName: "John",
  kidsCurrentAge: 2,
  genderText: "her"
/* Implementing Callback On Click Events */
// first, you need to enable WebEngage callbacks for your widget
_weq['webengage.enableCallbacks'] = true;
/* onOpen callback */
// binding onOpen callback for your notifications
// this gets triggered anytime a notification pops-up on your website
_weq['webengage.notification.onOpen'] = function(data){
  // fetching the clickstream information for current user
  var userIP = (data.activity.ip ? data.activity.ip : "Unknown");
  var estimatedUserGeo = + " (" + + ")";
  var browser = data.activity.browser + " (" + data.activity.browserVersion + ")";
  var platform = data.activity.platform;
  var URLofThePageOnWhichFeedbackWasSubmitted = data.activity.pageUrl;
  var titleOfThePageOnWhichFeedbackWasSubmitted = data.activity.pageTitle;
  var referrer = data.activity.referrer;
  // the notification which got clicked
  var notificationId = data.notificationId;
  var notificationTitle = data.title;
  // if you are using tokens in your notification, this array
  // will contain all of them and have their corresponding values/defaults etc
  var tokens = [];
  for var(i=0; i<data.tokenData.length; i++){
    var aToken = data.tokenData[i];
    // the identifier for each token. e.g [[userName]]
    var tokenName =;
    // you can set default value for each token in the dashboard
    var tokenDefaultValue = aToken.defaultValue;
    // the value set for each token in real-time via the JS API
    var tokenRealValue = aToken.value;
/* onClick callback */
// binding onClick callback for notifications
// if you attach this listener, every time a notification gets clicked,
// this function will be invoked
_weq['webengage.notification.onClick'] = function(data){
  // other than all the properties mentioned above, you'll
  // get these extra properties in the click event - the URL
  // and the text on the link/call-to-action button that got clicked
  var actionLink = data.actionLink;
  var actionText = data.actionText;    
/* onClose callback */
// triggers everytime a user clicks on the close button of the notification
// the data object, passed to this method, is the same as above
_weq['webengage.notification.onClose'] = function(data){

Custom data & advanced reporting
Using custom data, you can pass more information related to the customers viewing a notification or clicking on the call-to-action button. That ways, you can dice-and-slice the clickthrough information in your reporting section based on all the data you passed. Underneath is a code snippet of how to pass custom data and below that is a screenshot from the reporting section on how can clickthroughs be filtered based on these properties. Awesome. No? ;-)
/* Implementing Custom Data */
// you can add a JSON key-value map to this property
// when clicks get recorded, this data gets saved along with click
// in your reporting module (screenshot below), you can filter the
// clickthrough data based on these properties
// supported data types: string, date, boolean, integer
// you can add any number of fields depending upon your plan
_weq['webengage.customData'] = {
  underTrial: false,
  domain: "",
  daysInUse: 10,
  email: ""

That’s not all! You can do much more with other options in the API. Check out the documentation for JavaScript API (version 4.0) – along with our REST API’s and Webhooks.

Hope you liked what you saw. Do share your feedback on the API. A similar post here on how to use the JS API for our feedback product.

Stay tuned. We love you!

WebEngage Feedback JavaScript Callbacks – Building A Realtime Customer Feedback Listening Post On Your Website

Note: Breaking news first – we recently released our enhanced integration code which now supports a wide variety of JavaScript callbacks. If you are using WebEngage on your website, we recommend upgrading to the new code.

If you are new to WebEngage, take a quick live demo first – you’d love it!

This is how our new integration code looks like:
<script id="_webengage_script_tag" type="text/javascript">
  var window._weq = _weq || {};
  _weq["webengage.licenseCode"] = "_LICENSE_CODE_";
    var _we = d.createElement('script');
    _we.type = 'text/javascript';
    _we.async = true;
    _we.src = (d.location.protocol == "https:" ? 
               "//" : "//"
              ) + "/js/widget/webengage-min-v-4.0.js";
    var _sNode = d.getElementById('_webengage_script_tag');
    _sNode.parentNode.insertBefore(_we, _sNode);

In the previous version of our code, initialization of the widget was wrapped inside an onReady method. We kept it as such because of the async nature of our code plus a need to efficiently manage separate configuration and settings for all the three products from a central place once all the remote resources finished loading.

We got rid of that functional approach in this version. Instead, we created a JSON data structure with predefined keys that you can use to assign properties and attach callbacks for different events in context of each of the products.

JS API awesomeness in Feedback product

Our Feedback product allows you to create and configure a simple feedback tab for your website. You can add custom fields (radio buttons, text-boxes, dropdowns, file upload fields etc), create logical fields which are category dependent, build email routing rules and do a whole lot more from the WebEngage Dashboard. You can read more about the our feedback product here. The product is an out-of-the-box solution for your website. However, with the new integration code, you can add some interesting callbacks on your website to enhance the experience of users submitting feedback on your website. Underneath are a few samples.

UI level customizations
You can choose the default alignment, colors etc of the feedback tab for your website from the dashboard itself. However, let’s say on one of the sections on your site, you’d like to change these properties. Now, with the JS API, it is possible to do so. Just add these two lines to your integration code script on those pages/sections and see the magic unfold ..
_weq[""] = "left";          // or, "right"
_weq[""] = "#cc9900"; // hex code

Form data handling and callbacks
With the new API, not only can you pre-populate the feedback form for your signed-in users, but you can also set field level attributes (like make mandatory, move to hidden field, reduce category options etc). So, let’s say, on a certain section of your site, you only wanted to display 2 categories in the dropdown instead of all, you can do that with formData property as shown below.

The new integration code (API) also provides hooks for onOpen, onClose, onSubmit events thereby giving you the ability to implement awesome user experiences for customers submitting feedback on your website. So, you can now build something like this – offer a discount coupon code to users submitting feedback on the order confirmation page! Sounds cool? Read through the code and comments below:

/* use this initialization property to pre-populate feedback form w/ user data */
_weq[''] = [ 
  // array of JSON's for each field in the form
    // pre-populate the field with label "email"
    name: "Email",
    // set the value here
    value: "",
    // change the rendering mode of this field. other options are:
    // "readOnly" - to prevent the user from changing the value passed above
    // "hidden" - to pass as hidden variable without showing the
    // field to the user
    mode: "default"
    // pre-populate the field with label "Category"
    name: "Category",
    // overrides what was set in the dashboard
    isMandatory: true,
    // if you intend to show only a few categories on a certain page
    // use this property to specify the options you'd like to show
    // these categories should be pre-created in the dashboard first
    options: [
      "Billing Inquiries", 
      "Sales/pricing related query",
      "General feedback"
/* Callback events - the fun begins now! */
_weq['webengage.enableCallbacks'] = true;
// So, let's say you wanted your webpage to be made "aware" everytime
// any user is either opening the feedback tab or submitting a feedback.
// Underneath is how you can attach your callback functions to these events
/* Feedback onOpen callback - triggers everytime a user clicks on the */
/* feedback tab on your website */
_weq[''] = function(data){
      "User data: [" + 
        "IP - " + data.activity.ip + 
        "Browser - " + data.activity.browser + 
                     "(" + data.activity.browserVersion + ")" +
        "Platform - " + data.activity.platform +
      "]" +
      "Page data: [" +
        "URL - " + data.activity.pageUrl +
        "Title - " + data.activity.pageTitle +
/* Feedback onSubmit callback - triggers everytime a user submits */
/* feedback via the tab on your website */
_weq[''] = function(data){
  // in addition to the data that you get inside the onOpen method
  // you also get data submitted for each of the form fields 
  // along with user's geography etc.
  // iterating over the fields and fetching user submitted data
  var userResponse = {};
  for(var i=0; i<data.fields.length; i++){
    var field = data.fields[i];
    // while building your feedback form, you can choose any
    // kind of field - textbox, commentbox, radio-buttons,
    // checkboxes, file upload etc.
    // depending upon the field type, we wrap the responses
    // submitted by a user with corresponding "class" key in
    // the field object. E.g. for the auto screenshot field, when
    // used by a user, we provide a URL of the corresponding image
    // thumbnail that gets generated. for a detailed response
    // object view, click the "view response data object" button below
    var fieldValue = "";
    // auto screenshot field
    if(field.value["@class"] == "snapshot"){
      fieldValue = field.value.thumbnailImageUrl;
    // fields which produce a list of responses
    // e.g. multi-select, checkboxes etc
    else if(field.value["@class"] == "list"){
      fieldValue = field.value.values.join(", ");
    // fields which produce a textual response
    // e.g. textboxes, comment boxes etc
    else if(field.value.text){
      fieldValue = field.value.text
    userResponse[field.label] = fieldValue;
  /* Additional Information */
  // along with data submitted in the feedback form, our
  // API also shares with you additional details to
  // provide more context around the submission.
  var feedbackSubmittedOn = new Date(data.activity.activityOn);
  var userIP = (data.activity.ip ? data.activity.ip : "Unknown");
  var estimatedUserGeo = + " (" + + ")";
  var browser = data.activity.browser + " (" + data.activity.browserVersion + ")";
  var platform = data.activity.platform;
  var URLofThePageOnWhichFeedbackWasSubmitted = data.activity.pageUrl;
  var titleOfThePageOnWhichFeedbackWasSubmitted = data.activity.pageTitle;
  /* Taking action on user submitted data */
  // at this point, you know what data did the user submit
  // for each of the fields - name, email, category etc.
  // so, e.g. if you wanted to redirect the user submitting a sales
  // inquiry to your lead generation page and pass his/her
  // email to that page, here's how you can do it:
  if(userResponse["Category"] == "Sales/pricing related query"){
    // automatically close the feedback tab first!;
    // now, redirect the user to the lead page
    window.location.href = 
      "" + userResponse["Email"];

Wait, that’s not all! You can do much more with other options in the API. Check out the documentation for JavaScript API (version 4.0) – along with our REST API’s and Webhooks.

Hope you liked what you saw. Do share your feedback on the API. Here’s a similar post on how you can use our JavaScript API with the Notification product to build a realtime conversion engine for your website.

Stay tuned. We love you!

A peek into WebEngage’s security layer – super cool use of Java annotations

Note: If you are new to attribute programming, we recommend giving Attribute based programming or Java Annotations a read first.

To begin with, let us show you a small code snippet from one of our controllers. The saveSurveyResponse method underneath gets called every time some user takes an in-site survey on a customer’s website.

 * This method is invoked everytime someone takes a survey on a customer's website.
 * It performs two main tasks -
 * 1. saves the user's response in our database
 * 2. refreshes the stats and data graphs for the corresponding survey
public ... saveSurveyResponse(...) throws IOException {
  //mundane code here to compute a response object "surveyResponseDto"
  //first save the response in database
  this.publisherBc.saveSurveyResponse(licenseCode, surveyResponseDto);
  //then, refresh all the analytics associated with this survey
  this.publisherBc.refreshSurveyStatusOnResponse(licenseCode, surveyId);

As you can see above, the code performs two major tasks – #1. save the response in database and #2. refresh the analytics associated with the corresponding survey. We pre-compute a lot of data graphs and collate the information to create a bunch of Maps, so that the stats for our customers can be presented in real-time, in its true essence. That’s a lot of computing work. The method refreshSurveyStatusOnResponse usually takes about half a second (and much more at times) to complete. We can’t keep the end user on a third party website waiting because (s)he took a simple survey and we got in to some crazy computing business!

Solution? Simple – make that method call asynchronous.
Yes, one would either use a batch process or make that call asynchronous. However, it is a much bigger problem to address. There are several such methods in any application stack which should be executed asynchronously to keep the user experience intact. E.g. in any action that needs to send out an email, the sending email part can easily be made asynchronous because SMTP relays can be painful at times leading to huge lag; in the process, it keeps your end user waiting for a response.

Implementation? We created a cool Java annotation called Asynch.
We first created an annotation interface called Asynch and then implemented a method interceptor to look for this annotation on the callee. If the annotation were present, we’d execute it further in a new thread. Snippet from the implementation below.

 * Defining the Asynch interface
public @interface Asynch {}
 * Implementation of the Asynch interface. Every method in our controllers
 * goes through this interceptor. If the Asynch annotation is present,
 * this implementation invokes a new Thread to execute the method. Simple!
public class AsynchInterceptor implements MethodInterceptor {
  public Object invoke(final MethodInvocation invocation) throws Throwable {
    Method method = invocation.getMethod();
    Annotation[] declaredAnnotations = method.getDeclaredAnnotations(); 
    if(declaredAnnotations != null && declaredAnnotations.length > 0) {
      for (Annotation annotation : declaredAnnotations) {
        if(annotation instanceof Asynch) {
          //start the requested task in a new thread and immediately
          //return back control to the caller
          new Thread(invocation.getMethod().getName()) {
            public void execute() {
          return null;
    return invocation.proceed();

Done? Just that much?
Oh yes, pretty much. Here’s how the declaration of heavyweight method, refreshSurveyStatusOnResponse, looks like -

 * So, earlier we had a simple method in our interface which we later
 * annotated with the Asynch @interface. Bang! The caller doesn't need
 * to worry about it now. This method (no matter who the caller is)
 * gets executed asynchronously. Ain't that awesome? 
public void refreshSurveyStatusOnResponse(String licenseCode, Integer surveyId);

How did we use annotations to build our security layer?
Now that we gave you a fair idea of how we are using Annotations to our advantage, let’s dive a bit deeper. You are about to see how we built our entire Authorization and Authentication stacks using Java Annotation.

We are security freaks. Be it the web layer or data exchange layer, access to all our code is protected. The caller of a method is denied entry to the method if it does not have right privileges. Underneath is a small snippet from one of our web layer controllers. This method is invoked when someone tries to edit a survey from the WebEngage dashboard.

 * This is a public method invoked via a URL on the site. Once a user on the site
 * tries to reach this method, the "rules" specified below (via annotations) are
 * evaluated. If it matches with the criterion specified for the UserAuth class,
 * the user is allowed an entry into the method; otherwise is shown the exit door!
 * In the specific example below, only "signed-in" users who are "authorized 
 * publishers" (our terminology for WebEngage customers) AND have access to 
 * "survey configuration" related features, are allowed entry into this method.
@UserAuth (
  userTypes = {
  publisherUserFeatures = {
public ... edit(...) throws IOException{

Pretty nice. Right? So just by annotating the edit method with UserAuth, we made sure that survey edit URL’s returns a sweet nothing to those who are not supposed to use those URL’s. Where’s the beauty? This piece of annotation is reusable; we use it in a variety of ways on pretty much all the code that needs to be protected behind the concepts of user and their corresponding roles.

Of-course there’s a lot of application specific code behind understanding the annotation UserAuth. However, that’s one time and we have managed to reuse it in a much more sophisticated manner inside our Business Layer as well. Take a look at this usage in one of the methods below:

 * This is our business component method for customers who intend to change
 * the styling of their surveys to match the CSS with their site's look and
 * feel [oh, we have a sexy CSS editor inside dashboard for them to do so ;)]
 * This is a nested annotation. A list of @Authorize methods can be specified
 * as rules. Each of them specifying the method to be called (for authorization)
 * and the argument to be passed to it. For the caller to get access, these
 * values should meet the AuthRules criteria. If it does not, an 
 * AuthorizationException is thrown.
  authRules = {
      method = "hasPublisherUserFeatureAccess", 
      sargs = {"$0", "SURVEY_STYLING"}
      method = "hasPublisherFeatureAccess", 
      sargs = {"$0", "WE_SURVEY_CUSTOM_CSS"}
public void saveSurveyStyleCss(
  Integer publisherId, 
  AdhocAttributeName cssAdhocAttributeName, 
  String css
) throws AuthorizationException;

This explains how powerful annotations can get. At WebEngage, we use them to the fullest. Hope this article helps you build some cool stuff with attributes. Do let us know!

Note: Use the Asynch annotation idea with care. Spawning new threads without being in control can be fatal. If you plan to use it, make sure the threads are fetched from a pre-created thread pool.

Stay tuned. We love you!