RJMetrics Connectors for Dummies

It’s currently hour 23 of 24 for RJMetrics’ third hackathon, and I’ve officially built, tested, and deployed a brand new RJMetrics data connector on our beta Data Import API. It sucks down data from our marketing automation platform, Pardot, and updates my RJMetrics dashboards on an hourly basis.

This is exciting for several reasons:

  1. We now have a functional, close-to-release version of the Data Import API. This API will become the new standard for how data gets pushed to RJMetrics.
  2. The API is dead simple to integrate with. I am not a “real” developer and I was able to build an incredibly useful connector in 1 day using Ruby and deploying on a completely free Heroku instance.
  3. The dashboards that I use every day are now auto-updated without me having to lift a finger. This will save me several hours every week.

The Data Import API

We’ve kept our Data Import API a state secret to this point so as not to incite the mob—a lot of you have been waiting for this for a long time. But we’re close to launch, so I figured I’d let the cat out of the bag. Here’s the lowdown.

The RJMetrics Data Import API is an incredibly straightforward way to import data into your RJMetrics account. In fact, that’s all it does. Send it data, and it saves that data to a table in your account. Just make sure your data includes an ID field and is formatted as JSON and you’re good to go. Once you push data, it will become available for you to use in metrics and dashboards.

This means you can now integrate anything with RJMetrics. Customer support platform? Check. Inventory management platform? Check. CRM? Check. Custom application developed by Fred from your IT department? Check. Every aspect of your business can now be seamlessly integrated into your cloud business intelligence solution.

Building Your Connector

The logic of a connector is incredibly easy:

  • Identify a data source
  • Select all the rows
  • Push them to the Data Import API

The wonderful thing about this logic is that you don’t have to track what rows are new, old, updated, etc. Just push all the rows across. Our warehouse will match records by ID and perform inserts and updates as appropriate. Much less for you to worry about.

If you have many millions of rows in a given table, you may want to include logic to only push certain of those rows. The dataset that I was using was small enough that this wasn’t a concern.

Copy My Connector

If you develop in Ruby, you’re more than welcome to use my code as a starting point for developing your own connector. I wrote a (very) simple API wrapper that is bundled as a gem for easy inclusion. Include it in your gemfile by adding:

gem 'ruby_rjmetrics', :git => 'https://github.com/jthandy/ruby_rjmetrics.git'

Don’t feel like you have to use the gem, though. If you want to see how the API call is made, it’s really just 1 line:

The connector itself is a simple Rails app with no persistence layer, deployed on Heroku. There is a single controller for each type of data that’s responsible for getting data. The controllers are then called as rake tasks from the command line, allowing them to be easily integrated into cron (or the Heroku scheduler).

Again, feel free to steal all of the code, or use it as a jumping-off point. It’s quick and dirty, but it does the trick. Get it here.

If I can do it, you can too

I’m a marketer. I write some code every once in a while, but our developers here would never let me within a mile of the RJMetrics code base. So, when I say I wrote a connector, that means that you probably can too.

We’re now accepting applicants to participate in the closed beta of the Data Import API. If you’d like to participate, just contact support.

MySQL to MongoDB Query Translator

A few weeks ago, the first-ever RJMetrics hackathon took place at our Philadelphia headquarters. I decided to throw my hat into the ring with a project I’d been thinking about for a while: a MySQL to MongoDB query translator.

This was a unique challenge because MongoDB and MySQL are very different technologies that store data in very different ways. To some, translating between them might seem like a non-sequitur. However, I knew there was a use case because of my personal experience learning MongoDB. I would often think about queries in terms of SQL syntax, and a translator like this would have greatly softened the learning curve.

The final product is available at our Query Mongo site, and I encourage you to give it a try. It’s not perfect, but we hope it will be a helpful learning tool for the many people who have SQL experience and are getting started with MongoDB.

In this blog post, I’ll provide some insights into how this tool works.

Continue reading

RJMetrics Round-Up (11/28/2012)

The hits keep coming at RJMetrics!  Here’s some of the recent progress we’ve made:

Product Updates:

  • Our new chart builder made its public debut. All users can now participate in beta testing this tool. Read more about it in our documentation.
  • We changed the font throughout our dashboard. Say hello to Proxima Nova.
  • We have expanded our time zone support. Charts with a time frame relative to “right now” will be calculated against your time zone, which administrative users can change in the settings page.
  • Email summaries now include a warning if they contain stale data – that is, data for a time period that RJMetrics has not finished calculating. These email summaries are automatically resent once that data is current.
  • Our “Cohort Analysis 101″ guide is now live at www.cohortanalysis.com.
  • We have increased the parallelization of many of our calculations, resulting in significant speed boosts.
  • We have updated our integration to support many of the improvements to the Google Analytics API recently – specifically allowing the visitors trend to be grouped across many more dimensions.
  • There are now fewer constraints on restriction sets for repeat event probability charts.
  • We’ve sped up the dashboard-wide “change all dates” tool.
  • We’ve sped up the auto-complete drop downs in the chart builder.
  • We made major improvements to file uploader, including auto-detection of the file structure for new uploads.
  • We have begun beta testing a data mapping tool that uses zipcode information as an input – stay tuned for more on this.
  • We finished production of the promotional video that was created at our last hackathon – it can be viewed at https://www.rjmetrics.com/index2

Company News:

  • Our CEO Robert J. Moore has been busy getting the word out:
  • We’re currently interviewing candidates from Drexel University’s Co-Op program for participating in next year’s RJMetrics co-op.

RJMetrics Fall 2012 Hackathon

This past weekend, we had one of the most successful events in our company’s history: 16 team members participated in a 24-hour Hackathon. With a massive trophy and a fancy steak dinner on the line, everybody came to win. 

The Rules

This was our first Hackathon, so we asked our friends at other startups about their own best practices. We settled on the rules below.

Logistics:

  • Everyone in the company is encouraged to participate, but it’s not mandatory for anyone.
  • All normal work except for time sensitive customer support should stop for participants.
  • The event kicks off at noon on Friday and ends with “pencils down” at noon sharp on Saturday. Demos and voting will immediately follow.
  • Hackathon projects can be literally anything related to the company in some way. Code, prose, images, audio, video, data analyses, web pages, physical objects and anything else are OK.
  • Participants can brainstorm ideas and form teams at any time between the announcement and the start of the Hackathon. The definitive teams need to be documented no later than one hour after the start of the Hackathon.
  • Teams can be any size with any mix of company role.
  • Normal testing/feedback/QA/review rules are not mandatory for work done during the Hackathon.
  • Hackathon projects might end up being re-written, changed, becoming a core part of what we do, or being completely discarded after the Hackathon.
  • Open source technology may be used as a component of your project.
Presentations and Voting:
  • Demos take place at the end. Each team gets up to 5 minutes for a demonstration and up to 5 minutes for questions. Presentation order will be randomly drawn.
  • Vote for the team that you feel generated the most value for RJMetrics during the time of the Hackathon. If most of the work was done ahead of time, but the final 5 percent was done during the Hackathon, they get 5 percent of the credit.
  • All Hackathon participants will cast votes in the style of instant runoff voting, but they cannot vote for their own team.
  • The winning team get their names on a trophy and a $300 gift card to Del Friscos. The trophy will circulate among the desks of the winners until the next Hackathon, á la the Stanley Cup.

Ample food was always in supply

The Projects

In the end, eight submissions were presented. Our customers should keep an eye out for some of these making their way into our product in the coming weeks.

Adding medians and percentiles to our dashboards: medians aren’t native functionality in most SQL languages, which required the implementation of special stored procedures to make them work.

Automated Training: a team used Kera.io to build interactive online tutorials in which new users can learn by manipulating their own dashboard environments.

Nate and Xiao hammering out the user community project

User Community: a tightly-integrated online community for user discussions and sharing best practices.

Responsive iPad UI: a new user interface for our dashboards that allows iPad users to have an even easier navigation experience in our product.

More Data Sources: integration with Zapier to allow us to easily connect with third party APIs like Shopify, Salesforce.com and Zendesk.

Sales Video: this sweet video was built using PowToon and VoiceBunny and may soon be appearing on an A/B test on our homepage.

New Logo: an impressive prototype for a new RJMetrics logo.

MySQL to MongoDB Query Translator: a tool to translate a traditional MySQL query into its eqivalent in MongoDB.

Red Bull gives Rohan wings

The Results

Our main conclusion from this event: Hackathons are awesome. The time flew by, we all had tons of fun and the output was extremely impressive. It reaffirmed to everyone that there really are no weak links on this team– everyone was a real competitor.

The crew enjoys the final presentations

The Query Translator and iPad UI teams took home first and second place, and both will be enjoying steaks at Del Frisco’s soon.

As for the rest of the crew, an anonymous follow-up survey revealed that over 90 percent of participants would be “extremely likely” to participate in another Hackathon. At this rate, our next one may be just around the corner.

Discover your most valuable referral sources for target marketing

If you don’t already store marketing acquisition source information in your database, take a look at my colleague Chris’ blog post on how to track user acquisition source data.

With RJMetrics, it is easy to segment your revenue and user data by acquisition source. This will allow you to find your best performing channels. We all have limited marketing budgets, so let’s invest it wisely.

In this blog, we will explore some of the reports that will help you uncover your most valuable marketing channels.

I. The “New users by sources” report shows you the number of newly registered users over time by different acquisition sources. This allows you to track the performance of referral sources in acquiring new registered users.

How to create this report in RJMetrics’ chart wizard:

  1. Initiate the chart creation process through Charts -> Create Chart.
  2. Choose the “new users” metric
  3. Now set the Time Period of analysis to your user registration period of choice, and set the Time Interval to monthly.
  4. Go to the “Group by” tab to segment by “user’s acquisition source” and select a few sources or all sources.
  5. You can change the chart to a column chart using the icons found on top of the preview.

(Shortcut: You may also clone or Edit -> Save As an existing lifetime revenue chart and add a filter for acquisition source of the new chart.)

II. The “Lifetime revenue cohorts – user source” report shows the revenue over time limited to a specific user acquisition source. This allows you to see whether users acquired from a particular source spend more with you over their lifetime than another group of users.

You can replicate this report for many user referral sources and compare them side by side to discover which source generates the highest lifetime revenue fastest.

How to create this report in RJMetrics’ chart wizard:

  1. Initiate the chart creation process through Charts -> Create Chart.
  2. Choose the “revenue” metric
  3. Choose to “Perform Cohort Analysis”
  4. Set the Cohort Date to “User’s creation date” or “User’s first order date”. Note that this is the date used to create the cohort groups (e.g. all users that registered or made a first purchase within a time interval).
  5. Set the Interval, Time Period and Duration accordingly.
  6. Add a filter under “Filter by” for “User’s acquisition source = X” to only include users from the channel X.
  7. Set the perspective to “cumulative average value per cohort member,” which will divide the metrics (i.e. cumulative revenue) by the number of people in each cohort to return average lifetime revenue per member.

(Shortcut: You may also clone or Edit -> Save As an existing lifetime revenue chart and add a filter for acquisition source of the new chart.)

III. The “AOV by user sources” report shows the average order value over time segmented by user sources. This allows you to track whether users acquired from a particular source spend more per order than another group.

How to create this report in RJMetrics’ chart wizard:

  1. Initiate the chart creation process through Charts -> Create Chart.
  2. Choose the “average order value” metric
  3. Now set the Time Period of analysis to your user registration period of choice, and set the Time Interval to monthly.
  4. Go to the “Group by” tab to segment by “user’s acquisition source” and select a few sources or all sources.
  5. You can change the chart to a column chart using the icons found on top of the preview.

(Shortcut: You may also clone or Edit -> Save As an existing lifetime revenue chart and add a filter for acquisition source of the new chart.)

IV. The “Revenue by user registration date and sources” report shows the revenue over time segmented by user sources. This allows you to identify whether users from a particular source generates more or less of your overall revenue.

How to create this report in RJMetrics’ chart wizard:

  1. Initiate the chart creation process through Charts -> Create Chart.
  2. Choose the “revenue by user registration date” metric. Note that you may have to create this as a new metric by replicating the “Revenue” metric’s settings and replace the “Order date” with “User’s creation date” as time stamp.
  3. Now set the Time Period of analysis to your user registration period of choice, and set the Time Interval to monthly.
  4. Go to the “Group by” tab to segment by “user’s acquisition source” and select a few sources or all sources.
  5. You can change the chart to a column chart using the icons found on top of the preview.

V. The “Repeat orders by user sources” report shows the number of repeat orders over time segmented by user sources. This allows you to identify whether users from a particular source make more or less repeat purchases.

How to create this report in RJMetrics’ chart wizard:

  1. Initiate the chart creation process through Charts -> Create Chart.
  2. Choose the “Number of orders” metric.
  3. Now set the Time Period of analysis to your user registration period of choice, and set the Time Interval to monthly.
  4. Go to the “Group by” tab to segment by “user’s acquisition source” and select a few sources or all sources.
  5. You can change the chart to a column chart using the icons found on top of the preview.

(Shortcut: You may also clone or Edit -> Save As an existing lifetime revenue chart and add a filter for acquisition source of the new chart.)

If you need any help creating some of these reports or would like to perform even deeper analysis, simply contact us via “Help” -> “Contact Support” from your dashboard.

How to save acquisition data from Google Analytics in your database to create awesome marketing metrics

In this post we will explain how to save Google Analytics (GA) acquisition channel information into your own database – namely the sourcemediumtermcontentcampaign, and gclid parameters that were present on a user’s first visit to your website. For an explanation of these parameters, check out the Google Analytics documentation. Then, we will explore some of the powerful marketing analyses that can be performed with this information in RJMetrics.

Why?

If you’re just looking at the default Google Analytics conversion and acquisition metrics, you aren’t getting the whole picture. While seeing the number of conversions from organic search versus paid search is interesting, what can you do with that information? Should you spend more money on paid search? That depends on the value of customers coming from that channel, which is not something Google Analytics provides. [Note: Google Analytics eCommerce Tracking does mitigate this problem by storing transaction data in GA, but this solution doesn't work for non-eCommerce sites, and certain tools like cohort analysis are not easy to do in the GA interface].

What if you want to email a follow-up deal to all customers acquired from a certain e-mail campaign? Or integrate acquisition data with your CRM system? This is impossible in GA – in fact, it is against the Terms of Service for Google Analytics to store any data that identifies an individual.  But that doesn’t mean you can’t store this data yourself.

The Method

Google Analytics stores visitor referral information in a cookie called __utmz. After this cookie is set (by the Google Analytics tracking code), its contents will be sent with every subsequent request to your domain from that user. So in PHP, for example, you could check out the contents of $_COOKIE['__utmz'] and you would see a string that looks something like this:

100000000.12345678.1.1.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=rj metrics

There is clearly some acquisition source data encoded into the string, and I have done some testing to confirm that this is the visitor’s first acquisition source. Now we just need to know how to extract the data. Luckily, Justin Cutroni has previously described how this encoding works, and shared some javascript code to extract the key bits of information.

We took this code and translated it into a PHP library hosted on github.   To use the library, include a reference to ReferralGrabber.php and then call

$data = ReferralGrabber::parseGoogleCookie($_COOKIE['__utmz']);

The returned $data array will be a map of the keys source, medium, term, content, campaign, gclid and their respective values.

We recommend adding a new table to your database called, for example, user_referral, with the columns like: id INT PRIMARY KEY, user_id INT NOT NULL, source VARCHAR(255), medium VARCHAR(255), term VARCHAR(255), content VARCHAR(255), campaign VARCHAR(255), gclid VARCHAR(255). Whenever a user signs up, grab the referral information and store it to this table.

How to use this data

Now that we’re saving user acquisition source, how can we use it?

Lets suppose we are using a SQL database and have a users table with the following structure:

id email join_date acq_source acq_medium
1 john@abc.com 2012-01-24 google organic
2 jim@abc.com 2012-01-24 google cpc
3 joe@def.com 2012-01-25 direct -
4 jess@ghi.com 2012-01-26 referral techcrunch.com
5 jen@ghi.net 2012-01-30 other organic

For starters, we can count the number of users coming from each referral channel by running the following query against your database:

SELECT acq_source, COUNT(id) as user_count FROM users GROUP BY acq_source;

The result will look something like this:

acq_source user_count
google 294
direct 156
referral 55
other 16

This is interesting, but of limited use. What we would really like to know is the growth rate of these numbers over time, the amount of revenue generated by each acquisition source, a cohort analysis of users coming from each source, and the probability that a user from one of these channels will return as a customer in the future. The queries required to do these analyses are complex – which is why we built RJMetrics. Armed with this information we can determine our most profitable acquisition channels and focus our marketing time and money accordingly.

My colleague Xiao has also written a blog post detailing how to generate these and other useful marketing analyses using RJMetrics for you to check out.

The Future of Web Dev is (almost) here!

 

Hoverboards probably won’t be on the shelves until 2015, or possibly 2014, but alpha versions of amazing, bleeding-edge web development frameworks are available today!

Recently, there have been some exciting developments in this space. Several teams are working on their own solutions with the goal of drastically simplifying the process of developing a web application.

Derby

Meteor

Firebase

SocketStream

Capsule & Thoonk

Space Magic

These frameworks boast a few common features I am very excited about:

Sharing code between client and server

I feel terrible every time I have to duplicate model validation logic in both a server-side language and javascript. These new frameworks are based on javascript, or languages that compile to javascript (like coffeescript), which means everything you write can be run on the server or in the browser.

Notably, clojure, with the invention of clojurescript, also has the capability to compile to javascript, although I am not aware of anyone working to leverage the concept of real-time model syncing and live binding into a framework. I would love to see this.

Automatic real-time model syncing and view rendering

Every web app I’ve written has had semi-specialized code dedicated to translating UI interactions into changes to a data model, and then communicating those changes to and from the server. These frameworks allow you to define UI-model bindings declaritively and then handle propagation between UI, model and server automatically and in real time. And, if one client makes a change to a model that is also powering another client’s UI, the second client receives the updated model data and has their UI re-rendered automatically as well. These are powerful ideas and will simplify or eliminate many of the typical complications that currently hinder web development.

What’s the catch?

The catch is that these technologies are still very alpha. They are either missing important features, very buggy, or regularly making breaking changes to their APIs. I’ve spent some time with Derby in particular, and it’s definitely bleeding-edge (because I got cut, bad). But, that hasn’t lessened my strong belief that the fantastic ideas implemented in these solutions will be part of mainstream web development in the near future. I’m rooting for all of these guys.

Parallel SSH and system monitoring in Clojure

During my 10% time, I created two simple clojure tools to aid in basic sysadmin tasks. Today I’m open sourcing them on github and clojars.

parallel-ssh

The first tool I built is a library for running commands in parallel on multiple servers. It takes a BASH command and a csv of server names to run the command on. Internally, one clojure agent is spawned per server and each agent is responsible for running the command and storing the result. After all the agents have completed, or a specified timeout is reached, the agents are dereferenced and their output is returned. Currently, I just shell out to run ssh and I make the assumption that password-less login is available.

This library also has a command line interface:

 

I found it useful to wrap that in a BASH script that would run a command on all of our servers. This is clearly not a replacement for sophisticated server management tools like puppet, but it is helpful when you quickly want to an answer a question such as: “How much disk space is free?” or “How many servers is this process running on?”, etc..

server-stats


The second tool is a micro-framework built on top of parallel-ssh. Similar to python’s fabric, it allows the user to define custom commands to be run on a specified group of servers. It also has the capability to respond to the results of the command run based on custom triggers. Here’s an example configuration file:

First we define the ssh username to be used and our server groupings:

 

Now we can start adding commands. Here we add a command called ‘top’ that will be run only on web-servers and app-servers:

 


Note that the doc string is used in the auto-generated usage page, so you should never have to open the config file to figure out what a command does. We can now run this command from the command line:
We can make things a little more interesting by adding alert triggers. First we need to define an alert handler function. An alert handler takes 3 arguments: the alert message, the name of the server, and the output from the command that was run. Here we add a handler called ‘email’ that will send us an email when a trigger condition is met:
Now lets define a command and trigger that will use this alert:
This command has an extra field called ‘alerts’; this is an array of trigger conditions for this command. The command ‘disk’ only has one trigger, which states “when the Use% column of ‘df -ah’ is greater than 85%, send an email with the message ‘Disk space over 85% full’”. Heres a breakdown of an alert:

  • ‘column’ is used for commands that return column-formatted output (eg. df, iostat, top), and it instructs server-stats to look at a specific column for the value. If it is not specified it will assume the command output is a scalar value.
  • ‘value-type’ tells server-stats how to parse the command result string in to a clojure value. Right now there are only three possible value types: percent, bool, and number.
  • ‘handlers’ is a vector of alert handlers to call when this condition is met. In this case, it is just the email handler.
  • ‘msg’ is the alert message that gets passed as the first argument to the handler function.
  • Finally, ‘trigger’ actually defines the condition that has to be met. It is a tuple which has a Boolean operator and a value to compare against.

Additionally, you can define a global function to be called whenever a command can not be successfully completed for some reason (eg., server timesout). Here we send a text message using Twilio whenever that happens:


 

 

conclusion

Currently this is just used for basic server monitoring, but this could easily be used for much more advanced reactive behavior. Building this in clojure was a lot of fun and pretty easy since clojure has macros, easy to use concurrency, higher order functions, and full access to java libraries. The one downside of using clojure on the command line is you have to eat the JVM startup time on every run.

resources

http://clojars.org/server-stats

http://clojars.org/parallel-ssh

https://github.com/RJMetrics/Parallel-SSH

https://github.com/RJMetrics/Server-Stats



Challenging Dogmatic Development

Developers are an egotistical bunch, and as such we tend to deal in fantastical absolutes. These range from language choice to design patterns to naming conventions, and it seems like everybody has their opinion. Fortunately, development is a team sport. One developer’s personal preferences probably won’t sync up with his team’s best practices. So, in the best interest of teams everywhere, I want to enumerate some awful (and sometimes comical) dogmatic beliefs that make development downright painful.

Horrible Dev Dogma #1: Language x is inferior

C++ and PHP (among others) seem to suffer scorn in many developer circles. For example, in Peter Seibel’s Coders At Work, development gurus such as Jamie Zawinski and Brendan Eich describe C++ as being somewhere between the awful “just an abomination” and the slightly more generous “stupid”. So, some of the most famous and most talented developers in the world denounce C++ as useless, yet C++ is one of the most popular programming languages on the market, especially for Windows applications. In fact, Google’s codebase is made up almost entirely of C++. Does this mean Google is an abomination?

Part of the sentiment stems from the language, and part from developers who use it. C++ is oversaturated with shakily implemented features that empower bad developers to do things the wrong way. Thus we have Linus Torvalds ranting about C++ as the wrong language for git and mocking devs who would suggest its use in the process. Many interpret this as an attack directly on C++, but it is really an attack on bad developers (or, more exactly, developers who think git should be written in C++). These gurus are so sick of seeing bad devs write bad solutions in a mediocre language that they finally denounce that language altogether.

Bad developers are language agnostic, as are good ones. Let’s stop denouncing languages just because bad devs use them incorrectly.

Horrible Dev Dogma #2: I always use x formatting/naming scheme and so should you

Refactoring is an important part of maintaining a healthy codebase, but personal conventions are not applicable to a team project. Is it really necessary to name every variable after a Disney character? Do you have to use spaces when everyone else on your team uses tabs? There is no good reason for this kind of stubbornness. Learn what your team does, and do the same.

Horrible Dev Dogma #3: Everyone should use TDD/CDD/x

This is pure insanity. Not everyone should use testing driven development. I repeat: testing driven development is not for everyone. Nor is comment-driven, agile, XP or any other software development method. There is no perfect development method, and all of these have their pros and cons.

Software development methods should make the developer’s life easier, not harder. Additionally, the development method should bow to the developer, not the other way around. TDD is painful for some and convenient for others.

Pete Sergeant at Write More Tests has an interesting take on TDD which I find particularly appealing. TDD answers some interesting questions about software development, and most importantly it makes development easier in some cases and for some developers. By no means is it the ultimate solution to all development problems.


The keys to a successful dev team are flexibility and consistency. There is no perfect end-all, be-all solution to software development, so let’s stop pretending that our own personal preferences will fit everybody else and start challenging dogmatic development.

Our Living Style Guide (Writing maintainable HTML/CSS)

There’s a lot of talk about scaling apps for growth. Scaling server architecture, writing code that will handle millions of requests etc. but I rarely hear about scaling HTML/CSS. By this I don’t mean performance, I mean maintainability. Let’s take a look at this fact: Facebook currently has 6498 color declarations! You don’t get there unless there wasn’t a plan for maintainable markup from the beginning. Since that sounds like a big ol’ mess, I’m dedicated to avoiding that problem at RJ.

The solution, quite simply, is to start forming a set of rules, nomenclature and workflow to form an architecture.

The Goals:

  • Create a worflow for creating and understanding new markup.
  • Give developers who aren’t experts in CSS, a logical and consistent way to markup HTML without having to ask a designer.
  • Impose markup standards within a growing organization.
  • Make it obvious when something strays from being consistent with an organization’s style, and publicly shame us into correcting it. It’s an experiment, and hopefully generates feedback.
  • Save time, and thus, save $ for beer.

The Strategy:

1. Break UI components into objects conceptually. (Buttons, notices, pickers, etc)

2. Address those components in a unified stylesheet: common.scss (We use Sass with Compass, and it’s pretty awesome).

3. Drop common.css into the app and play whack a mole with the style inconsistencies that crop up.

This part ended up being WAY easier than I anticipated. The reason being that there wasn’t much semantic markup. Nearly everything was a div or span with a class on it. I love semantic markup, but this was a case where a lack of it had an unintended positive outcome. This alone deserves its own post. The biggest issue actually ended up being typography since there were so many declarations. We’re still sorting this out.

4. Isolate more objects and add them to common.css until all commonly used objects reside in one place. I like to think of common.css eating the other style sheets.

5. Create a living style guide that outlines what each object looks like, its class structure, and its markup. It’s using the same common.css that’s deployed in our app, so if it works in the style guide, it will work in the app.

Take adding a notice for example. All the developer needs to do is go to the style guide, copy the HTML, add the classes they need, and paste into the app. They can even add classes that don’t exist yet, and have someone like me go back and add the CSS later.

scalable app design

The style guide was inspired by and is based on Kyle Neath’s KSS and more recently takes a play from Pea.rs by SimpleBits. Nomenclature and organization are inspired by Nicole Sullivan’s OOCSS and Jonathan Snook’s SMACSS.

Check it out, and let us know what you think.

Update: If you’ve got an opinion on this, maybe you’d like to work with us! Apply here.