Category Archives: Programming

How to get a team to get behind an engineering project

A client of mine recently asked: “What’s the best way to get a team behind a future refactor/architecture goal that hasn’t happened yet?”

This is a great question. Before I answer how to do this successfully, let me point out three certain ways to fail in this endeavor. Then I’ll share the key to giving an idea its best shot at success.

Three ways to fail to get a team behind an engineering project

The effort fails because the project was a genuinely bad idea

I mention this just to get the obvious out of the way: bad ideas often fail and they of course ought to fail. Even though this scenario isn’t necessarily a happy one, there’s nothing unjust about it.

The effort fails because it was “pearls before swine”

Sometimes you have a good idea but, for whatever reason, the team you’re in or the leadership you’re under is made up of the kind of people who are behind the times or (to be blunt) just plain not very smart.

In 2013 I worked somewhere where I tried to help implement continuous integration, but my boss was dead-set against it. There’s nothing I could have done to have been successful in this case because my boss just wasn’t the kind of person who “gets it”. The only “solution” to this problem was to go work somewhere else.

This scenario is regrettable but it’s futile to try to fight against it.

The effort fails because, although the idea was a good one, the methodology of persuasion was bad

Of the three ways to fail at getting a team to get behind an engineering effort, this one is the most tragic. I had a good idea, I had a receptive audience, but I bungled the pitch and so my idea got killed.

The key to making any project idea maximally enticing

Think about how interested you tend to be in implementing other people’s ideas. Then think about how interested you are in implementing your OWN ideas. If you’re like most people, you’re of course much more interested in your own ideas than other people’s.

So the key to making an idea enticing is: get your teammates (or bosses or subordinates) to believe that the idea is theirs.

This is easier said than done. I’ll describe two ways of accomplishing this.

How to get your teammates to believe that your ideas are their ideas

I know of two ways to get someone to think that an idea of mine is actually an idea of theirs: 1) use some sort of psychological manipulation to trick or 2) find an idea of theirs that actually, legitimately matches an idea of mine.

The latter method is of course going to be the more successful.

Here’s an example. Let’s say I want to try to get the team to increase test coverage on our application. One way I could go about this is by saying, “Hey guys! We need to increase our test coverage! We need to start this project ASAP!” This method isn’t likely to be very successful. Any attempt to “sell” an idea tends to be met with an equal resistance.

A better way to approach it would be to start by saying, “Hey guys, how do you feel about the quality of our codebase in general?” Then I would listen to my teammates’ responses and make sure they felt heard. (It’s easier to influence someone else if you first let them influence you.) Then I would ask, “How do you feel about our test coverage specifically?” If the whole team unanimously agreed that our test coverage was fine, I’d probably stop right there. It’s impossible to get someone to spend time fixing a problem they don’t believe exists.

But if my teammates expressed some level of dissatisfaction with our current test coverage, I would ask them to elaborate. I would take notes on what they said. I would ask if these problems strike them as worth fixing. Then I’d ask them if they’d be open to coming up with some goals together around test coverage and a plan to achieve those goals. People are much more supportive of goals and plans they were involved in creating than goals and plans someone else came up with.

Key takaways

Uncover existing desires. If you have a desire to accomplish X, ask probing questions of your teammates (or bosses or subordinates) to see if they also desire X. Position the project not as an endeavor to realize your desires, but to realize their desires.

Involve the team in the roadmapping. The team will be motivated to help accomplish the goal in proportion to how involved they were in developing the goal.

Mystified by RSpec’s DSL? Some parentheses can add clarity

Many developers have a hard time wrapping their head around RSpec’s DSL syntax. To me for a long time, RSpec’s syntax was a mystery.

Then one day I realized that RSpec’s syntax is just methods and blocks. There’s not some crazy ass metaprogramming going on that’s beyond my abilities to comprehend. Everything in RSpec I’ve ever used can be (as far as I can tell) boiled down to methods and blocks.

I’ll give a concrete example to aid understanding.

Below is an RSpec test I pulled from a project of mine, Mississippi.com, which I’ve live-coded in my Sausage Factory videos.

require 'rails_helper'

RSpec.describe Order, type: :model do
  subject { build(:order) }

  describe 'validations' do
    it { should validate_presence_of(:customer) }
  end

  describe '#total_cents' do
    it 'returns the total amount for the order' do
      order = create(
        :order,
        line_items: [
          create(:line_item, total_amount_cents: 5000),
          create(:line_item, total_amount_cents: 2500)
        ]
      )

      expect(order.total_cents).to eq(7500)
    end
  end
end

Now here’s the same test with all the optional parentheses left in instead of out. What might not have been obvious to you before, but is made clear by the addition of parentheses, is that describe and it are both just methods.

Like any Ruby method, it and describe are able to accept a block, which of course they always do. (If you don’t have a super firm grasp on blocks yet, I might suggest reading up on them and then writing some of your own methods which take blocks. I went through this exercise myself recently and found it illuminating.)

In addition to putting in all optional parentheses, I changed every block from brace syntax to do/end syntax. I think this makes it more clear when we’re dealing with a block versus a hash.

require('rails_helper')

RSpec.describe(Order, { type: :model }) do
  subject do
    build(:order)
  end

  describe('validations') do
    it do
      should(validate_presence_of(:customer))
    end
  end

  describe('#total_cents') do
    it('returns the total amount for the order') do
      order = create(
        :order,
        line_items: [
          create(:line_item, total_amount_cents: 5000),
          create(:line_item, total_amount_cents: 2500)
        ]
      )

      expect(order.total_cents).to(eq(7500))
    end
  end
end

The latter method is runnable just like the former version (I checked!) because although I changed the syntax, I haven’t changed any of the functionality.

I hope seeing these two different versions of the same test is as eye-opening for you as it was for me.

Lastly, to be clear, I’m not suggesting that you permanently leave extra parentheses in your RSpec tests. I’m only suggesting that you temporarily add parenthesis as a learning exercise.

How to test Ruby methods that involve puts or gets

I recently saw a post on Reddit where the OP asked how to test a method which involved puts and gets. The example the OP posted looked like the following (which I’ve edited very slightly for clarity):

class Example
  def ask_for_number
    puts "Input an integer 5 or above"
    loop do
      input = gets.to_i
      return true if input >= 5
      puts "Invalid. Try again:"
    end
  end
end

What makes the ask_for_number method challenging to test is a dependency. Most methods can be tested by saying, “When I pass in argument X, I expect return value Y.” This one isn’t so straightforward though. This is more like “When the user sees output X and then enters value V, expect subsequent output O.”

Instead of accepting arguments, this method gets its value from user input. And instead of necessarily returning a value, this method sometimes simply outputs more text.

How can we give this method the values it needs, and how can we observe the way the method behaves when we give it these values?

A solution using dependency injection (thanks to Myron Marston)

Originally, I had written my own solution to this problem, but then Myron Marson, co-author of Effective Testing with RSpec 3, supplied an answer of his own which was a lot better than mine. Here it is.

I comment on this solution some more below, but you can see in the initialize method that the input/output dependencies are being injected into the class. By default, we use $stdin/$stdout, and under test, we use something else for easier testability.

class Example
  def initialize(input: $stdin, output: $stdout)
    @input = input
    @output = output
  end

  def ask_for_number
    @output.puts "Input an integer 5 or above"
    loop do
      input = @input.gets.to_i
      return true if input >= 5
      @output.puts "Invalid. Try again:"
    end
  end
end

require 'stringio'

RSpec.describe Example do
  context 'with input greater than 5' do
    it 'asks for input only once' do
      output = ask_for_number_with_input(6)

      expect(output).to eq "Input an integer 5 or above\n"
    end
  end

  context 'with input equal to 5' do
    it 'asks for input only once' do
      output = ask_for_number_with_input(5)

      expect(output).to eq "Input an integer 5 or above\n"
    end
  end

  context 'with input less than 5' do
    it 'asks repeatedly, until a number 5 or greater is provided' do
      output = ask_for_number_with_input(2, 3, 6)

      expect(output).to eq <<~OUTPUT
        Input an integer 5 or above
        Invalid. Try again:
        Invalid. Try again:
      OUTPUT
    end
  end

  def ask_for_number_with_input(*input_numbers)
    input = StringIO.new(input_numbers.join("\n") + "\n")
    output = StringIO.new

    example = Example.new(input: input, output: output)
    expect(example.ask_for_number).to be true

    output.string
  end
end

Under test, an instance of StringIO can be used instead of $stdout, thus making the messages sent to @output visible and testable. That’s how we can “see inside” the Example class and test what at first glance appears to be a difficult-to-test piece of code.

Examples of pointless types of RSpec tests

A reader of mine recently shared with me a GitHub gist called rspec_model_testing_template.rb. He also said to me, “I would like your opinion on the value of the different tests that are specified in the gist. Which ones are necessary and which ones aren’t?”

In this post I’d like to point out which types are RSpec tests I think are pointless to write.

Testing the presence of associations

Here are some examples from the above gist of association tests:

it { expect(profile).to belong_to(:user) }
it { expect(user).to have_one(:profile }
it { expect(classroom).to have_many(:students) }
it { expect(gallery).to accept_nested_attributes_for(:paintings) }

Unless I’m crazy, these sorts of tests don’t actually do anything. A test like it { expect(classroom).to have_many(:students) } verifies that classroom.rb contains the code has_many :students but that’s all the value the test provides.

I’ve heard these tests referred to as “tautological tests”. I had to look up that word when I first heard it, so here’s a definition for your convenience: “Tautology is useless restatement, or saying the same thing twice using different words.” That’s exactly what these tests are: a useless restatement.

What does it mean for a classroom to have many students and what sorts of capabilities does the existence of that association give us? Whatever those capabilities are is what we should be testing. For example, maybe we want to calculate the average student GPA per classroom. The ability to do classroom.average_student_gpa would be a valuable thing to write a test for.

If we test the behaviors that has_many :students enables, then we don’t have to directly test the :students association at all because we’re already indirectly testing the association by testing the behaviors that depend on it. For example, our test for the classroom.average_student_gpa method would break if the line has_many :students were taken away.

How do you know the difference between a tautological test and a genuinely valuable test? Here’s an analogy. What would you do if you wanted to test the brakes on your bike? Would you visually verify that your bike has brake components attached to it, and therefore logically conclude that your bike has working brakes? No, because that conclusion is logically invalid. The way to test your brakes is to actually try to use your brakes to make your bike stop. In other words, you wouldn’t test for the presence of brakes, you would test for the capability or that your brakes enable.

Testing that a model responds to certain methods

it { expect(factory_instance).to respond_to(:public_method_name) }

There’s negligible value in simply testing that a model responds to a method. Better to test that that method does the right thing.

Testing for the presence of callbacks

it { expect(user).to callback(:calculate_some_metrics).after(:save) }
it { expect(user).to callback(:track_new_user_signup).after(:create) }

Don’t verify that the callback got called. Verify that you got the result you expected the callback to produce.

Testing for database columns and indexes

it { expect(user).to have_db_column(:political_stance).of_type(:string).with_options(default: 'undecided', null: false)
it { expect(user).to have_db_index(:email).unique(:true)

I had actually never seen this before and didn’t know you could do it. I find it pointless for the same exact reasons that testing associations is pointless. Don’t test that the database has a particular column, test that the feature that uses that column works. Don’t test that the database has a uniqueness index, test that you get a graceful error message if you try to create a duplicate.

For better tests, test behavior, not implementation

In order to write tests that are actually valuable, test behavior, not implementation. For example, rather than testing an association, test the behavior that the association enables.

If you’re new to testing and would like some better guidance on how to write valuable tests, I might suggest my model spec tutorial, my RSpec/Capybara hello world, or my book, The Complete Guide to Rails Testing.

How I test JavaScript-heavy Rails applications

A common question I get is how to test JavaScript in Rails applications. My approach is almost radically simple and unsophisticated.

My Rails + JavaScript testing approach

I think of the fact that the application uses JavaScript like an inconsequential and irrelevant implementation detail. I test JavaScript-heavy applications using just RSpec + Capybara integration tests, the same exact way I’d test an application that has very little JavaScript or no JavaScript at all.

I don’t really have anything more to say about it since I literally don’t do anything different from my regular RSpec + Capybara tests.

Single-page applications

What about single-page applications? I still use the same approach. When I used to build Angular + Rails SPAs, I would add a before(:all) RSpec hook that would kick off a build of my Angular application before the test suite ran. After that point my RSpec + Capybara tests could interact with my SPA just as easily as if the application were a “traditional” Rails application.

I’ve tried testing single-page applications using tools like Protractor or Cypress and I don’t like it. It’s awkward and cumbersome to try to drive the Rails app from that end. How do you spin up test data? In my experience, it’s very tedious. Much easier to drive testing from the Rails end and treat the client-side JavaScript application as an implementation detail.

Side note/rant: despite the popularity of single-page applications, “traditional” Rails applications are 100% fine. Using Rails with React/Vue/Angular/etc. isn’t “modern” and using Rails without any of those isn’t “outdated”. For most regular old boring business applications, Rails by itself without a front-end framework is not only a sufficient approach but a superior approach to an SPA because the complexity of development with plain Rails and only “JavaScript sprinkles” tends to be far lower than Rails with a JavaScript framework.

Testing JavaScript directly

Despite my typical approach of treating JavaScript as a detail, there are times when I want to have a little tighter control and test my JavaScript directly. In those cases I use Jasmine to test my JavaScript.

But it’s my goal to use such little JavaScript that I never get above that threshold of complexity where I feel the need to test my JavaScript directly with Jasmine. I’ve found that if I really try, I can get away with very little JavaScript in most applications without sacrificing any UI richness.

A repeatable, step-by-step process for writing Rails integration tests with Capybara

Many Rails developers who are new to writing tests struggle with the question of what to write tests for and how.

I’m about to share with you a repeatable formula that you can use to write an integration test for almost any Rails feature. It’s nothing particularly profound, it won’t result in 100% test coverage, and it won’t work in all cases, but it will certainly get you started if you’re stuck.

The three integration test cases I write for any Rails CRUD feature

Most features in most Rails applications are, for better or worse, some slight variation on CRUD operations. Here are the tests I almost always write for every CRUD feature.

  1. Creating a record with valid inputs
  2. Trying to create a record with invalid inputs
  3. Updating a record

Let me go into detail on each one of these. For each test case, let’s imagine we’re working with a resource called Location.

Creating a record with valid inputs

This test case will involve the following steps:

  1. If necessary, sign a user in (using login_as(create(:user)) as I describe in this post)
  2. Visit the “new” route for the resource (e.g. visit new_location_path)
  3. Fill out the form fields using fill_in, select, etc.
  4. Click the “submit” button
  5. Expect that the page has whatever content the resource’s index page has, e.g. expect(page).to have_content('Locations')

Here’s what that test might look like:

require 'rails_helper'

RSpec.describe 'Creating a location', type: :system do
  before do
    login_as(create(:user))
    create(:state, name: 'Michigan')
    visit new_location_path
  end

  scenario 'valid inputs' do
    fill_in 'Name', with: "Jason's House"
    fill_in 'Line 1', with: '69420 Cool Ave'
    fill_in 'City', with: 'Sand Lake'
    select 'Michigan', from: 'State'
    fill_in 'Zip code', with: '49343'
    click_on 'Save Location'

    expect(page).to have_content('Locations')
  end
end

Trying to create a record with invalid inputs

This test is pretty simple because all I have to do is not fill out the form. So the steps are:

  1. If necessary, sign a user in (using login_as(create(:user))
  2. Visit the “new” route for the resource (e.g. visit new_location_path)
  3. Click the “submit” button
  4. Expect that the page shows an error (e.g. expect(page).to have_content("Name can't be blank"))

Here’s what a test of this type might look like.

require 'rails_helper'

RSpec.describe 'Creating a location', type: :system do
  before do
    login_as(create(:user))
    create(:state, name: 'Michigan')
    visit new_location_path
  end

  scenario 'invalid inputs' do
    click_on 'Save Location'
    expect(page).to have_content("Name can't be blank")
  end
end

Updating a record

The steps for this one go:

  1. If necessary, sign a user in (using login_as(create(:user))
  2. Create an instance of the resource I’m testing (e.g. location = create(:location))
  3. Visit the “edit” route for the resource (e.g. visit edit_location_path(location))
  4. Fill in just one field with a different value
  5. Click the “submit” button
  6. Expect that the page has whatever content the resource’s index page has, e.g. expect(page).to have_content('Locations')
require 'rails_helper'

RSpec.describe 'Updating a location', type: :system do
  before do
    login_as(create(:user))
    location = create(:location)
    visit edit_location_path(location)
  end

  scenario 'valid inputs' do
    fill_in 'Name', with: "Jason's Filthy Shack"
    click_on 'Save Location'
    expect(page).to have_content('Locations')
  end
end

“But is that enough? Don’t I need more?”

No, this is not “enough”, and yes, you do need more…eventually. What I’m hoping to share with you here is a base that you can start with if you’re totally lost. Writing halfway-good tests is better than writing no tests, and even writing crappy tests is better than writing no tests. Once you get some practice following the formula above, you can expand on that formula to get a better level of test coverage.

Where to go next

If you get some practice with writing tests like the above and you want to go further, you might like my RSpec/Capybara integration test tutorial.

The difference between RSpec, Capybara and Cucumber

If you’re new to Rails testing you’ve probably come across the terms RSpec, Capybara and Cucumber.

All three are testing tools. What are they for? Do you need all of them? Here are some answers.

RSpec

RSpec is a testing framework. It’s what allows you to write and run your tests.

An analogous tool would be MiniTest. In my experience, most commercial Rails projects use RSpec and most open-source Ruby projects use MiniTest. At any Rails job you’re more likely to be using RSpec than MiniTest. (I’m not sure why this is the way it is.)

Capybara

Some Rails tests operate at a “low level”, meaning no browser interaction is involved. Other “high level” tests do actually spin up a browser and click links, fill out form fields, etc.

Low-level tests can be executed with just RSpec and nothing more. But for tests that use the browser, something more is needed.

This is where Capybara comes into the picture. Capybara provides helper methods like fill_in to fill in a form field, click_on to click a button, etc.

Please note that Capybara does NOT have to be used in conjunction with Cucumber. It’s completely possible to write integration tests in Rails with just RSpec and Capybara.

Cucumber

Cucumber is a tool for writing test cases in something close to English. Here’s an example from Wikipedia:

Scenario: Eric wants to withdraw money from his bank account at an ATM
    Given Eric has a valid Credit or Debit card
    And his account balance is $100
    When he inserts his card
    And withdraws $45
    Then the ATM should return $45
    And his account balance is $55

Cucumber can be connected with RSpec and Capybara and used to write integration tests.

My personal take on Cucumber is that while the English-like syntax might appear clearer at first glance, it’s actually less clear than bare RSpec/Capybara syntax. (Would a Ruby class be more understandable if it were English instead of Ruby?)

Cucumber adds both a layer of mental overhead and a layer of maintenance overhead on top of the RSpec + Capybara combination. I always try as hard as I can to try to steer new testers away from Cucumber.

“So, what should I use to test my Rails apps?”

My advice is to use the combination of RSpec + Capybara and forget about Cucumber. What if you don’t know where to start with writing RSpec/Capybara tests? If that’s the case, you might like to check out my guide to RSpec + Capybara testing, which includes a tutorial.

Stuck on a programming problem? These tactics will get you unstuck most of the time

Being stuck is possibly the worst state to be in when programming. And the longer you allow yourself to stay stuck, the harder it is to finally get unstuck. That’s why I find it important to try never to allow myself to get stuck. And in fact, I very rarely do get stuck for any meaningful length of time. Here are my favorite tactics for getting unstuck.

Articulate the problem to yourself

I teach corporate training classes. Often, my students get stuck. When they’re stuck, it’s usually because the problem they’re trying to solve is ill-defined. It’s not possible to solve a problem or achieve a goal when you don’t even know what the problem or goal is.

So when you get stuck, a good first question to ask is: do I even know exactly what the problem is that I’m trying to solve? I recommend going so far as to write it down.

If it’s hard to articulate your goal, there’s a good chance your goal is too large to be tractable. Large goals can’t be worked on directly. For example, if you say your goal is “get to the moon and back”, that goal is too big. What you have to articulate instead are things like figure out how to get into space, figure out how to land a spaceship on the moon, figure out how to build a spacesuit that lets people hang out in space for a while, etc.

Just try something

Far too often I see programmers stare at their code and reason about what would happen if they changed it in such-and-such a way. They run intricate thought experiments so they can, presumably, fix all the code in one go and arrive at the complete solution the very next time they try to run the program. Nine times out of ten, they make the change that they think will solve the problem and then discover they’re wrong.

A better way to move forward is to just try something. The cost of trying is very low compared to the cost of reasoning. Also, trying has the added bonus of supplying empirical data instead of untested hypotheses.

Articulate the problem to someone else

“Rubber duck debugging” is the famous tactic of explaining your problem to a rubber duck on your desk. The phenomenon is that even though the duck is incapable of supplying any advice, the very act of explaining the problem out loud leads you to realize the solution to the problem.

Ironically, most of the “rubber duck” debugging I’ve done in my career has involved explaining my issue to a sentient human being. The result is often the classic scenario where you spend ten minutes explaining the details of a problem to a co-worker, realize the way forward, and then thank your co-worker for his or her help, all without your co-worker uttering a word.

The explanation doesn’t have to be verbal in order for this tactic to work. Typing out the problem can be perfectly effective. This leads me to my next tactic.

Put up a forum question

In my experience many programmers seem to be hesitant to write their own forum questions. I’m not sure why this is, although I have some ideas. Maybe they feel like posting forum questions is for noobs. Maybe they think it will slow them down. Neither of these things are true, though. I’ve been programming for over 20 years and I still post Stack Overflow questions somewhat regularly. And rather than slowing me down, posting a forum question usually speeds me up, not least because the very act of typing out the forum question usually leads me to realize the answer on my own (and usually about 45 seconds after I post the question).

If you don’t know a good way, try a bad way

Theodore Roosevelt is credited with having said, “In any moment of decision, the best thing you can do is the right thing, the next best thing is the wrong thing, and the worst thing you can do is nothing.” I tend to agree.

Often I’m faced with a programming task that I don’t know how to complete in an elegant way. So, in these situations where I can’t think of a good solution, I do what I can, which is that I just move forward with a bad or stupid solution.

What often happens is that after I put my dumb solution in place, I’ll take a step back, look at what I’ve done, and a better solution will make itself fairly obvious to me. Or at least, a nice solution will be easier for me to think of at this stage than it would have been from the outset.

With programming and in general, people are much better at looking at a bad thing and saying how it could be made good than they are at coming up with a good thing out of thin air.

If you go down a bad path and find that you’ve completely painted yourself into a corner, it’s no big deal. If you’re using version control and using atomic commits, you can just revert back to your last good state. And you can start on a second attempt now that you’re a little older and wiser.

Study the hell out of the problem area

There’s a thought experiment I like to run in my head sometimes.

Let’s say I’m working on a Rails project and I can’t quite figure out how to get a certain form to behave how I want it to behave. I try several things, I check the docs, but I just can’t get it to work how I want.

In these situations I like to ask myself: “If I knew everything there was to know about this topic, would I be stuck?”

And the answer is of course not. The reason why I don’t understand how to get my form to work is exactly that—I don’t understand how to get my form to work. So what I need to do is set to work on gaining that understanding.

In an ideal world, a programmer could instantly pinpoint the exact piece of knowledge he or she is missing and then go find that piece of knowledge. And in fact, we’re very fortunate to live in a world where that’s possible. But it’s not possible 100% of the time.

In those cases I set down my code and say okay, universe, I guess this is how it’s gonna be. I’ll go to the relevant educational resource—in the case of this example perhaps the official Rails documentation on forms—and start reading it top to bottom. Again, if I knew everything about Rails forms then I wouldn’t have a problem, so I’ll set out of a journey to learn everything. Luckily, my level of understanding usually becomes sufficient to squash the problem long before I read everything there is to read about it.

Write some tests

One of the reasons I find tests to be a useful development or debugging tool is that tests help save me some mental juggling. When developing or debugging there are two jobs that need to be done: 1) write the code that makes the program behave as desired and 2) verify that the desired behavior is present.

If I write code without tests, I’m at risk of mentally mixing these two jobs and getting muddled. On the other hand, if I write a test for my desired behavior, I’m now free to completely forget about the verification step and focus fully on coding the solution. The fewer balls I have to mentally juggle, the faster I can work.

Take a break

One of my favorite Pink Floyd songs is “See Emily Play“. In the first chorus they sing “There is no other day / Let’s try it another way.” In a later chorus they sing “There is no other way / Let’s try it another day.” This is great debugging advice.

I often find that if I allow myself to forget about whatever problem I’m working on and go for a walk or something, my subconscious mind will set to work on the problem and surface deliver the solution later. Sometimes this happens 15 minutes later. Sometimes it doesn’t happen until a day or a week later. In any case, it’s often effective.

Work on something else

If all else fails, you can always work on something else. This might feel like a failure, but it’s only a failure if you decide to give up on the original problem permanently. Setting aside one goal in exchange for making progress on a different goal is infinitely better than allowing yourself to stay stuck.

Logging the user in before Capybara feature specs/system specs

Logging in

If you’re building an app that has user login functionality, you’ll at some point need to write some tests that log the user in before performing the test steps.

One approach I’ve seen is to have Capybara actually navigate to the sign in page, fill_in the email and password fields, and hit submit. This works but, if you’re using Devise, it’s a little more complicated than necessary. There’s a better way.

The Devise docs have a pretty good solution. Just add this to spec/rails_helper:

RSpec.configure do |config|
  config.include Warden::Test::Helpers
end

Then, any place you need to log in, you can do login_as(FactoryBot.create(:user)).

Log in before each test or log in once before all tests?

One of my readers, Justin K, wrote me with the following question:

If you use Capybara to do system tests, how do you handle authentication? Do you do the login step on each test or do you log in once and then just try and run all of your system level tests?

The answer is that I log in individually for each test.

The reason is that some of my tests require the user to be logged in, some of my tests require that the user is not logged in, and other tests require that some specific type of user (e.g. an admin user) is logged in.

I do often put my login_as call in a before block at the top of a test file to reduce duplication, but that doesn’t mean only once for that set of files. A common misconception is that a before block will give a performance benefit by only running the code in the before block once. This is not the case. before is shorthand for before(:each) and any code inside it will get run before each individual it block. I never use before(:all) inside of individual tests because I want each test case to be as isolated as possible.

All my best programming tips

What follows is a running list of all the programming tips I can think of that are worth sharing.

Many of these tips may seem so obvious they go without saying, but every single one of these tips is here because I’ve seen programmers fail to take advantage of these tips on multiple occasions, even very experienced programmers.

I’ve divided the tips into two sections: development tips and debugging tips. I’ve arranged them in no particular order.

Development tips

Articulate the goal

Before you sit down and start typing, clearly articulate in writing what it is you’re trying to accomplish. This will help you avoid going down random paths which will dilute your productivity. It will also help you work in small, complete units of work.

Break big jobs into small jobs

Some jobs seem so big and nebulous as to be intractable. For example, if I’m tasked with creating a schedule that contains recurring appointments, where do I start with that? It will probably make my job easier if I start with a smaller job, for example, thinking of how exactly to specify and codify a recurrence rule.

Keep everything working all the time

Refactoring is the act of improving the structure of a program without altering the behavior of the program. I’ve seen a lot of occasions where a programmer will attempt to perform a refactoring and fail not to alter the behavior of the code they’re trying to refactor. They end up having to throw away their work and start over fresh.

If in the course of refactoring I break or change some existing functionality, then I somehow have to find my way back to making the code behave as it originally did. This is hard.

Alternatively, if I take care never to let the code stray from its original behavior, I never have to worry about bringing back anything that got altered. This is relatively easy. I just have to work in very small units and regression-test at the end of each small change.

Continuously deliver

Continuous delivery is the practice of always having your program in a deployable state.

Let’s say I start working on a project on January 1st which has a launch date of July 1st. But then, unexpectedly, leadership comes to me on March 1st and says the launch date is now March 10th and we’re going to go live with whatever we have by then.

If I’ve been practicing continuous delivery, launching on March 10th is no big deal. At the end of each week (and the end of each day and perhaps even each hour) I’ve made sure that my application is 100% complete even if it might not be 100% finished.

Work on one thing at a time

It’s better to be all the way done with half your tasks than to be halfway done with all your tasks.

If I’m 100% done with 50% of 8 features, I can deploy four features. If I’m 50% done with 100% of 8 features, I can deploy zero features.

Also, open work costs mental bandwidth. Even if you believe it’s just as fast to jump between tasks as it is to focus on just one thing at a time, it’s more mentally expensive to have 5 balls in the air than just one.

Use automated tests

All code eventually gets tested, it’s just a question of when and how and by whom.

It’s much cheaper and faster for a bug to get caught by an automated test during development than for a feature to get sent to a QA person and then kicked back due to the bug.

Testing helps prevent the introduction of new bugs, helps prevent regressions, helps improve code design, helps enable refactoring, and helps aid the understandability of the codebase by serving as documentation.

Use clear names for things

When a variable name is unclear due to being a misnomer or when it’s unclear due to being abbreviated to obfuscation, it adds unnecessary mental friction.

Don’t abbreviate variable names, with the exception of universally-understood abbreviations (e.g. SSN or PIN) or hyperlocal temp variables (e.g. records.each do { |r| puts r }).

A good rule for naming things: call things what they are.

Don’t prematurely optimize

There’s a saying attributed to Kent Beck: “Make it work, then make it right, then make it fast.”

For the vast majority of the work I do, I never even need to get to the “make it fast” step. Unless you’re working on high-visibility features for a high-profile consumer app, performance bottlenecks often won’t emerge until weeks or months after the feature is initially deployed.

Only write a little code at a time

I have a saying that I repeat to myself: “Never underestimate your ability to screw stuff up.” I’ve often been amazed by how often a small change that will “definitely” work doesn’t work.

Only work in tiny increments. Test what you’re working on after each change, preferably using automated tests.

Use atomic commits

An atomic commit is a commit that’s only “about” one thing.

Atomic commits are valuable for at least two reasons. One, if you need to roll back a commit because it introduces a bug, you won’t have to also roll back other, unrelated (and perfectly good) code that was mixed into the same commit. Two, atomic commits make it easier to pinpoint the introduction of a bug than non-atomic commits.

A separate but related idea is to use small commits, which is just a specific application of “Only write a little code at a time”.

Don’t allow yourself to stay stuck

Being stuck is about the worst state to be in. Going down the wrong path is (counterintuitively) more productive than standing still. Going down the wrong path stirs up new thoughts and new information; standing still doesn’t.

If you can’t think of a good way to move forward, move forward in a bad way. You can always revert your work later, especially if you’re using small, frequent, atomic commits.

If you can’t think of a way to keep moving, try to precisely articulate what exactly it is you’re stuck on and why you’re stuck on it. Sometimes that very act helps you get unstuck.

If you think the reason you’re stuck is a lack of some necessary piece of knowledge, do some Google searches. Read some relevant blog posts and book passages. Post some forum questions. Ask some people in some Slack groups (and if you’re not in any technical Slack groups, join some). Go for a walk and then come back and try again. If all else fails, set the problem aside and work on something else instead. But whatever you do, don’t just sit there and think.

Debugging tips

Articulate the problem

If you can precisely articulate what the problem is, you’ve already gone a good ways toward the solution. Conversely, if you can’t articulate exactly what the problem is, your situation is almost hopeless.

Don’t fall into logical fallacies

One of the biggest dangers in debugging is the danger of tricking yourself into thinking you know something you don’t actually know.

If in the course of your investigation you uncover a rock solid truth, write it down on your list of things you know to be true.

If on the other hand you uncover something that’s seems to be true but you don’t have enough evidence to be 100% sure that it’s true, don’t write it down as a truth. It’s okay to write it down as a hypothesis, but if you confuse hypotheses with facts then you’re liable to get confused and waste some time.

Favor isolation over reason

I know of two ways to identify the cause of a bug. One is that I can study the code and perform experiments and investigations until I’ve pinpointed the line of code where the problem lies. The other method is that I can isolate the bug. The latter is usually many times quicker.

One of my favorite ways to isolate a bug is to use git bisect. Once I’ve found the commit that introduced the bug, it’s often pretty plain to see what part of the commit introduced the bug. If it’s not, I’ll usually negate the offending code by doing a git revert --no-commit the offending commit and then re-introduce the offending code tiny bit by tiny bit until I’ve found the culprit. This methodology almost never fails to work.

Favor googling over thinking

Mental energy is a precious resource. Don’t waste mental energy over problems other people have already solved, especially if solving that particular problem does little or nothing to enhance your skills.

If you run a command and get a cryptic error, don’t sit and squint at it. Copy and paste the error message into Google.

Thinking for yourself of course has its time and place. Understanding things deeply has its time and place too. Exercise good judgment over when you should pause and understand what you’re seeing versus when you should plow through the problem as effortlessly as possible so you can get on with your planned work.

Learn to formulate good search queries for error messages

Let’s say I run a command and get the following error: /Users/jasonswett/.rvm/gems/ruby-2.5.1/gems/rspec-core-3.8.0/lib/rspec/core/reporter.rb:229:in require': cannot load such file -- rspec/core/profiler (LoadError)

Which part of the error should I google?

If I paste the whole thing into Google, the /Users/jasonswett/.rvm/gems/ruby-2.5.1/gems/rspec-core-3.8.0, which is unique to my computer, my Ruby version and my rspec-core version, narrows my search so much that I don’t even get a single result.

At the other extreme, require': cannot load such file is far too broad. Lots of different error messages could contain that text.

The sensible part of the error message to copy/paste is require': cannot load such file -- rspec/core/profiler (LoadError). It’s specific enough to have hope of matching other similar problems, but not so specific that it won’t return any results.

Learn to formulate good search queries in general

I’m not sure how to articulate what makes a good search query but here’s my attempt: a query that’s as short as it can possibly be while still containing all the relevant search terms.

Don’t give too much credence to the error message

Whenever I get an error message, I pretty much never pay attention to what it says, at least not as the first step.

When I get an error message, I look at the line number and file. Then I go to that line number and look to see if there’s anything obviously wrong. Probably about 90% of the time there is.

For the other 10% of the time, I’ll go back to the error message and read it to see if it offers any clues. If it does, I’ll make a change to see if that fixes the problem. If the error is so cryptic that I don’t know what to do, I’ll google the error message.

Make the terminal window big

If I had a dollar for every time I helped a student who was trying to debug a problem using a terminal window the size of a Post-It note, I’d be a rich man. Maximize the window so you can actually see what you’re doing.

Close unneeded tabs

Open tabs cost mental energy. Between the cost of re-finding the page later and the cost of being mentally cluttered with all those tabs open, it’s way cheaper just to re-find the page later. On average I myself usually have about two tabs open at any given time.