MP

Fresh Starts and Resolutions

January will be over in just a few days, so now is a good time to remind everyone that New Years isn’t the only time you can take steps towards changing your life. There’s a science behind successful resolutions, and it doesn’t require you to wait for a new year. Any milestone of time will work: a new semester, month, week, or even your birthday.

Don’t start out this year with a handful of lofty resolutions that you’ve already failed. Instead, cherry-pick one or two you know you can accomplish and start working on them at the beginning of next week, February 1st. The confidence boost you’ll get from succeeding will help you slowly build up to more difficult goals as the year progresses.

Merry Christmas

Perl standing in snow

Merry Christmas, everyone! I hope you have a wonderful holiday season, however you choose to celebrate it.

You Don’t Pass or Fail at Being a Person

Nick Bradbury, in a great article comparing programming to life:

I realized that the people in charge are as clueless as the rest of us. Like our software, our society just kind of happened over the years and it’s always on the verge of coming tumbling down. Nobody really knows what they’re doing or what they’re talking about.

There’s no magical moment you grow up, understand everything, and become “good” at life. We can become better at the things we’ve experienced, but we’re effectively a kid again whenever something new and scary comes our way.

There’s a passage I love from Neil Gaiman’s The Ocean at the End of the Lane:

Grown-ups don’t look like grown-ups on the inside either. Outside, they’re big and thoughtless and they always know what they’re doing. Inside, they look just like they always have. Like they did when they were your age. Truth is, there aren’t any grown-ups. Not one, in the whole wide world.

We’re all just stumbling around in our own way, learning as we go, and yet somehow humans have managed to survive for the last 200,000 years. I always find that comforting whenever I’m worried about making the wrong decision. To quote The Ocean again, “You don’t pass or fail at being a person, dear.” Just do the best you can.

Burnout Is about Resentment ➔

When I experience burnout, I usually assume it’s due to monotony and that I need to take a break, but that might be looking at it from the wrong perspective. Marissa Mayer put a new twist on things:

I have a theory that burnout is about resentment. And you beat it by knowing what it is you’re giving up that makes you resentful. I tell people: Find your rhythm. Your rhythm is what matters to you so much that when you miss it you’re resentful of your work.

Maybe boredom isn’t why taking a break helps. Maybe the break helps because I’m able to start focusing on something I care about more.

Reasons Stop Mattering ➔

I was recently reminded of an anecdote Steve Jobs told newly-appointed Vice Presidents. It sums up the realities of business so well:

Jobs imagines his garbage regularly not being emptied in his office, and when he asks the janitor why, he gets an excuse: The locks have been changed, and the janitor doesn’t have a key. This is an acceptable excuse coming from someone who empties trash bins for a living. The janitor gets to explain why something went wrong. Senior people do not. “When you’re the janitor,” Jobs has repeatedly told incoming VPs, “reasons matter.” He continues: “Somewhere between the janitor and the CEO, reasons stop mattering.”

This Is Not about Alcohol ➔

David Cook on alcohol and sexual assault:

In 2013, a U.N. international study showed that 70 percent of men who admitted to raping women did so because they felt entitled. As if they owned her body.

Such power ownership is not some gee-whiz feeling that appears from nowhere, a rape urge that falls out of the sky as five beers turn to six.

Waiting for the Best Solution

The Amazon Fire TV and Xbox One have started implementing voice control, and it’s likely we’ll see future Apple TV upgrades that offer the same.1 But why did it take us so long to make input less awkward? Were we so focused on the best solution that we forgot to look for better solutions that would suffice in the meantime?

We have a wonderful iOS Remote application for controlling the Apple TV but no quick way to open it. Even if our iPhone is within arm’s reach we still need to unlock it, find and open the Remote app, wait for it to load, select the Apple TV, and wait for it to connect before we can start navigating.

Earlier this month I tweeted:

The iOS Remote app should display a notification on your lock screen whenever a linked AppleTV is waiting for keyboard input.

How long would it take to build a solution that opens Remote and connects to your Apple TV when you tap on a notification? It’s not perfect, and it doesn’t cover every situation, but it would have temporarily eased our pain over the last five years.2

I’m not in any position to question Apple’s decisions—they’ve had unequivocal success without my input—but the rest of us could use this as a reminder. The next time you’re brainstorming, try not to toss aside all the imperfect solutions you come up with along the way. Maybe one of them is a simple solution you could implement now while continuing to work on the holy grail.

  1. WWDC 2014 kicks off next week, but the Apple TV’s release schedule isn’t consistent and there are ever-circulating rumors of bigger changes coming this Fall. 

  2. iTunes Remote was released in 2008, and iOS has had push notifications since 2009. 

Golang: Missing or Incomplete Socket Messages

I recently ran into a puzzling error while working with Go socket connections. When performing a read operation, incoming messages would periodically arrive incomplete or not at all. The fix turned out to be easy, but the problem wasn’t immediately obvious to me.

Almost every Go server implementation is built on top of the io.Reader interface. Go also provides us bufio.Reader, which implements buffering for an io.Reader object. It’s common to run into the following pattern for reading bytes from a connection:

data, err := bufio.NewReader(conn).ReadBytes('\r')
if err != nil {
    // Handle error
}

But we rarely need to read just one message from a socket connection. To continuously wait for new messages, we can enclose our code in a for loop and hand off processing to another goroutine. If we’re feeling clever, we can reduce memory usage and processing time by using a pointer to pass off our data:

for {
    data, err := bufio.NewReader(conn).ReadBytes('\r')
    if err != nil {
        // Handle error
    }

    go processData(&data)
}

Everything looks good so far, but this code actually introduces a big problem that you might not notice during testing: we’re creating a new bufio.Reader, and underlying buffer, on every iteration. Since the buffer is not persistent across iterations, any messages received before the new Reader is created will be lost.

The proper way to write this code is to create a new Reader outside of the for loop. You can then call its read methods like normal on each loop iteration:

reader := bufio.NewReader(conn)

for {
    data, err := reader.ReadBytes('\r')
    if err != nil {
        // Handle error
    }

    go processData(&data)
}

Survivorship Bias ➔

David McRaney, tackling misconceptions about studying success:

The problem here is that you rarely take away from these inspirational figures advice on what not to do, on what you should avoid, and that’s because they don’t know. Information like that is lost along with the people who don’t make it out of bad situations or who don’t make it on the cover of business magazines – people who don’t get invited to speak at graduations and commencements and inaugurations.

This is a long read, but it’s particularly worthwhile for recent college graduates who are slowly making their way into the world. It’s easy to furiously soak up advice from people and companies we admire, but we often forget how much we can learn from failure—including our own.

Printing TODO Comments with Ack

Example output of TODO comments

There are a lot of terminal commands floating around that will list the TODO comments in your source code, but I’ve always found their output format lacking. Last week I decided to come up with something better. It’s language agnostic and it organizes everything into vertical columns to make reading easier:

shell ack -i \ -o \ --group \ --color \ --sort-files "\b(TODO|FIX(ME)?|OPTIMIZE|BUG)(\(\w+\))?: (.*)" \ --ignore-dir={.git,node_modules,vendor,Pods} \ | perl -pe "s/:/\t/" \ | perl -pe "s/\t(\w+)(\(\w+\))?:(.*)/\t\$1:\$3 \$2/"

Here’s a breakdown of the options we’re using, and a few others you might find useful:

Option Description
--color Highlight the matching text
-follow Follow symbolic links
--group Group matches by filename
-i Ignore the case of matches
-n Don’t search within subdirectories
-o Display only the matched text1
--sort-files Sort the outputted files in alphabetical order
--ignore-dir A list of directories that should be ignored

Updates

  • Fixed a bug where words such as “suffix” were captured, and added examples for ignoring directories. — 17 April 2016
  • Added support files whose names begin with a period. — 30 July 2016
  • Moved Go-style attribution to the end of the comment. This should make the list easier to scan. For example, “XYZ(who): Comment text” is now displayed as “XYZ: Comment text (who)”. — 25 May 2014
  • Added support for “BUG” and Go-style attributed comments such as “BUG(who)”. — 29 April 2014
  1. When combined with wildcard matching, this helps us discard any tabs or spaces that come before the comment.