In this three part series, I’m going to cover the evolution of Stickler CI in the past 2 years from the initial prototype to the present day. This specific article will cover how I built the initial prototype and then added paid plans.
My side project
Stickler CI is a software as a service application that automates the more tedious parts of code review; enforcing style and checking lint errors. It integrates with your GitHub repositories and checks each pull request for code style errors. Stickler CI will leave review comments when someone on your team makes a style error. This saves developer time as feedback on style errors is handled automatically, and your team doesn’t have to read through build reports to find whitespace errors.
I was inspired to build Stickler CI after spending a lot of time giving feedback on style errors, as our team was refactoring older parts of our applications at FreshBooks. I thought there must be a better way to give feedback on code style & formatting errors discovered in code review.
The initial prototype
The frontend application is built with CakePHP, while the backend portion of the application that performs code review is built with Python. Persistent data is stored in MySQL and RabbitMQ is used for the review job queue. After the initial stealth mode development phase over the course of a few months I had an initial prototype ready. The first release was deployed to a single 1GB DigitalOcean droplet. I started off with only a single user (me). The architecture looked like this:
All the components of the application lived in a single box. My goal was to get the prototype in front of people with the smallest amount of expense and complexity. While the single server approach has limitations around capacity and scalability I wasn’t concerned about those issues at this time; I had only one user. Before making the deployment/infrastructure more resilient or scalable I wanted to see if anyone would use, and pay for this product.
The simple infrastructure enabled me to use the exact same Ansible playbooks to manage both my local and production environments. It also made it easy to scale ‘up’ as I could easily get a bigger instance from DigitalOcean. At this point, Stickler CI was only able to connect to public repositories as I wanted to charge for private repositories if the prototype proved successful.
I enabled Stickler CI on some of my open source projects to help contributors with their own lint errors and hopefully drum up some interest in the product. From my open source projects I attracted the very first users, a couple of users reached out wanting to use Stickler CI on their private repositories. This was enough of a positive signal to compel me to build private repository support and paid plans.
Adding paid plans
I chose to use a very simple subscription based business model that scales based on the number of private repositories a person wanted to enable. I chose private repositories as a differentiator because it was a similar billing model to Stickler CI’s competitors.
I used Stripe to build subscriptions. Stripe is a great product with a fantastic API and great documentation. Stripe handles card tokenization, plan listings, and subscription billing. I leveraged the metadata fields on plans to store the number of private repositories each plan provides. This removed the need to maintain a mapping of products and their repository limits in my application code.
During an upgrade I can read the repositories metadata field and know how many private repositories to grant the user.
At this stage there were ~50 users and Stickler was processing about ~150 jobs a day. The addition of Stripe didn’t fundamentally change the architecture, but I was starting to get worried about some of the corners I cut to get the product out the door. I’ll detail those shortcuts and how I fixed them in my next article.