Thrust Division

/ blog

Targeting Monkey: the stack

August 10, 2015

As promised, I'll bring you a little more insights on the stack behind our new product Targeting Monkey. Targeting Monkey will be launching on Product Hunt tomorrow and we look forward to welcoming new users to the service.

Visit Targeting Monkey now

If you are reading this blog, you're likely someone who's interested in the technicalities of a product like ours, so Is'll go straight to the juice.

The front end (dashboard)

The front end is a simple Laravel app. We opted for Laravel because we are very comfortable with it and find it is very easy to create a simple dashboard with authentication and billing. We were torn between Laravel and RoR, but went for the former as we had just finished another project with it and it was just faster.

The front end deals with the following:

This is accomplished with either built in features (like Cashier) or simple Models. We strove to keep the dashboard as simple as possible in terms of code.


One thing we found Laravel doesn't handle very well are coupons. (By the way, use coupon THRUSTDIVISION for 6 months of free “Serious” plan).

We needed to add coupon verification to stripe, so that when a user enters a coupon, prices are changed and a notification is shown. We also wanted to prevent users from submitting the upgrade form with invalid promo codes.

To accomplish this, we created a controller that validates coupons and returns a description of it (e.g. Free for 6 months, then $39/month). Then, we added a new validation to our form submit that validates the coupon, and a listener that monitors changes to the coupon field and verifies it. We found it to be a simple and elegant solution.

Pro tip: we use browserify for all our front end javascript—it makes everything modular and changes very easy to make right.

The homepage

We went through different versions of the homepage, and we finally settled on one we think makes it clear what the service does, and how easy it is to implement it.

The dashboard is not perfectly responsive. Yes, it is mobile first, but we recognized that 99% of users who'll create an account will do so from a desktop or at least a tablet, so it is not necessary that every form looks perfect on every device.

The homepage is a different story, though. The website will be shared on publications that are often read during a commute and it is very important to make a nice impression on all devices. Therefore, we made sure the homepage looks good at all sizes.

The backend

The whole point of Targeting Monkey is that is is very simple to incorporate into any page without the need for back end code and database updates. A site like the one you're browsing, hosted on Amazon S3, can use it to personalize things like contact information based on the location of its users.

Our first thought was to use javascript. We could create a nice library with methods such as'gb_london') that could be used to change stuff on the homepage.

There are two major problems with this approach:

  1. If the user loads new stuff into the DOM with ajax, or uses one page apps, the whole code needs to be rewritten to make it work;
  2. Even on regular sites, if the javascript is executed before the <body> it won't find anything in the DOM, and if it is executed after, users will notice the content changing. Javascript was no good.

Using CSS

We therefore decided to go with the best language that is processed before the DOM is rendered and won't show any weird “flashes” to the user. CSS.

The idea was nice, and probably novel, but it lacked elegance. CSS Classes are just that, classes. They don't have a value, and everything is in the same messy place. So we went with attributes.

We created a very elegant syntax to use Targeting Monkey. To show an element to people who we know are outside of Tokyo, Japan, we can write the following:

<div monkey-hide="city_id.is_not? jp_tokyo">

It looks like Ruby.

Generating the CSS

Ruby-like as it is, the CSS is actually generated by a Flask based python app. We like to keep the front end and the actual app as separate as possible, and there is very little the Flask app shares with the dashboard. In fact, even the databases are different.

When a user is created on the front end, or a domain is added, there is an Api liaison class that places very tiny bits of information on a redis cluster, ready to be accessed by the API. There are only a few keys, like tm.domain_to_owner, tm.user_quota, tm.domain_hits, tm.user_json_allowed, etc. This allows us to move at different speeds with the two apps.

Our python app uses the information it generates for the specific IP and compiles them into a CSS file which is like this:

[monkey-show]:not([monkey-show*="true expression"]):not([monkey-show*="another true expression"]):not([monkey-show*="etc"])
display: none;

Clearly, the CSS is much longer, and can be seen here.

Quotas, hits tracking

Since everything is done in the front end, we always get a Referal (sic) header with all requests. This tells out which domain the request came from, and we can track it to make sure we bill the right amount.

To avoid making loading take too long, we have set the cache maxage to 0, but will return a 304 header to all requests from the same domain the same day. The loading time is usually very fast.

Generating the information

We use the standard method for generating information about an IP address. We have multiple databases and cross check the location to make sure it is right. When it cannot be cross checked, we do not return the attribute city_id, but only likely_city_id. We allow the developer to choose whether accuracy is important or not for his specific use-case.

The city name

We noticed that quite often IP addresses are registered to small cities part of a bigger entity. You'll often see an IP registered to Beverly Hills, California. Obviously, you'll want to show Los Angeles.

We use an algorithm that finds the largest city that is close enough to the original city that can be assumed that the latter is part of the former. Woah. I had to read that again!


We host everything on AWS, using Elastic Beanstalk for deployment of the actual code. We found it very efficient and fast.


We'd love for you to reach out on twitter if you have questions regarding Targeting Monkey, and be sure to tell us what you think of our new tool. We think it's pretty cool, and really hope you do too.


Next time you'll hear from me, I'll have some news regarding a product we have neglected for a while, but that tens of thousands of people each month relay on to help with their outreach. We have big updates coming up.