Thick Client === Happy Clients
First some background on us. We are a technology company that builds web, mobile, desktop and embedded solutions for clients big and small. We have worked to get our startup clients from the incubator to seed-stage funding and have helped our large corporate clients like AEP communicate better by converting processes typically carried out in excel spreadsheet into rich web experiences (and on Internet Explorer 8, no less!).
When we first began our work with AEP, we had just been contracted to do some shadow IT for one of the company's business units. Our first question in our first meeting was, "We know you do a lot of .NET here, but would you mind if we gave Ruby on Rails a go?". It should have been a red flag that there were no internal IT representatives in the room, but we weren't about to look a gift horse in the mouth. The business unit signed off on our project and gave us the go-ahead to write a Rails application.
Automating Front-end Workflow
In particular, we have adopted Yeoman for scaffolding new applications, Grunt for automating tasks (minifying scripts, compiling SASS and Coffeescript, running static code analysis, unit and end to end tests, and even removing unused CSS rules) and Bower for managing dependencies. Scaffolding, previewing, testing and building a new AngularJS with Yeoman is as simple as:
npm install -g generator-angular # install generatoryo angular # scaffold out a AngularJS projectbower install angular-ui # install a dependency from Bowergrunt test # test your appgrunt serve # preview your appgrunt # build the application for deployment
At this point, if we wanted, we could begin test-driving the front-end with mock services while another developer begins writing Rails/PHP/.NET/Go/etc.
Build Artifacts in Version Control !== Best Practice
Our build workflow looked like this:
- After merging changes into
mastercheckout new branch called
grunt build(target is
./publicin the Rails root directory)
- Commit changes
- Push to remote
- Issue pull request to
- Merge changes
I get worn out just reading that list.
If the threat of extra work isn't compelling enough to convince you that this isn't the right way to be doing things, how about this: build artifacts simply do not belong in source control. If only Heroku could recognize our Rails application and also know to fetch our front-end dependencies and run our Grunt tasks.
Automating Multiple Builds on Heroku
I'd like to say that I got fed up with the above build process and went searching for a better way, but that would be--at best--a half-truth. A few months ago, we got an email from an Advocate at Heroku named Christopher Lauer asking if he could chat with us about ways to improve our Heroku experience. I had no idea they did this kind of thing and was thrilled to get a forum to ask questions about the platform.
This is where Christopher turned me on to the idea of using multiple build packs. I had no idea what buildpacks were, so I started poking around. From the documentation:
git push heroku, Heroku’s slug compiler prepares your code for execution by the Heroku dyno manager. At the heart of the slug compiler is a collection of scripts called a buildpack.
Ok, so buildpacks are the steps Heroku uses to build my application once it recognizes that I'm using Ruby/Node/etc. So how do I tell Heroku that it should stop trying to recognize my environment and instead let me call the shots? It's a simple environment variable. So, if I want Heroku to look for a Ruby/Rails application, I would configure it to use the ruby buildpack:
heroku config:set BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-ruby
If, instead, I would like it to run a package.json and install dependencies like Compass and Grunt, I would configure it like this:
heroku config:set BUILDPACK_URL=https://github.com/stephanmelzer/heroku-buildpack-nodejs-grunt-compass.git
But this doesn't yet solve my problem. Enter, multi build packs.
heroku config:add BUILDPACK_URL=https://github.com/ddollar/heroku-buildpack-multi.gitcat .buildpackshttps://github.com/stephanmelzer/heroku-buildpack-nodejs-grunt-compass.githttps://github.com/heroku/heroku-buildpack-ruby.git
Now, when you push to Heroku, the service will first look for a package.json and Gruntfile in the root (I point the Gruntfile to the Gruntfile in my subdirectory with grunt-hub) before bundling and spinning up a Rails server.
$ git rm -rf /public
Removing my build artifacts from Github was a triumphant moment. Concurrently developing for the front and back-end feels humane.
I'm sure that there were likely some holes in this document and more than a few logical jumps. I'll keep an eye on the comments and plan on linking a few resources below.