What you should really be talking about

I was reading State of Apple on ignorethecode.net yesterday which has this great quote:

But if I’m using an iPhone, how much money Samsung makes doesn’t really affect my experience at all — and actually, neither does how much money Apple makes, or how many bugs Android has. What affects my experience is how good the iPhone is. And if nobody talks about the iPhone’s problems, it’s not going to get any better.

We can spend so much time talking about things that don’t really matter to us. Talking about the problems with someone else’s product or service isn’t going to improve our experience. We should spend less time talking about the other guy, and more time examining and talking about our situation/product/service/etc.

Things we cannot see.

I’m starting to read Start With Why by Simon Sinek. I haven’t gotten very far into it yet, but I just read a chapter with a couple of quotes I really like:

… great leaders understand the value in the things we cannot see.

… it is what we can’t see that makes long-term success more predictable.

These come after a story about American auto manufacturing business men who traveled to Japan to learn from Japanese auto manufacturers. While the American factories solved an issue with how car doors fit by employing a person to use a mallet to ensure a correct fit, the Japanese factory took a different approach. The Japanese factory went back to the design of their components to make sure the engineering was done in such a way that the doors fit correctly every time. It really didn’t make much difference in the bottom line, but it’s important in the sense that the Japenese factory was solving the right problem instead of just fixing the symptoms.

Another quote:

So many organizations function in a world of tangible goals and the mallets to achieve them.

I think this is very applicable to software development. There are so many things when writing software that are analogous to fixing doors with a mallet. When we have defects, do we send them to the person with a mallet to just “fix it” or do we go back to the design and see where we could improve at a more fundamental level? How can we improve our design, or our process, so that we don’t need somebody with a mallet at the end of the line just to get things functioning?

When working with non-developers, it is often difficult to convey the importance of tackling technical debt, or spending twice as long on a feature just to “do it right”. But these things are important. They may not show up to the end user, but they show up in long term success.

Just some food for thought…

MyMediaManager

My wife and I have over 16,000 digital photos going back to 2007. Over the years we’ve used different camera phones, digital cameras, storage devices, and backup plans. I’ve managed to consolidate them all into DropBox over the last year or two, but they’re still in random folders and not really organized very well. I’m sure we’re not the only people with this issue. I know there are applications out there to help you manage photo libraries, but none that I’ve tried work like I want.

I decided to solve this problem with code. I’m starting small, but I hope over time to build out several utilities to help me manage our digital media in a way that makes sense and is simple to do.

Introducing, MyMediaManager. Right now, it simply takes photos from a source directory and moves them into a target directory organized into folders by year and month. It does this using metadata in the image files.

This is built to work how I want it to work, but if it would be useful to you, feel free to use it and contribute if you would like.

Postmortem on bug found in i18n-extra_translations

I spent a significant portion of the second half of last week struggling with an issue in a Ruby on Rails project I work on regularly. This post is a postmortem on the issue.

Sometime in the middle of last week I started seeing rspec failures that looked like this:

ActionView::Template::Error:
 undefined method `[]=' for :used:Symbol
#./app/controllers/application_controller.rb:300:in `some_method'
#./app/controllers/application_controller.rb:350:in `second_method'
#./spec/controllers/some_random_controller_spec.rb:220:in ...

I modified the stack trace because it came from various different  specs, and the exact details don’t matter right now. The important thing to know is that the failures appeared to be random (different number of failures every time), the stack trace always terminated in the same method in the ApplicationController, and the method in the ApplicationController rendered a partial that lives inside a gem included in the Gemfile. We’ll refer to this external gem as common_components from here on.

The main reason that I struggled with this issue for so long is that I didn’t know about the -b option for rpsec. If you run rspec with the -b option, it will show you detailed stack traces. By default it only shows you the part of the stack trace relevant to your current project. Once I tried this, the detailed stack trace gave me a much better idea of where the problem was. I’ll spare you the massive stack trace that was generated and just say that the error was coming from common_components/_error.html.haml. The line the error was coming from looked like this:

%p= t('error.default_message')

Looking even further along in the stack trace I could see that the error was coming from gems/i18n-extra_translations-0.0.5/lib/i18n/extra_translations/store.rb:17:in `add_key’. I didn’t understand why this code was throwing an error, so I dug into the source code. The error came from line 17 of store.rb, which looks like this:

This store class inherits from Hash and is used as a container of all translation keys used by the application. The gem has an extender which replaced the I18n.translate method and will call store.miss or store.use. These two methods call the protected method add_key and tell it to add the key to the store with either a :used or a :miss result. The use of keys.inject on line 16 is a crafty way of building a nested Hash structure based on the dot separated translation keys. For instance, if these were the only two translations used in the application:

en.error.default_message
en.error.default_header

Then the store would end up looking like this:

{
  en: {
    error: {
      default_message: :used,
      default_header: :used
    }
  }
}

That code took a little bit to parse in my head, but once I figured out exactly how it was working, I still wasn’t quite sure why the error was happening. Clearly, line 16 sets h to a Hash, so why is it saying h is :used:Symbol on line 17? It didn’t make sense.

After a few minutes of head scratching, I realized that we had conflicting translation keys. It would make sense that h is set to :used in the case that add_keys has already been called with a less specific key then we’re currently adding.

Take the following translation files for example:

Now let’s step through, at a relatively high level, what happens in i18n-extra_translations when these keys are translated:

  1. I18n.translate(‘en.error’) is called.
  2. The store now looks like:
    {
      en: {
        error: :used
      }
    }
  3. I18n.translate(‘en.error.default_header’) is called.
  4. store.use is called for this key which in turn calls add_keys.
  5. On line 16 of store.rb, h is set to :used instead of an empty hash because it’s already been initialized in the previous I18n.translate call.
  6. On line 17, []= is undefined for :used:Symbol because it’s expecting h to be a Hash, not a symbol.

The problem became extremely clear once I realized what was happening. If you have “conflicting” translations, and a less specific translation is used after a more specific translation, the i18n-extra_translations gem can’t handle it. This also explains why the failures were random… since rspec runs tests in a random order, which tests failed would depend on which order the translations were called.

I’ve logged an issue on the project in GitHub. For now, we can fix the issue by “namespacing” translations in the common_components gem under a root key. For example, en.common_components.error.default_message instead of en.error.default_message.

tl;dr

The i18n-extra_translations gem (source) has a bug – or unsupported use case – that doesn’t allow it to function correctly in the case that you have conflicting translation files loaded. For instance, if a gem you’ve included in your project has the key en.error.default_message=”Internal Server Error” and your application has the key en.error=”Error” the i18n-extra_translations gem will fail internally with the error undefined method `[]=’ for :used:Symbol.

GIT-PROTIP: Copy Most Recent Commit Hash

I often have the need to get the latest commit hash on my current branch so I can reference it on GitHub in response to a comment on a pull request. My workflow was always:

  1. Make code changes
  2. Commit changes
  3. Run git log
  4. Copy latest commit hash
  5. Run git push
  6. Paste the commit hash into GitHub

I decided to make this workflow a little easier by adding an alias for the following command which cuts out steps 3 and 4:

I aliased this command to glc and now I just run that alias and the latest commit hash is copied to my clipboard and ready to be pasted into GitHub.

Intercontinental Submarine Fiber Optic Cable

UPDATE: A cool resource (interactive map) for learning about the existing infrastructure: http://www.cablemap.info/

I was reading an article online that mentioned that the United States is connected to 63 countries by fiber optic cable. It made me curious about intercontinental fiber optic cable and how that cable gets ran over such distances. I searched Google and found this really interesting video. Enjoy!

http://www.youtube.com/watch?v=KulqAHJ16UQ

Sublime Package Control

If you’re a developer that uses Sublime as your editor, you need to know about the Sublime Package Control. The Sublime Package Control is an awesome little package that integrates with Sublime and allows you to easily install and manage other packages.

To install Sublime Package Control, just hit Ctrl + ` to bring up the console and paste in the following snippet:

import urllib2,os; pf=’Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),’wb’).write(urllib2.urlopen(‘http://sublime.wbond.net/’+pf.replace(‘ ‘,’%20′)).read()); print(‘Please restart Sublime Text to finish installation’)

Restart Sublime and then you can run the Package Control: Install Package helper and it will give you a list of packages that you can install.