When we think about usability, it’s often from the perspective of an end user trying to use a finished UI. However, if you’re writing tools or libraries, you should keep in mind that the devs who’ll be using them are users, too! It’s worth taking the time to polish the experience of working with your code, whether it’s going to be used by a handful of your coworkers or the teeming masses of your company’s user base.
In this article we’re going to focus specifically on the initial integration process. In some cases, this may be as simple as adding a line to your gemfile, but often it can be much more complicated, especially for in-house software packages.
To tackle this problem, we’ll use an iterative approach to identify and simplify the rough spots in your integration process. In addition, we’ll cover some specific techniques that can be used to ease those pain points and smooth things out. In my examples, I’ll discuss integrating a new gem in to a Rails app, but the underlying ideas should be fairly universal.
Introducing the T-Shirt Service
The basic approach I’m going to outline involves integrating your gem with a very simple dummy application. At New Relic, we call this the “T-Shirt Service”. It’s just a vanilla Rails app with a controller, a model, and a few tests — you could easily set up something similar in about five minutes. Since we’re in Portland, ours serves an ASCII T-shirt with a bird on it.
Make sure that you include all of the features you expect the real client applications to have. For example, if you use Cucumber in your company, set up a couple Cucumber tests. Once you have the very basics built out (and nothing else!), create a new git repo with the basic app in it. You are now ready to start integrating!
Six Steps To Awesome Integration
Clone the repo.
This step is pretty straightforward — you need a local checkout of the T-Shirt Service if you don’t already have one.
Make a branch for whatever project you want to try to integrate.
Again, pretty straightforward. We want a consistent starting point, and a place to save the final integration steps. If there are any specific features you think might present challenges for installing your gem, make sure to add them to the T-Shirt Service and commit those changes on the branch. For example, you might be releasing a new background job library to replace an older one. In that case, you might want to add a task or two using the old library, so you can test the process of migrating to the new hotness.
Integrate your project on the branch.
Now you go through the entire process of installing your project into a new client from scratch. Ideally, pretend that you’re a junior developer who doesn’t know anything about your gem except that they need it. One shortcut that may save you some time is to use the ‘path’ argument in your Gemfile when installing your new gem (e.g.,
gem “your_awesome_gem”, path: “../your_awesome_gem”); this can help you avoid the full round trip of releasing a new gem version for each change you end up making. Don’t commit anything in the T-shirt app while you’re going through the install process! If you are tempted to do so, this should tell you that that step needs to be easier.
When you come to a spot that is slow, annoying, or requires special knowledge, make it better.
This is the meat of the process. We’ll go through some specific techniques for improving things later, but you want to look for any kind of cutting and pasting, copying things around, editing files, setting configurations… basically any step at all that goes beyond “put this line in your Gemfile.” Then, you figure out a way to eliminate that step, or at least make it easier. Make that change in your gem and commit it.
Blow away all your changes and start over at step 3.
Run good old ‘
git reset --hard HEAD‘ in the T-shirt app and install again from scratch! Confirm that the new, streamlined process still works, and that it is in fact easier.
When a well-trained artichoke could install your gem in under 5 minutes, you’re done.
Good job — that sounds like a pretty easy install process!
Specific Ways To Make Things Better
Document the install process. This may seem obvious, but your users will need to know the steps to install your project! The README is a good place for this, especially if you use github.
Write an install script. If you expect most clients to be Rails apps, Rails generators can be a very powerful option for performing complicated installation tasks such as adding routes, creating or modifying files, etc. For example, instead of requiring a client app to create a particular controller, an install generator might include a template for the required file and copy it into place automatically (potentially running it through ERB first if any customization is required). You can find more detailed information about Rails generators online here.
More broadly, rake tasks or even plain ruby scripts can accomplish many of the same goals. It can also be helpful to include some text output describing any remaining manual steps that still need to be taken, even if you documented them in your README (or elsewhere).
Create a config file, and fill it in with reasonable defaults. This can be a .yml file or a plain Ruby file that sets some variables. Alternatively, there are several gems you can use to track configurations if your needs are more complex. The key here is “reasonable defaults,” so that most of your users won’t need to make any changes.
Provide mixins that expose your functionality where it will be needed. For example, many authentication libraries include a controller mixin that provides commonly needed methods like ‘
logged_in?’, etc. Try to figure out the simplest way for your users to get at the functionality your gem is providing without having to write any intermediate code of their own (even if not every client will need it).
Provide default implementations that can be overridden. Sometimes, providing ways for users to override internal bits of logic in your code can allow you to make simplifying assumptions and streamline the installation process for the most common cases.
For example, perhaps you need to keep track of some state, but you don’t always know what data stores will be available in a given client — some might be web apps that want to use the Rack session, others might be scripts running out of cron jobs with access to Redis, etc. By providing an explicit hook to override that functionality (either by overriding a method from a mixin or explicitly passing a lambda to a configuration variable), you can write a default implementation that handles the most common case, but still allow clients with special needs to use your gem.
Don’t forget test support! Remember that clients are going to be writing tests that rely on the functionality provided by your code. Give them an easy way to stub or fake out that functionality! Consider creating an explicit test mode that can be enabled, and make sure to include a couple tests in your integration runs.
Using these techniques, you can craft an installation experience that is smooth and painless, even if your code requires complicated setup or might be installed in diverse environments. Taking the time to polish your installation process now will save your company time and money in the long run. It will prevent bugs that can arise from misconfigurations, and it will make your users a lot happier to use your code.