The Cross-Country RV Trip I Didn't Take

At HealthPro, we recently started having some company culture discussions. The general idea is that a company will always have some culture, and that you can influence what it becomes by being aware of it and proactively discussing it. After a general brainstorming session together, we started discussing things in Slack (“Work As If Remote”, right?)

The value that we were discussing was “Try new things / Don’t be afraid to make mistakes”. One of Kyle’s questions that seemed to get a lot of response was “What’s the most spectacular failure you’ve been a part of? What did you learn from it?”

I liked my response, so thought I would share it with everyone now.

My Story

One thing that I did in 2011 that was a pretty big failure in my mind was unsuccessfully trying to go on a cross-country RV trip.

I had some money saved up and the startup I was working for had folded (which is probably a failure story itself), and thought it would be good to get away from everything and travel out west and try to play a lot of Ultimate. I thought that I would need a small RV to make living work (and could maybe code or something in there), so traveled several hours to Kentucky based on a Craigslist ad to buy a 22-foot 1987 Winnebago Minnie Winnie. My (now) wife drove me there and followed me back in one very long day.

When driving back, we stopped at a gas station, and the RV wouldn’t start! We waited a bit and it started up again, but I was loath to stop it again. When we got home after about ten hours of driving round-trip in the summer, I was pretty exhausted.

I am not mechanically inclined, so considering driving across the country in a 25-year-old RV was not really that exciting. I kind of freaked out and sat in the basement for a day or two. I signed up for a conference in Colorado that I was going to drive to that week, but didn’t end up going and so lost out on the money that I put into it.

The RV sat in the driveway through the winter. After nine months I put it up for sale.

The woman who ended up buying it has the foresight to have a mechanic appraise it, which seems like a smart thing to do. They said it had some roof damage, so I ended up selling it to her for about $2000 less than what I bought it for. The mechanic also said that there was essentially no oil in the thing, so it was lucky that I didn’t burn up the engine with the several hour drive from Kentucky. I was glad to be finally rid of it, but it was a fairly expensive mistake.

Obviously in the grand scheme of things, it was not that big of a deal. Instead of going on the trip I used the rest of the savings to start some independent work, which launched my consulting business. If that is the worst thing that happens to me, then I would consider myself very fortunate.

Lessons Learned

First, get vehicles checked out if you aren’t sure about them.

I realized that there were easier and cheaper ways to make the trip that I wanted to take. I could have taken my pretty well-functioning car and bought a tent and camped out. The gas certainly would have been cheaper (30 MPG vs 10 MPG.) Plus, I would have saved the capital outlay and potentially loan interest of purchasing another vehicle.

Do the simplest thing that could possibly work. Instead of making the big trip first, make smaller trips to figure out if I like it or not and I can come back easily if there are any complications. While having the conference as a deadline pushed me to action, I might have made worse decisions because I tried overcomplicating things early.

Last, play to your strengths. Buying a super-old RV that I didn’t know much about and that I would need to maintain was not in my wheelhouse.

Work As If Remote

At Haven, one of the unwritten values we had was “work as if remote”. In this post I’ll explain what this means and why it is important.

What does it mean?

“Work as if remote” means we always pretend that there are people working remotely, and behave accordingly. Even if everyone on the team is in the same room, or at the same meeting, we operate like there are people that are across the country. We do this by documenting:

  • the plans that we have
  • the decisions we make
  • the things we do
  • the things we learn
  • meetings or conversations we have

The tools

We implemented this at Haven by using Slack for most communication, and Trello for capturing story-specific details in line with the cards that they were related to.

Also, MeetingHero (now the inferiorly named WorkLife) allowed us to record meetings in a collaborative way, although a shared Google Drive document could achieve the same goal.

Benefits

There are usually people working remotely, even if you don’t think they are. First, there may actually be remote people that you have just forgotten about. :) We had a designer working in San Francisco, and while he didn’t chime in often, writing as much as we could likely gave him more context for designs.

You might think that everyone who cares about a given subject is in the current room, but often there are other people that would benefit from having conversations written down.

Working as if remote allows us to bring new people up to speed more quickly, because we document what we are doing and how we do it. Asking a question in a shared channel enables anyone to answer it without interrupting everyone. Everyone can search back through history for the discussion and resolution of problems.

Writing out what we are doing forces our thinking to be sharper and our decisions more explicit. We get a chance to look back at the decisions we make along the way and introspect when things go well or go poorly. We make it clearer what we are planning on doing and can hold ourselves accountable. Coworkers understand what we do on a daily basis and where they might be able to help.

In today’s software development environment, being able to work remotely some of the time is more and more common. I doubt that I will consider future work that isn’t partially remote, and there are or will likely be more people like me. Most people expect to be able to run errands or take care of their kids or have a more flexible schedule, but our communication patterns need to change if we are to be successful and have this be a possibility. To this end, I would argue that working as if remote is one of the foundations of a healthy culture around taking vacations and traveling. In my opinion, it should not matter if I am across the street or across the country if we are getting work done effectively as a team.

Tradeoffs

Communicating in this way may seem like a lot of extra work. In reality, it doesn’t take much more time than having the conversations that we are already having. Also, it can actually save us time. When I try to remember something I did yesterday and I wrote something about it, it saves time and effort.

Four people getting together in a room for two hours is an expensive thing. We have an obligation to make sure that meeting time is well spent and that we are clear on what comes out of the meeting. By typing up good notes, we give people who weren’t in the meeting the benefit of being in the meeting without needing to devote the entire time to be there.

Good practices

Be asynchronous

From code review to meetings to doing standups, there are many activities that can be made asynchronous or location-independent. You may start a process that works synchronously (perhaps a recurring meeting), but once the team understands the parameters, consider how can it be distributed over time and location.

Overcommunicate

This is probably a good principle in general, but write more and about more topics than seems necessary. If you feel like someone might say, “TMI (too much information)!”, then you are probably headed in the right direction.

Record synchronous communication

If you have a useful conversation with someone, post a summary so that others can learn from it. Also, this helps document what you talked about to ensure that you actually heard it right.

Embrace the firehose

Posting everything that happens can be a little overwhelming. Personally, I’d rather have more information than less, so I think this is worth it. But when everything is a priority, nothing is. It is useful to mark things as “FYI” or “important” so that others understand the priority and can effectively filter the firehose of information. You also need to set up your channels and policies to be responsive but also not get overwhelmed.

Don’t be afraid to sync up

Even when remote, be willing to have a Skype / Hangout to sync up. Synchronous communication allows you to hash things out much more quickly. Then, of course, write down what you talked about and the main decisions or clarifications made. :)

Conclusion

I wanted to share this because I think it was a really useful philosophy that we had. I will definitely be trying to do things along these lines going forward.

Debugging An Issue With Should.js

Quick post today about something that I was debugging that I thought might help someone out.

We were using should.js for Mocha assertions. In some of the tests, I was suddenly getting warnings like:

WARN Strict version of eql return different result for this comparison
WARN it means that e.g { a: 10 } is equal to { a: "10" }, make sure it is expected
WARN To disable any warnings add should.warn = false
WARN If you think that is not right, raise issue on github https://github.com/shouldjs/should.js/issues

I tried adding the should.warn = false but that did not seem to have an effect. Plus, this code would have been required in each file that had the problem, so it is an unsatisfactory solution.

The solution is to change things like the following:

response.status.should.eql('404');

in the tests to:

response.status.should.equal(404);

Basically should.js tries to ensure that we are doing what we expect when matching equality. Since the types don’t exactly match, it issues a warning so that we take a cloer look. This behavior must have changed recently when I saw it, since it previously seemed to work. Happily, the new way of doing this is more correct as a side effect.

Coffeebot

I wanted to be alerted via Slack when the coffee was finished brewing so I could be sure that I would get coffee. Also, I wanted to work on a small hardware project to have some fun. I had a few hardware pieces from RobotsConf 2014, so figured I’d try getting something working that would fix my coffee notification problem.

The end result is a piece of hardware that sends a Slack message when the coffee has started brewing, and sends another one when the coffee is probably done:

What the bot looks like in action

Here is what the hardware ended up looking like in the end:

The hardware

I’m pretty happy with the process and the result, and wanted to share how I thought about it.

How I went about making it

I figured that I would need some of the following hardware capabilities to make this happen:

  • microcontroller for programming the logic
  • ability to hit the internet (possibly with a wireless adapter)
  • built in breadboard helpful to wire this up as a prototype

One of the boards I had laying around was a Particle Core (formerly Spark Core), and after understanding what it did, it seemed to fit the bill. It has a built-in breadboard, and, even better, an onboard Wi-Fi module. You can set it up to talk with your wireless network through the command line or with a phone app, which is kind of neat. It also has an interesting multicolor LED which signals the current system and networking state.

Early steps

My first step was just to get the thing blinking an LED. I had some experience with Arduino programming, and the Particle Core is programmed in a similar way.

I downloaded some sample code from somewhere, and got a basic LED blink working. The Particle Core has some integrated LEDs, so this was fairly straightforward (just write a certain pin HIGH.)

I didn’t want to have to download an IDE or use a web-based tool to compile the firmware. I read through the Particle docs and figured out how to compile the Particle firmware through a command-line interface by installing the particle-cli NPM package. After some setup hurdles it overall worked pretty well.

Networking

Next, I wanted to hit the network, since that was critical. If I couldn’t do that, I couldn’t send anything to Slack.

I initially had some problems connecting to my local network. The instructions did not seem helpful for resolving the issue, and I even tried the phone app to get things connected. I ended up needing to use 2.4 GHz channel, since the Particle could not talk to a 5 GHz channel. Once that was squared away, I was ready to try to send an actual request.

I pulled in an open-source HTTP library for Particle Core that seemed like it might work. My goal was to hit example.com and print out a response. After some finagling, I was able to log out the response, which meant the network connection was successful.

It took a little while to hook the hardware button I had up so that when I pressed it, a network request would be initiated. I think this was due to the button being small and so there was no documentation or even a readable serial number. I had to search around for a bit until I figured out how the switch was working internally.

A minor setback

At this point, I tried integrating directly with the Slack API. The issue that I ran into was that the HttpClient library doesn’t support HTTPS. This is an important detail because Slack’s API is only available over HTTPS. The issue that I linked to above mentions that a workaround is to post an intermediate server that you control.

At first, I didn’t really like this idea, but then I thought that I could more easily make changes and deploy when the payload and security configuration were on a server that I could control rather than the hardware. The hardware could remain basic and the server could contain more of the sending logic.

So I spun up a Heroku instance and pointed the Coffeebot at a simple Node server running on it. I tested by creating a private Slack room that I didn’t invite anyone else to, so that people wouldn’t get annoyed by the testing I had to do to get it working.

I ended up using some emoji the Coffeebot’s image and called it “Drippy”. I think this gave it a bit of a fun feeling. :)

Source code

There are two repositories:

My memory is not that strong, so thankfully I documented things pretty well in the spark-coffee repository. Also, having decently atomic commits even when I knew I was going to throw the code away was useful when tracing the evolution of the project. In my opinion, people underrate commits as project documentation and history.

Schematic

I don’t have a shareable schematic, but if there is an easy way to make something like this, I would consider spending a few minutes on it.

Overall impressions

It was easier than I thought to get everything working. I really like having integrated Wi-Fi support; it was nice to not have to deal with the complexity of integrating with a Wi-Fi USB module.

It would be interesting to be able to send commands to Coffeebot from Slack. We could integrate more sensors like temperature or volume to figure out remotely whether we will need to brew more coffee soon or not.

Setting Up RuboCop on an Existing Rails Project

I recently set up RuboCop on an existing Rails project. I’ll share how I approached it and what could have gone better. Specifically, I’ll help you fit RuboCop to your project’s needs, and not the other way around.

What is RuboCop?

RuboCop is a Ruby linter. It has a bunch of rules about how Ruby should be formatted. It calls these formatting units cops, and has many built in. You can define your own cops if you want to get crazy. Most of the built-in cops have configuration, and at least the ability to disable the cop.

Why use a Ruby linter?

Having a style guide of some sort saves the software team time. It’s nice to have a canonical guide on what the source code should look like. Having a guide saves the team from wasting time formatting and lets them get on to more productive things. It also makes reading the code easier because everything is in a consistent format.

But having a style guide is not enough. Developers end up either unwittingly violating the style guide, or feeling like they are making passive-aggressive comments in code review. With a linter and continuous integration, you can ensure that the project automatically points out style violations. It stops wasting time and effort and lets you focus on the things that matter. It takes your documentation of what the software should look like and turns it into an executable specification.

Avoiding poor decisions

The built-in cops are based closely on the Ruby style guide. However, those guidelines probably don’t line up with the current code your project has. Sometimes the cops are overbearing. Sometimes they don’t make much sense. Sometimes they are just too strict.

The first thing to do–which I did a poor job of this time–is to ask the team that you are working with which things in the guide they disagree with. I spent a little too much time thinking on my own and changing things that eventually needed to be reverted.

Another poor decision was when I disagreed with the linter, but instead of listening to my experience and judgment, acquiesced to the tool’s demands. I think that linters should serve the project’s goals, not the other way around. If you find yourself rewriting or restyling swaths of code, consider if you could make the cops less picky or disable them entirely. Hopefully this post will help with understanding the tool’s settings well enough to change them.

The first run

I’d recommend installing RuboCop and then just running it and seeing what happens. You will likely get a lot of errors. Some basic checks to start putting in your RuboCop configuration (.rubocop.yml):

  • does it at least finish the run without crashing? :)
  • do I have the right Ruby files being linted?
  • do I have the right files excluded?

Sizing up the suggestions

Now that you have a list of suggestions from RuboCop, it’s time to whittle them down. But finding exactly what you need to do for each cop can be tough. What I would recommend at this point is to enable two RuboCop settings, either on the command-line or in your configuration.

Printing cop names

The first setting I recommend prints the full name of cops when there is a failure. This helps you learn more about them and to know the right name for disabling and configuring the cop.

On the command-line:

$ rubocop --display-cop-names

This turns the output from something like:

lib/foo.rb:42:10: C: Prefer single-quoted strings when you don't need string interpolation or special symbols.

to:

lib/foo.rb:42:10: C: Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.

I generally prefer the long form of flags for scripts since I’ll have to type it once and it’s self-documenting. On the command line I’ll usually use the shorter version of the flag. Another good approach is changing the .rubocop.yml file to have this be the default configuration, so you just need to invoke rubocop and it uses the settings there.

In the second example above, you can see that the cop category is Style, and the name is StringLiterals. So if you want to disable this check, you can add the following to your rubocop.yml:

Style/StringLiterals:
  Enabled: false

Understanding cops

The second setting I recommend is --display-style-guide. This setting is useful for seeing what RuboCop wants and if you want to actually follow that rule or not. It links to an explanation of the rule, typically in the Ruby style guide.

For example, you might get:

lib/foo.rb:39:39: C: Use def with parentheses when there are parameters. (https://github.com/bbatsov/ruby-style-guide#method-parens)

Sometimes the style guide is less than helpful: it just says this pattern is bad, with no explanation of why. So you need to use discretion. But linking to the docs is better than needing to guess what format RuboCop wants your code to be in.

Getting everything working

So you’ve got 47 errors. Where to begin?

You might want to go through and fix any common style violations in bulk, or disable cops that are overly picky. This might cut the errors down to something more manageable.

If your project is big, you might work on a subset of the project. For example, if you have many Ruby files in lib, and many others under app, do each of these subdirectories separately. Then when you lint the whole project, it will be clean.

Overwhelmed with errors and want to work on one change at a time? Try the --fast-fail flag to stop the RuboCop run after the first issue. Then fix the issue and continue running, and it should give a different error, hopefully later in the lint process. Another approach is to remember how many violations were spotted on the last run and the number should go down by one after each fix.

Linting on Rails

If you are on a Rails project, there is a flag for Rails-specific style checks. You can enable this with --rails. I recommend getting all of the Ruby checks working first, and then you can nail down the Rails-specific things. I learned about some interesting deprecations or recommendations, which were useful since I hadn’t worked on a Rails project in a little while.

Handling complexity

RuboCop has some interesting defaults for code complexity. While we can all agree that less complex code is good, it is labor-intensive to retrofit an existing codebase to follow code complexity guidelines. You are probably going to get errors like:

lib/foo.rb:39:3: C: Method has too many lines. [47/20]
lib/foo.rb:39:3: C: Perceived complexity for `bar` is too high. [8/7]

At first I tried cleaning some of these up, but there are a few issues with this:

  1. I’m new on the project
  2. the project may not have solid test coverage
  3. the code works now, and if I modify it, it might not actually make it much better and might introduce bugs
  4. who is to say what the ideal complexity should be?
  5. it’s just going to take a lot of time that could be better used at this point on the project lifecycle

However, lint errors cause our continuous integration to fail, so we need to address them somehow. Rather than disable the complexity cops, I think a balanced approach is to agree on a reasonable upper limit for a method’s length in our codebase and then fix any offenders. Since measures like function or module length tend to follow a power law, there should be only a few very complex areas.

For the rest, we set the limits high enough that they don’t fail, and if the module or function then goes over the limit, RuboCop will warn us and we will have increased feedback that our design is unsustainable. Generally code review should filter out egregious examples, but the fact that we added twenty lines to an already 200+ line file is often lost unless we use a tool that is more objective. Basically if the linter fails on complexity when the limits are high, then we know it is a useful failure to report and “stop the line” on.

An interesting approach would be to make the limits high and make a plan to scale down over time. Say our goal is a maximum of 100 lines in a particular file, but right now we have many that are over 200 lines long. We set the limit high at 250 to start, and then every week decrement it by ten until it gets to 100 lines. We could do this with a calender reminder, a bot that rewrites the configuration file, or actually encode it in the .rubocop.yml file or an environment variable, depending on how RuboCop reads in the configuration file.

(Again, just because your project passes linting doesn’t mean that it is well-written. It is just a tool to try to help code quality and save the team time.)

Don’t let your hard work go to waste

So you’ve gotten down to zero style suggestions. Congratulations!

But just because the cops are passing now doesn’t mean they will stay that way. Unless you put it on your continuous integration, the project will quickly gain style violations. Partially because there may still be some wrinkles in our RuboCop configuration, partially because we are humans and sometimes do things in different or suboptimal ways.

When the codebase is under CI, team members get quick feedback when they have angered RuboCop. Putting RuboCop on CI is pretty easy once you have fixed the issues.

Some handy aliases

At the beginning I commonly mistyped rubocop, so I made these aliases for ZSH to prevent me from having to retype it:

alias rubycop="rubocop"
alias rubocopy="rubocop"

Found this post helpful?

If you found this post helpful and want more like it, check out my guide to Starting on an Existing Rails Project, where I cover how to quickly come up to speed on a Rails project and make an impact.