Choosing Appropriate Tools for the Job

Yuji Yokoo
October 21, 2015

Choosing Appropriate Tools for the Job

In software development, it is easy for developers to sometimes choose tools that are not perfect for the job. This could be because maybe it made sense at that particular time, we were facing a tight deadline, we were not familiar enough with our tools, or a plethora of particular circumstances. It is always beneficial to consider alternatives.

Recently, we have encountered a few situations where using alternatives to our “default” tools improved our project code. The tools that were already in place did the job sufficiently well, but replacing them with simpler ones made more sense in these situations.

Feature Specs and View Specs

We had a situation where feature specs were overused, and we replaced them with view specs successfully. In this particular project, we used rspec for each module, and feature specs for integrating the whole lot. In one case, all we were checking was that the message intended for admin users was displayed for admin users, and not regular users. We were checking the page contents in both cases here, but feature specs actually used real web browsers in a “headless” manner, while view specs simply rendered templates. The code was like this: erb <% if current_user.admin? %> <%= @admin_message %> <% end %> We had feature specs like these: ruby feature 'user notifications' do scenario 'admin message is not shown' do login_as user ... expect(page).not_to have_content(latest_admin_message) end end feature 'admin notifications' do scenario 'admin message is shown' do login_as admin ... expect(page).to have_content(latest_admin_message) end end We replaced these with view specs: ruby context 'user notifications' do before do allow(user).to receive(:admin?) { false } end it 'does not show admin message' render expect(rendered).not_to have_content(latest_admin_message) end end context 'admin notifications' do before do allow(user).to receive(:admin?) { true } end it 'shows admin message' do render expect(rendered).to have_content(latest_admin_message) end end Unit tests cannot always replace integration tests, but in this case, this was sufficient for us. In fact, this eliminated many other potential causes of failure that we were not interested in, and the view specs ran much faster. Another important benefit of this was that it has made the specs more focused and clearer about our intentions.

VCR and Webmock

In a separate situation, the VCR gem was used to capture the remote API response where direct use of Webmock would simplify the code. If you don’t know what VCR does, it lets us “record” web responses to requests, and play it back later so we don’t have to access the real remote server every time. It is extremely valuable in testing since it removes the dependency on external services. While it is no replacement for manual, real-world tests, it lets us simulate very closely. It saves a lot of information in yaml files. This is not all, but it looks like this: yaml --- http_interactions: - request: method: get uri: body: encoding: US-ASCII string: '' headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: - "*/*" User-Agent: - Ruby response: status: code: 200 message: OK and so on.

The problem we had, was that we had tests that used recorded and then modified response files. Let’s say we want to test our code against errors, like 500, or 200 with broken JSON contents. These error scenarios should be tested but they are not easy to actually record, since we usually don’t (or at least we hope not to) receive a lot of malformed responses or internal server errors from remote services. In our test cases, we had modified response files (or “vcr cassettes”) to suit our needs. Instead of modifying saved response files, we simplified this by using Webmock’s stub_request: ruby stub_request(:any, remote_api_address).to_return { { status: 500, body: '' } } or: ruby stub_request(:any, remote_api_address).to_return { { status: 200, body: 'Malformed JSON!' } } This made our specs a lot easier to read since we did not need all the extra details VCR would save for us in these cases. One of the major advantages of VCR is the ability to update the input files by recording the updated responses but, we were not interested in that here. This rewrite made it more focused and easier to understand the intentions.

Other Gems

Similarly, we have come across situations where it made sense to replace a decorator gem with our own, simpler decorators that did less. There were also some other gems we have removed in the past, like the one that helped us with memoization. These gems worked well, and they were very good for what they were intended for. However, it does not mean we should use third-party tools just because their functionalities cover what we need.


Every tool has its advantages and disadvantages. Every time we add a third-party tool, we are accepting all the disadvantages that come with it. Keeping the code simple and free of unnecessary complexity makes it easier to read and maintain. We should always try to choose the simplest tools that do the job. It’ll help us keep things simpler, because simpler code is always easier to work with.