How Video Games Made Me A Better Software Engineer (& Dad!)

About six months ago I left an amazing job at Amazon for a very different, yet equally amazing job at Riot Games. I won’t bore you with the laundry list of factors that went into my decision, but I will confess that one of the many factors was my life-long love of video games. I’m a bit quirky in the way that I play video games because I can’t play a game casually. When I pick up a game, I play purely to master the game and to challenge myself (and possibly my team, depending on the game) to see how good I/we can be. As crazy as it sounds, that constant quest for mastery has taught me a valuable lesson that not only has made me better at my job as an engineering manager, but has helped me to grow in other areas of my life.

Before I hit you with the punch line, let me give you some quick background to help set the stage. As you may or may not know, Riot Games produces a very popular game called League of Legends that pits two teams of five players against each other in a ~20–60 minute battle to destroy the other team’s Nexus before they destroy yours. League of Legends is one of those games that is relatively quick to learn, but takes a lifetime to master because of the complexity of gameplay. Any player can try to work his or her way up the game’s elo rating system, which is broken down into several divisions: bronze, silver, gold, platinum, diamond, master, and challenger.

When I first started trying to climb the elo ladder, I was able to work my way from bronze to silver by just grinding out a bunch of games. As I was playing, I was building my “mechanics” and learning basic concepts that allowed me to improve fairly quickly. As I kept playing, however, my progress stalled out before I was able to hit gold. That’s when it struck me that if I wanted to improve, I would have to actively start doing things to improve. I wasn’t going to get better by just putting my time in, playing game after game, and making the same mistakes over and over again.

That same concept of mastery applies to almost every area of our lives. When I landed my first job as a software developer, I had so much to learn that I could build my development chops by simply doing my job. At some point that ceased to be true and I had to start doing very intentional things to continue to improve. Sometimes that meant seeking out seasoned veterans for some pair programming, and other times it meant changing teams to work in a new domain or with a new set of tools.

IMHO, the hardest part of improving at something is 1) identifying when we’ve hit our natural plateau and we’re just grinding it out without getting better, 2) deciding that we actually want to invest the immense time and energy needed to get better, and then 3) taking some time where we are very intentionally in the “stretch zone” and practicing for mastery. This season I’ve advanced to platinum in League of Legends, and the only way I was able to accomplish that goal was by setting aside a chunk of play time every week where I wrote down a specific goal (which could be something like “die 3 or less times”, or “kill 85 minions by the 10-minute mark”), focused on achieving that goal while I played, and sometimes watched replays of my games to find mistakes and figure out what goals I should set in the future. As an engineering manager, I put myself in the stretch zone by keeping my personal development plan (PDP) relevant and up-to-date, spending quality time learning from mentors each week, reading books and blogs that are written by other managers that I respect, and continually collecting feedback from the folks that I’m managing on how I can be more effective and using that feedback to drive new goals into my PDP. As a husband and a dad, I get in the stretch zone by sitting down with my wife every Sunday evening and talking through how things are going at home and using those discussions to pick a few things to focus on for the week.

There are a lot of other areas in my life where I’m intentionally not putting in the effort to get in the stretch zone and improve, and I’m fine with that because I only have a finite amount of time and focus. I love playing golf and would like to be a better golfer, but right now I’m just hitting the course occasionally and playing for fun. I suspect very few people have the discipline and the mental focus to context switch and really improve at more than about three things at a time.

I leave you with this challenge: Identify one thing that you want to get better at, and come up with a plan to get into the stretch zone at least a few times a week for the next month. Then leave me a comment below and let me know how your experience went. And the next time someone tells you to quit playing video games and do something productive, tell them that you’re learning valuable lessons that apply to the rest of your life.

Delight Customers By Not Giving Them What They Want

It sounds odd, doesn’t it? I’ve spent the last 2 years at Amazon, where we preach customer obsession day in and day out. It’s one of the things that makes me love my job. Yet I’m firmly convinced that in some very few cases, the best thing to do for a customer is to not give them exactly what they are asking for.

Consider a recent discussion that happened at work during a meeting. To set the stage, you have to understand that Amazon has made a massive bet in continuous deployment over the last several years. John Jenkins, a long time Amazonian before joining Pinterest, gave some impressive stats on how frequently Amazon deploys code to production during his talk at Velocity in 2011. In the two years since that talk the company has doubled down on it’s investment to ship code to production continuously, and while I can’t disclose specific details the numbers today would blow the 2011 numbers out of the water. We have an internal saying that “bake time is for cookies”, and the normal mode of operation for most teams is that changes flow to production within minutes or hours of when they are checked in. Of course, deploying continuously comes with a cost. First, you have to have tooling in place that can drive the correct integration and deployment workflows and respond to failures gracefully. Second, applications have to be designed and implemented a certain way: interfaces must be hardened to allow independent deployment of components, adequate automated tests must be written, etc. In practice this state can be difficult to achieve when you’re starting from a messy, legacy code base.

In this particular instance a team was trying to move to continuous deployment, but they wanted to take baby steps to get there. They were asking us to build legacy features into our shiny new continuous deployment tooling that would allow them to use some of the tools while still running a bunch of manual tests, batching up changes, and deploying infrequently. My boss has an amazing knack for analogies, and he described the situation this way:

“We’re trying to sell modern medicine. We’ve built a beautiful new hospital that people are excited about. But some people can’t fully justify the cost of buying in, and now they’re starting to come to us and ask us to put rooms in the new hospital for bloodletting.”

There were several problems with building the features that this team was requesting. First, it would enable the team not to make the massive leap that was necessary to change the way that they develop and deploy software. In the world of software engineering, states that are viewed as stepping stones often become much more permanent. Worse yet, it would allow other teams to leverage the features and not make the leap. And finally, the work required to build the features would prevent our team that built the continuous deployment tooling from building cool new features that people who were really doing continuous deployment needed.

Don’t get me wrong, this kind of circumstance is the exception and not the rule. If you think that your users are asking for the wrong thing you should first doubt and question yourself, then question yourself again. But occasionally the impulse is correct and the way to delight a customer you have to not give them what they want. They won’t appreciate it in the short term, but if you’re confident that you’re making the correct call they may thank you down the road.

The Final Nail In The Windows Coffin

I generally boot over to Windows for one of 2 reasons: to play games, or to use Office. The rest of my time is happily spent in Ubuntu. I’ve been under the impression that people generally use Windows because it’s more “polished”. My mother is never going to be able to hack away at the command line or understand the dark magics of device drivers, so she needs the neat and tidy packaging that Microsoft offers. Tonight I decided to upgrade from Windows 7 to 8, and it was the worst experience possible. My motivation was that my Windows 7 installation had developed a weird tendency to BSOD (for seemingly random reasons after some debugging) with the dreaded “Page Fault in Nonpaged Area” message, so I figured I would try a clean OS install and thought I would upgrade in the process to see what Windows 8 is all about.

I started by downloading the Microsoft Windows Update utility, as recommended. I went through the steps and was told that I had two purchase options: Windows 8, or Windows 8 Pro. The former was $120, so I spent a while poking around looking for a way to select that I wanted the less expensive “Upgrade” version. I couldn’t figure it out, so I eventually caved and bought the full meal deal. I’m a firm believer in clean installation for Operating Systems based on some anecdotal past experiences, so I downloaded the ISO and burned a DVD. A few minutes later I was booted into the installation utility and was ready to install.

That’s when I hit my first speed bump. When I selected the appropriate disk, Windows told me that it couldn’t create a new partition or locate an existing one, and that I should check the setup log files for more info. I had everything important backed up to Dropbox, so I tried deleting the partition, formatting, and every other option available to me. I reboot and went through the process again with the same result. Before hunting down where the “setup log files” were, I hit Google on my cell phone and stumbled on this article and tried the command line partitioning utilities that were suggested. I rebooted again, and still no dice. After a lot of tinkering, I ended up having to unplug my other drives including the one where Linux was installed and reboot the computer, and then things magically worked.

I hadn’t ever messed with Windows 8, so I surprised to be greeted by no start button and no immediately obvious way to launch applications. I was told that I needed to activate Windows, and asked to re-enter my Product Key that I had already entered a million times while trying to get the installation working (fortunately by this point I had it practically memorized). When I tried to activate I got an error message telling me that my product key could only be used to upgrade Windows, despite the fact that I had been using Windows 7 just an hour prior, was under the impression that I bought a full non-upgrade version of Windows 8, and didn’t see any clear warnings to this effect during the purchase process. I went back to Google and poked around for a while and found this suggestion on hacking the registry to make activation work anyway, which seemed to do the trick.

Next I tried to change the wallpaper from the ugly flower, and that didn’t work without any obvious error messages. I was able to click on other images, but all I saw was weird flashing behavior in the edges of the window and the background didn’t change. Again per Google it sounds like I may need to wait until a while after activation to change my wallpaper, which is just bizarre.

I started downloading apps, and when I hit the Skype site they sent me over to the Windows App Store to download it. Inconveniently, there was no clear visual indication how to get back to my desktop from the Metro style UI. I started trying to poke around with Metro and was annoyed at how poorly it’s visual metaphor seemed to map to the mouse and keyboard, so I searched around for a way to permanently disable Metro for desktop users. Unfortunately that seems to require downloading (or in most cases purchasing) a separate application, which seems absurd.

The icing on the cake was that on my next reboot, I again hit the new and slightly less ugly BSOD with the same error that I was getting before. Both the Windows and Linux memory and disk analysis tools seem to suggest that all is fine on the hardware front, and I have yet to have any issues with Ubuntu which is running on the same machine down to the disk. I guess I’m back to trying to troubleshoot that issue later tonight.

After multiple hours of just trying to get things up and running, I’m to picture my mom buying the latest version of Windows because of “ease of use” and having to run disk partitioning utilities from the command line and edit registry keys. Clearly that ain’t happening. I’m also flashing back to how seamless and straightforward installing Ubuntu was last time around. If my experience isn’t atypical, then I think the final nail has been driven into the Windows coffin. That may sound like a sensational claim, but Windows has already lost the battle for mobile to Android (and to a lesser extent these days, iOS) and more and more of computing is moving away from the desktop. At some point individuals and companies that do use desktops for niche activities aren’t going to be willing to pay $120 for an inferior product to something that they can get for free, particularly if they’re already having to retrain habits because existing UI conventions are already broken in any option.

I’m excited that Steam is out for Linux because it feels like that may start a movement for PC games to ship on non-Windows operating systems. Now if only I can get Office working with Wine, I will never have to boot over to Windows again…

String Alignment, Dynamic Programming, & DNA

Most modern runtime environments provide an implementation of some basic functionality for working with strings. We’re used to leveraging simple tasks like splicing together strings or checking whether one string contains another, but what if we wanted to do something more complex and determine whether two strings have a shared etymology or are likely to have a similar meaning? The problem is interesting for a few reasons. First, there are some very interesting applications for predicting how closely related strings are. In terms of spoken and written language, being able to glean information about the evolutionary origin of words can serve as one building block for more complex problems like both natural language processing and translation. There are also very important uses for this kind of algorithm in related fields like DNA sequencing, and even in comparing the source code of programs. Second, the algorithms at the core of the solution to the problem provide a neat example of Dynamic Programming, which is a handy tool for any developer to have in their toolbox.

To get started, let’s consider a simple real world example. Suppose that we need to programmatically decide whether the English word “dictionary” and the Spanish word “diccionario” are related in order to draw some conclusions about the meaning of each word in absence of a… well, dictionary. We begin by noting that in practice, words tend to evolve over time through the insertion, deletion, and mutation of characters. To capitalize on this observation, we need to start by trying to find the most likely alignment between the two words by inserting gaps to make similar characters match up. The first step in finding that alignment is to establish a scoring system between characters in each word. The scoring system should be based on data and should reward similarity while penalizing differences between the strings. For our example, let’s start with a very simple scoring system where letters that match exactly are worth +2, vowels that don’t match are still worth +1 (and we’ll include the character ‘y’ with vowels), and letters that are mismatched (or matched with gaps) are worth -3. This scoring system takes advantage of a somewhat anecdotal observation that vowels tend to turn into other vowels more frequently than other mutations.

Given these words and the scoring system, our brains can actually scan the two words rather intuitively and posit a valid alignment. We can represent the alignment by showing the two words with dashes inserted for gaps, and a third row where letters indicate a direct match and plus signs indicate a non-match that was still assigned a positive score:


Our alignment has 8 letters that match perfectly and a score of 11, and at a glance it seems like a very strong match. It also happens to be a global alignment, which I’ll touch on later. The gap in “dictionary” is also somewhat arbitrary in that the score would be identical if the gap followed the ‘y’ instead of preceding it, which means that in this case there are multiple alignments that are equally valid.

So we’ve produced an alignment, but how could we automate the alignment finding process? To take things a step further, how can we calculate the probability that the alignment is meaningful and not just coincidence? Let’s deal with the former problem first. At first blush, it looks like we could just implement a recursive top down algorithm that finds the optimal alignment by drilling down to individual characters and then building back up by adding together the maximum scoring alignments of each possible combination of substrings and gaps. If we want to align “dictionary”, we could first find the possible alignments and scores of ‘d’ and ‘i’ and then find the alignments and scores for “di” by picking the from individual character alignments and gaps, and so on. The problem with this approach is that we would end up considering each character in the first string with each character in the second string, and then we would also consider each combination of characters in the first string with each combination in the second string. The result is a less than desirable runtime complexity of O(n^2m) for strings with length n and m, respectively. Unfortunately that won’t scale well with bigger strings (like DNA, which we’ll touch on later) when we have to run a large number alignments to try to calculate the probability that the alignment is significant (which we’ll also touch on later).

An alternative is to use Dynamic Programming (DP): breaking the problem up into smaller sub-problems and solving from the bottom up. The beauty of DP is that we can optimize so that we don’t have to redo some of the simple calculations that get repeated with recursion, and we can also take advantage of nuances of the problem in the way that we setup the algorithm. For string alignment the particular nuance that we can capitalize on is the fact that subsequent characters of the string must occur in order, so at each character we can only either insert the next character or insert a gap in one string or the other.

I’ll pause briefly before explaining the DP algorithm to clarify that there are two flavors to string alignment. Global alignment requires that we use each string in it’s entirety. Local alignment requires that we find only the most aligned substring between the two strings. The most widely used global alignment algorithm is called Needleman-Wunsch, while the local equivalent is an algorithm called Smith-Waterman. Both algorithms are examples of DP, and begin by building up a two dimensional matrix of integers with dimensions of the size of each respective string plus one (because we can start each aligned string with either a character or a gap). I’m going to use the local alignment algorithm in my example, but global alignment is very similar to implement in practice.

The first step in finding a local alignment is building up the matrix of integers, where each location in the matrix represents a score as we traverse the alignment. We start by initializing the values along the top and left of the matrix to zero, since local alignment can start at any point in the string. If we were finding a global alignment we would have initialized the edges so that at each subsequent location we imposed the mismatch or gap penalty, because we would need to include the entire string in the result. Next we start with the top left corner that remains and work either right or down (it doesn’t matter as long as we stay consistent), and at each location we enter the maximum of four values: the value to the left plus the score of the top character and a gap, the value to the top plus the score of the left character and a gap, the value up and left plus the score of the characters to the top and left, or zero. If that explanation isn’t clear or the intuition isn’t there, consider what each decision is implying with respect to the alignment that we’re scoring. The first two options imply inserting a gap in one of the strings, the third implies aligning two characters, and the forth implies ending a local alignment. For our contrived example and scoring system, the matrix looks like the following:

  – D I C C I O N A R I O
– 0 0 0 0 0 0 0 0 0 0 0 0
D 0 2 0 0 0 0 0 0 0 0 0 0
I 0 0 4 1 0 2 0 0 0 0 2 0
C 0 0 1 6 3 0 0 0 0 0 0 0
T 0 0 0 3 3 0 0 0 0 0 0 0
I 0 0 2 0 0 5 2 0 0 0 2 0
O 0 0 0 0 0 2 7 4 1 0 0 4
N 0 0 0 0 0 0 4 9 6 3 0 1
A 0 0 0 0 0 0 1 611 8 5 2
R 0 0 0 0 0 0 0 3 81310 7
Y 0 0 0 0 0 0 0 0 51010 7

Apologies if my somewhat crude tables don’t format well on your device, I was lazy and copied/pasted them from program output without applying a lot of formatting. You can see that building up the matrix takes O(nm) time for strings with length n and m, so we’ve reduced our runtime complexity by an order of magnitude.

The next step is retrieving the alignment from the matrix we start with the location with the maximum value, which we can store as we build up the matrix. From that location we trace backwards and figure out which action resulted in that value by considering the locations to the top, left, and top left of the current location and calculating which value was a valid part of the calculation for the current value. In some cases there will be multiple valid alignments, as indicated by either multiple starting locations with identical scores or multiple valid locations that could have contributed to the calculation for the current location. In that case it’s important to consider the context and decide whether it makes sense to return multiple alignments, pick a single alignment arbitrarily, or create a criteria to pick between alignments (for example penalizing longer alignments). Since we’re finding a local alignment, we trace until we reach a location where the score is equal to or less than zero. If we were doing global alignment we would use the same logic, but we would start in the bottom right location and go until we reach the top left location. The same matrix below shows our trace back with the path that we’ve selected in bold:

  – D I C C I O N A R I O
– 0 0 0 0 0 0 0 0 0 0 0 0
D 0 2 0 0 0 0 0 0 0 0 0 0
I 0 0 4 1 0 2 0 0 0 0 2 0
C 0 0 1 6 3 0 0 0 0 0 0 0
T 0 0 0 3 3 0 0 0 0 0 0 0
I 0 0 2 0 0 5 2 0 0 0 2 0
O 0 0 0 0 0 2 7 4 1 0 0 4
N 0 0 0 0 0 0 4 9 6 3 0 1
A 0 0 0 0 0 0 1 611 8 5 2
R 0 0 0 0 0 0 0 3 81310 7
Y 0 0 0 0 0 0 0 0 51010 7

At this point we’ve got an alignment and a score of 13, which is pretty rad. But how can we tell whether the score is statistically significant? Clearly we can’t make hard and fast rules, because the number that the score produces is relative to a lot of factors including the size of the matrix and the alphabet that was used. One option is to create a bunch (in practice a bunch usually means a minimum of 10^4) of random permutations of one of the strings that reuses all the characters and is the same length, align them with the other string, and then see how many alignments score equal to or better than the initial match. The resulting “P-Value” can be calculated by taking the number of better matches plus one divided by the number of attempts plus one (because we include the initial match in our set). Interpretation of the value depends on the context, but in our case we may decide that a probability value of .5 may indicate that the relationship is random while a value of 10^-4 may suggest a more compelling relationship between the strings. For the given example I tried running 10^5 different alignments while randomizing a string and didn’t see a single alignment with a score that was equal or better, which isn’t a surprise because the alignment that we produced is almost a direct match. The obvious conclusion is that the words almost certainly did evolve from the same origin at some point in human history, and thus likely share meaning.

Now that we’re armed with our nifty algorithm, where else can we put it to use? Some of the more interesting applications for string alignment occur when trying to sequence and compare DNA. At the end of the day, DNA is just a long string that consists of a 4 letter alphabet of nucleotides. DNA produces RNA, which in turn also produces proteins that are made up of a chain of amino acids. Each protein is also just a string that consists of a 20 letter alphabet of amino acids. DNA (and as a result, the proteins that it produces) has evolved through a series of insertions, deletions, and mutations just like written or spoken language. DNA evolution is actually much more pure than that of written and spoken language, because it’s much more difficult for an entire section of DNA to instantly mutate than it is for a brand new word to become adopted in a culture. A gene is the DNA equivalent of a word: it’s a section of DNA that is ultimately responsible for the production of a protein that may contribute to a physical trait. By aligning either DNA or protein sequences, we can make very educated guesses about whether genes are evolutionarily linked, and whether the proteins that they produce fold and function similarly. By looking at highly conserved regions of related proteins over time and making statistical observations about the likelihood of certain amino acids and common mutations, researchers have produced scoring systems like BLOSUM that are very effective when comparing proteins.

Another interesting application for our algorithm is in examining iterations to the source code of a program, or comparing multiple implementations of a program. A program is really just a string where the alphabet is generally defined by the context free grammar of a programming language. Suppose that we want to try to figure out whether a student in a computer science class copied his assignment from a peer, or did the work on his own. In this case we can simply align each two programs, look at the probabilities that the programs are related, and apply some manual investigation and common sense investigation to any results that appear closely related.

That’s a lot of information to pack into a single post, but I hope you find it helpful and are able to think of other creative applications for the algorithms and concepts. As always, I’ll look forward to reading your comments!

Getting Rock Stars Excited About Working For You

The most important thing that the manager of a software development team can do is to staff their team up with rock star developers. The “10x Software Development” principle that Steve McConnell and others brought to the mainstream is the biggest reason why: the best developers aren’t just twice as effective as average ones, they’re an order of magnitude better. This is old news, and we’ve all heard it before by now (and on a side note, it’s also why I’ve previously harped on the importance of building a network of talented folks). What isn’t old news is that there are a few straightforward steps that managers can take to maximize their chances of landing those amazingly talented developers.

I’ve managed teams at both Microsoft and Amazon. I’ve recruited the cream of the software development crop against virtually every other big tech company, exciting start-ups, and even against other teams within my company. I’ve won more of those battles than I’ve lost, and I’ve learned my share in the process. I’m going to share a few very tangible suggestions that you can put into practice today that I humbly submit will make you more effective at winning your own recruiting battles. Most of the tips are equally relevant whether you’re managing a team within a huge company, or hiring employee #1 at a start-up operating out of your garage.

To set the stage, let’s start by focusing on a scenario that you’re likely to come across in the recruiting process. Suppose that you’ve been fortune enough to find a rock star developer who’s interested in your team. She breezed through your interview process, and your HR folks (who incidentally may also be you at a small company) have reached out to the her and started to negotiate compensation. The word comes back from HR that the developer is interested in joining your team, but she has other offers out from two other companies with comparable compensation and she’s having a hard time making a decision. The good HR peeps inform you that you’re at the absolute top the compensation range, so there isn’t any way to sweeten the offer and blow her away with dollar bills. They’ve lined up a sell call for you to chat with the developer, answer her last few questions, and get her jazzed about your team. This is a position that I’ve personally been in at least 10 times over the past year.

In this situation there are two sets of inputs that will determine whether the candidate accepts your offer. The first is a set of things that are mostly out of your control at this point, for example: the brand image of the company that you work for, the location of the team offices, differences in the compensation packages being offered, and the appeal of the projects that each team is working on based on that developer’s particular experiences and interests. The second set is in your control, for example: the way that you come across in the phone conversation and any other communication with the candidate, the way that you choose to paint a picture of your team, and how well you and the person connect. The following are tips that I’ve found effective at maximizing that second set of inputs that are in your control and increasing your odds of landing a rock star:

  • Prepare a one pager that sells your team, today. Leave the stuff that isn’t sexy out. Don’t talk about your ops rotation or that legacy product that your team has to keep on life support. You should field questions about those areas honestly when asked, but the one pager isn’t the place to do so. Sell the long term team vision, and make it something that good developers want to sink their teeth into. Emphasize hard technical challenges. Be sure to call out who your customers are and what the impact of the technology is for them, and consider including a powerful quote from at least one of satisfied customer. Email the one pager to the candidate as soon as possible once you’ve decided to hire them and before the sell call to keep them thinking about your team and help them formulate follow up questions for the phone call.
  • Come to the phone call prepared. Know the candidates resume and background inside and out. You studied this extensively prior to interviewing the candidate, but be sure to refresh your memory immediately before the call. Understand that when you chat, you shouldn’t feel the need to cover absolutely everything that your team is doing; in most cases that will be more than a person can digest in a single phone call. Create a list of no more than 3 high level bullet points that you want to be absolutely sure to cover. Tailor those bullet points to the kind of technologies that the candidate is likely to be excited about.
  • Spend the first 5-10 minutes of the phone call learning more about the candidate. Be sure that you’ve accurately identified their interests. Be ready to adjust your message on the fly to emphasize the points that jive with their interests. Ask probing questions to show them that you’re really interested in what they care about, and to be sure that you’re correctly identifying their interests.
  • Walk around while you’re talking. Put the phone on speaker, or get a headset. Use your arms and other body language, even though the candidate can’t see you. It’s easier to speak naturally and get excited about what you’re talking about if you’re not chained to a desk and phone. Your enthusiasm about the team and products is absolutely key to getting the candidate excited, and it’s harder for most people to convey over the phone than in person. It’s also something that is very difficult to fake if you’re working on a product that you aren’t a fan of.
  • Try to take the phone call to a place where it turns into a technical discussion between you and the candidate. If you can get to a point where they’re excited about the technology, making suggestions, and engaging in a dialogue about where they would take the technology you’re in great shape. Intentionally nurture that kind of discussion.
  • End the call by answering questions and offering to connect them with additional resources. I personally always provide my contact info and encourage the candidate to get in touch with me if any future questions come up. I also offer them a chance to schedule a follow up phone call with a senior engineer from the team (that I trust to represent the team well) to dive further into the technical weeds if the candidate is interested. They will only rarely take you up on that offer, but making the offer still goes a long way to establish the technical credibility of the team.
  • If the opportunity presents itself, follow up with something memorable. When I was contemplating Amazon my baby was just a few months old, and the hiring manager sent me an Amazon branded onesie. We sometimes send candidates Kindles to keep Amazon front and center in their mind and show them how much we want them to join us. I had a candidate a few months ago that wanted to join the team but was hesitant about the weather in Seattle, so I took a picture out the office window on the next day (which happened to be beautiful and sunny) and emailed it to him. Anything that keeps your team and company front and center in the candidate’s mind is awesome. Be sure not to force it if the opportunity doesn’t present itself, because that kind of attempt will likely come across as desperate and produce the opposite effect.

I hope you find those ideas helpful and are able to implement them effectively, unless I happen to be managing the team that you’re competing against for that next rock star developer. 🙂 I’ll certainly welcome feedback and/or additional suggestions from others in the comments below. I’ll add the usual disclaimer that these ideas are all my own, and don’t reflect any of the opinions of my current or former employers.

Debunking the Myths of RPC & REST

The internet is chock-full of articles, blog posts, and discussions about RPC and REST. Most are targeted at answering a question about using RPC or REST for a particular application, which in itself is a false dichotomy. The answers that are provided generally leave something to be desired and give me the impression that there are a slew of developers plugging RESTful architectures because they’re told that REST is cool, but without understanding why. Ironically, Roy Fielding took issue with this type of “design by fad” in the dissertation in which he introduced and defined REST:

“Consider how often we see software projects begin with adoption of the latest fad in architectural design, and only later discover whether or not the system requirements call for such an architecture.”

If you want to deeply understand REST and can read only one document, don’t read this post. Stop here and read Fielding’s dissertation. If you don’t have time to read his dissertation or you’re just looking for a high level overview on RPC and REST, read on. To get started, let’s take a look at RPC, REST, and HTTP each in some detail.

Remote Procedure Call (RPC) is way to describe a mechanism that lets you call a procedure in another process and exchange data by message passing. It typically involves generating some method stubs on the client process that makes making the call appear local, but behind the stub is logic to marshall the request and send it to the server process. The server process then unmarshalls the request and invokes the desired method before repeating the process in reverse to get whatever the method returns back to the client. HTTP is sometimes used as the underlying protocol for message passing, but nothing about RPC is inherently bound to HTTP. Remote Method Invocation (RMI) is closely related to RPC, but it takes remote invocation a step further by making it object oriented and providing the capability to keep references to remote objects and invoke their methods.

Representational State Transfer (REST) is an architectural style that imposes a particular set of constraints on an interface to achieve a set of goals. REST enforces a client/server model, where the client is interested in gaining information and acting on a set of resources that are managed by the server. The server tells the client about resources by providing a representation of one or more resources at a time and providing actions that can either get a new representation of resources or manipulate resources. All communication between the client and the server must be stateless and cachable. Implementations of a REST architecture are said to be RESTful.

Hypertext Transfer Protocol (HTTP) is a RESTful protocol for exposing resources across distributed systems. In HTTP the server tells clients about resources by providing a representation about those resources in the body of an HTTP request. If the body is HTML, legal subsequent actions are given in A tags that let the client either get new representations via additional GET requests, or act on resources via POST/PUT or DELETE requests.

Given those definitions, there are a few important observations to make:

  • It doesn’t make sense to talk about RPC vs REST. In fact you can implement a RESTful service on top of any RPC implementation by creating methods that conform to the constraints of REST. You can even create an HTTP style REST implementation on top of an RPC implementation by creating methods for GET, POST, PUT, DELETE that take in some metadata that mirrors HTTP headers and return a string that mirrors the body of an HTTP request.
  • HTTP doesn’t map 1:1 to REST, it’s an implementation of REST. REST is a set of constraints, but it doesn’t include aspects of the HTTP specific implementation. For example your service could implement a RESTful interface that exposes methods other than the ones that HTTP exposes and still be RESTful.

What people are really asking when they ask whether they should use RPC or REST is: “Should I make my service RESTful by exposing my resources via vanilla HTTP, or should I build on a higher level abstraction like SOAP or XML-RPC to expose resources in a more customized way?”. To answer that question, let’s start by exploring some of the benefits of REST and HTTP. Note that there are separate arguments for why you would want to make a service RESTful and why you would want to use vanilla HTTP, although all the arguments for the former apply to the latter (but not vice versa).

The beauty of REST is that the set of legal actions from any state is always controlled by the server. The contract with the client is very minimal; in the case of HTTP’s implementation of REST the contract is a single top level URI that I can send a GET request to. From that point on the set of legal actions is given to the client by the server at run time. This is in direct contrast to typical RPC implementations where the set of legal actions are much more rigid; as defined by the procedures that are consumed by the client and explicitly called out at build time. To illustrate the point, consider the difference between finding a particular product by navigating to a webpage and following a set of links (for things like product category) that you’re given until you find what you’re looking for, versus calling a procedure to get a product by name. In the first case changes to the API can be made on the server only, but in the second case a coordinated deployment to the client and server is required. The obvious issue with this example is that computers aren’t naturally good at consuming API’s in a dynamic fashion, so it’s difficult (but possible in most cases) to get the benefit of server controlled actions if you’re building a service to be consumed by other services.

One main advantage of using vanilla HTTP to expose resources is that you can take advantage of a whole bunch of technologies that are built for HTTP without any additional effort. To consider one specific example, let’s look at caching. Because the inputs of an HTTP response are well defined (querystring, HTTP headers, POST data) I can stand up a Squid server in front of my service and it will work out of the box. In RPC even if I’m using HTTP for message passing under the hood, there isn’t any guarantee that messages map 1:1 with procedure calls, since a single call may pass multiple messages depending on implementation. Also, the inputs to RPC procedures may or may not be passed in a standard way that makes it possible to map requests to cached responses, and requests may or may not include the appropriate metadata for to know how to handle caching (like HTTP does). It’s nice to not have to reinvent the wheel, and caching is just one example of an area where HTTP gives us the ability to stand on the shoulders of others.

Another advantage to HTTP that I’ll just touch on briefly is that it exposes resources in a very generic way via GET, POST/PUT, and DELETE operations which have stood the test of time. In practice when you roll your own ways to expose resources you’re likely to expose things in a way that is too narrow for future use cases, which results in an explosion in the number of methods, interfaces, or services that end up becoming a part of your SOA. This point really warrants a blog post of it’s own, so I’ll avoid going deeper in this particular post.

There is a lot of additional depth that I could go into, but in the interest of keeping this post reasonably brief I’ll stop here. My hope is that at least some folks find the material here to be helpful when deciding how to best expose resources via a service! If I’ve missed anything or if you feel that any of the details are inaccurate, please feel free to comment and I’ll do my best to update the material accordingly.

The New Platform War

There’s a new battle raging for customer eyeballs, application developers, and ultimately… dollar signs. To set the stage, flash back to the first platform war: the OS. Windows sat entrenched as the unassailable heavyweight, with Linux and Mac OS barely on the scene as fringe contenders. But the ultimate demise of Windows’ platform dominance didn’t come from another OS at all; it came from the move to the browser. Microsoft initially saw the problem and nipped it in the bud by packaging IE with Windows, then tried to prolong the inevitable by locking the IE team away in a dark basement and trying to stifle browser innovation by favoring closed solutions for browser development like Silverlight instead of open standards like HTML 5. That strategy clearly wouldn’t work forever, and the net result was a big boost in the market share of competing browsers like Firefox and ultimately Chrome. Suddenly people weren’t writing native Windows apps anymore, they were writing applications that ran in the browser and could run on any OS.

The pattern of trumping a dominant platform by building at a higher level has repeated itself many times since. In some sense Google subverted platform power from the browser by becoming the only discovery mechanism for browser apps. When social burst onto the scene Facebook and Twitter became king of the hill by changing the game again. The move to mobile devices has created a bit of a flashback to the days of OS platform dominance, but it’s inevitably a temporary shift. At some point history will repeat itself as devices will continue to become more powerful, standards will prevail, and developers will insist on a way to avoid writing the same app for multiple platforms.

Which brings us to today, as the platforms du jour are again threatened. In this iteration the challenger to the dominance of Facebook and Twitter is the domain specific social apps that are built on top of them. When social network users share their status with friends, text + images + location isn’t enough anymore. Different kinds of activities call for customized mechanisms of data entry and ways to share the data that are tailored for the experience. For instance, when I play 18 holes of golf I enter and share my data with Golfshot GPS, which makes data entry a joy by providing me yardages and information about the course and gives my friends the ability to see very granular details on my round when I share. When I drink a beer I share with Untappd, when I eat at a restaurant I share a Yelp review, if I want to share a panoramic view I use Panorama 360. Even the basic functions like sharing photos and location work better with Instagram and Foursquare than Facebook’s built in mechanisms.

The social networks will never be able to provide this kind of rich interaction for every experience, and they shouldn’t attempt to. At the same time they run the risk of the higher level apps becoming the social network and stealing eyeballs; a position which some apps like Foursquare clearly already have their eyes on. For power users these apps have already made themselves the place to go and enter domain specific data. That trend will continue to expand into the mainstream as people continue to dream up rich ways to capture real life experiences through customized apps. To use the OS analogy: there’s no way that Microsoft can dream up everything that people want to build on top of Windows and bake it into the OS, nor would it be a good thing for consumers if they could.

It will be interesting to see how Facebook and Twitter respond to the trend. I suspect that users will continue to move towards domain specific apps for sharing, but that the social networks will remain the place to browse aggregated status for friends across specific domains. Unless, of course, the owners of the highest profile apps somehow manage to get together and develop an open standard for sharing/storing data and create an alternative browse experience across apps to avoid being limited by the whims of Facebook and Twitter and the limitations on their APIs.

A Strategy For The Dreaded Interview Coding Question

If you’re a software developer and you’re thinking about changing jobs, you’re probably at least a bit anxious (if not downright freaked out) about the prospect of facing a whiteboard armed with only a trusty dry erase marker and your wits while an interviewer fires a coding question at you. That’s not shocking because software development interviews are weird: the skills necessary to answer the technical and behavioral/situational questions that are asked don’t necessarily map 1:1 with the skills to be a good developer. We’re used to developing with access to tools like IDE’s and StackOverflow, without unnatural time constraints and the pressure of landing a job in the balance. I’ve interviewed literally hundreds of candidates in my roles as a manager both at Microsoft and Amazon, and I’ve seen hundreds bomb coding questions. That doesn’t shock me for the reasons previously mentioned, but what does shock me is the number of bright folks who fail on the questions simply because they don’t approach them with a solid strategy.

The anti-patterns are crystal clear and they almost always lead to a wipe, for example: diving straight in on code, assuming that language/syntax doesn’t matter, or failing to consider edge cases before implementation. To avoid these pitfalls, I recommend that every interviewing developer should practice the following strategy before going into interviews and put it into practice (without fail, no matter how simple the question seems) during the process.

Restate the problem and ask clarifying questions.

Repeating the problem in your own words and asking some follow up questions only takes a second, and it’s a good way to quickly tease out any bad assumptions that have been made. It also gives the interviewer confidence that you’re used to attacking real world coding tasks the right way: being sure that you’ve correctly interpreted requirements and thinking through questions that impact various potential approaches. Ask how important optimization is instead of just assuming that implementing the naive solution is bad. Ask what you should optimize for, for example quickest execution speed or smallest memory footprint.

Walk through a basic example in detail and consider a few edge cases.

Take the time to think through at least one straightforward case, as well as a few relevant edge cases. Talk through your thought process as you’re going through them, utilizing the whiteboard as much as you can. Consider null or zero length inputs. Consider very large inputs, and be prepared to answer questions about whether your implementation would fit in memory on specific hardware given specific inputs. The process of walking through these cases should get you very close to pseudocode.

Write up pseudocode.

Be sure that you’re not writing in a real programming language. Pick a spot on the board where you don’t have to erase your pseudocode when you start to write real code, and will be able to read it. Lots of interview questions require thinking about recursive versus iterative implementations, so it doesn’t hurt to always consider whether that question is in play if it is relevant to the problem. Don’t abandon the pseudocode to dive into real code until you have completed the problem. Be sure to continue the dialogue with the interviewer while you’re thinking, and show that you can listen and course correct given hints.

Pick a language, and ask how important syntax is.

Always assume that for actual implementation, the interviewer cares about the details. I’m generally not a stickler for small syntactical minutia, but I get annoyed I get when an interviewer just assumes that it’s alright for the final implementation to be in pseudocode or some hodge-podge of languages. If you decide to code in a language other than the one that you indicated that you’re the most comfortable with on your resume, be sure to explain why. Asking how much the interviewer cares about syntax can help you decide whether to take an extra pass at the end of the loop being sure that everything is spot on; if the interviewer doesn’t care they may see it as a waste of precious time.

Code it!

You’ve done all the hard work, getting from pseudocode to your language of choice should be fairly trivial.

It’s important to remember that a typical interview in a loop will run 45-60 minutes, and most interviewers are going to want to touch on more than a single coding question. The expectation is most likely that you can complete the question in 20-30 minutes, so be sure that you’re not spending a ton of time on each step. A lot of interviewers will tell you that they’re not necessarily looking for you to get to a fully working implementation, but don’t believe them. If you don’t get code up on the board or stall out they will definitely ding you. Don’t spend more than a few minutes restating the question and walking through edge cases. The bulk of your time should be spent in an even split between pseudocode and code.

The beauty of following this strategy is that you will come across as organized and informed even if you don’t understand the question. It also provides an opportunity to work with the interviewer through follow up questions while running through examples and pseudocoding. Remember, the interviewer knows the answer to the question and they probably want to get you hints as you move in the right direction, so engaging them and using them as a resource is critical. Hope that these ideas help, now go nail that interview loop!

The Physical Versus The Digital

I don’t want to buy things twice. I’m even more hesitant to pay again for intellectual property, which costs little or nothing to clone. I don’t want to buy Angry Birds for my iPhone, Kindle Fire, PC, and Xbox 360. I’m even crankier about buying digital goods when I’ve already bought the IP via physical media. I want the convenience of reading my old college text books on my Kindle without buying them again, and I shouldn’t have to. I hate the dilemma of trying to figure out whether to order my grad school textbooks digitally (because it’s lightweight, convenient, and portable) or not (because the pictures render properly, it’s handier to browse, and looks cooler on the shelf). Maybe I’m in the minority here, but I’m also too lazy to consider buying and setting up a DIY Book Scanner.

Anyone who reads, plays games, or listens to music has shelves or boxes of books, NES cartridges, or CD’s that they probably don’t use often and don’t know what to do with. I would love the option to fire up RBI Baseball or reread Storm of Swords on modern devices with the push of a button, but it’s not worth storing the physical media and/or keeping obsolete devices around.

My frustration has caused me to conclude the relatively obvious: some company needs to offer a way to send back physical media along with a nominal fee in trade for the digital version. The physical media could be resold second hand or donated to charitable causes, and the folks ditching their physical media could access the things that they have already paid for in a more convenient format. Amazon is the one company that seems poised to make this happen given that they deal in both physical/digital and they have efficient content delivery mechanisms in place for goods of both kinds. Is there a financial model that makes swapping physical for digital work for all parties involved, and is it something that will ever happen?

Great Documents: A By-Product of Effective Software Development Managers

If you’re managing people who develop software you should probably be spending a nontrivial portion of your time writing documents, and the quality of those documents is critical. Documents matter because there are several questions that every manager needs to answer for their management chain:

  • What’s current state of the union?
  • Where are we headed over the next 12-36 months?
  • What level of staffing do we need to achieve that vision?
  • Are my employees compensated appropriately?

Managers also need to answer a related and partially overlapping set of questions for their employees:

  • Where is the team headed?
  • What’s my role in helping us get there?
  • How have I been performing?
  • How can I improve my performance, and grow my career?

Several tools are available to answer these questions in the modern business setting, but none are as effective as written documents. Face to face conversations or meetings are less efficient, more random, and can’t be archived for easy consumption after the fact. PowerPoint presentations require the context of a speaker (if they don’t you’re abusing PowerPoint and giving bad presentations) who is only presenting to a limited audience, so they share the archive problem of conversations. Videos or audio recordings of either conversations or presentations are impossible to quickly scan, and it’s more difficult for the person consuming the information to backtrack or skip around as needed. Email or instant messenger conversations are less formal and rigorous than documents by convention, so they allow for glossing over areas where deeper thought and investment is essential. Put simply: creating documents forces a manager to codify thoughts or ideas into an artifact that is easy for others to parse at any point down the road, with an effectiveness that no other process can duplicate.

I didn’t realize the value of written documents until I started working at Amazon almost a year ago. It’s very common at Amazon to walk into an hour long meeting and spend the first 20 minutes in silence reading and marking up a hard copy of a particular document, and spend the remaining 40 minutes discussing it. Initially I found that odd, until I went through the exercise of preparing my first long range planning document for my team and getting it iteratively reviewed by my team, peers, and various levels of my management chain. It took a lot of work, but all of that work ended up being hugely beneficial. We spent extra time meeting with customers to update requirements and get product feedback, held brainstorming sessions with team members and senior engineers who were interested in the space, and did some analysis on the cost of operations and ways that we could optimize some of that overhead. The final product was a 6 page plan that I could hand to anyone and rest assured that after a few minutes of reading they would have a great feel for what my team is up to, and why.

As a quick aside, this is a great example of why I previously wrote encouraging both software developers and managers to change companies/environments over the course of their career. There are a lot of things that I learned at Microsoft that I never could have learned at Amazon. There is an equally long list of things that Amazon does really well that I wouldn’t have learned anywhere else. Throwing yourself in a totally different neck of the woods provides a unique opportunity to grow in areas that you couldn’t have developed by staying put.

Back to documents. To clarify, I’m not talking exclusively about technical documentation like functional specifications, design documents, or test plans. I’m more focused on things like vision documents (which direction things are headed), long range planning documents (the nitty gritty on how to move in that direction), and documents about things like employee growth or promotion readiness. The neigh-sayers will argue against the value of these kinds documents because they aren’t part of what ultimately ships to the customer: the bits and bytes that get burned to a disc or deployed to a server somewhere. I would argue the exact opposite. For example taking the time to produce a long range plan that you can hand to engineers, customers, and partners can help you avoid building meaningless features, and help customers and partners give earlier feedback on where you’re headed. Similarly, taking the extra time to prepare a document evaluating an employee’s readiness for promotion is a great way to keep that employee apprised of growth areas, ensure that the employee is happy in their career progression and nip problems in the bud, and in the end save you from reduced productivity while back-filling for the attrition of an unhappy team member.

So without further ado, here are a few tips that I think will make you better at producing high quality documents:

Define your audience before you start.

In most cases it’s not possible to effectively address multiple audiences in a single document. Before you put pen to paper, define your audience. If the audience seems too broad, consider writing multiple documents instead of one. For example if you’re writing a document that tells what your team will deliver over the next 12 months, it may be appropriate to have 2 flavors of the doc: one for your management chain, and one for your customers. Your managers may want to know some of the dirty details like how much time your team spends on operations or how much test debt you need to backfill, but your customers may only care about what new incremental features your team will deliver. I’ve also seen cases where authors don’t define their audience right out the gate where the end result is a document that’s not really meaningful to any group of people.

Make bold claims. Don’t use weasel words. Be specific about dates.

Weasel words kill the impact of a document, and are a mechanism to avoid hard thinking or research that needs to happen. Consider a sentence like “Implementing this feature will represent a significant win for customers.” The sentence begs the questions: what feature, how significant, and what kind of win? Now consider the impact of rewriting it to “Implementing the features to allow multi-tenancy will allow customers to reduce the size of their fleet by 50%, resulting in a million dollar reduction in TCO.” Note that getting from A to B requires a lot of research, but the result is a statement that is much more impactful and makes it easier to gauge the value of the feature in question.

It’s equally important to be specific about dates. For example when you read something like “The feature will be completed later this year”, you should automatically ask the question: when this year? Are we talking next week, or late December? If my team has a dependency on your feature, I’ll need some more granular deets. If it’s impossible for some reason to provide a date, then provide a date by which you’ll have a date.

Finish Early, Allow Bake Time

This is critical. If your document is due in 3 weeks, plan to complete it in 1. Before you write the document you should identify peers that you want to read it, ping them to be sure that they block out time to do so, and then be sure to get them a copy on schedule. Consider iterative rounds of reviews with different groups of people that are stakeholders for the document. For example if you’re a line manager creating a vision document for your team you may want to start by getting it reviewed with a few of your peers and senior engineers, then take it to folks up your management chain, and then review the document with a few key customers. In my experience the resulting document is often drastically different (read: drastically better) than the original version.

Review, and review again. Use hard copy.

On a similar note, review your work often. Don’t write a document in one shot and call it good. When you finish it, step aside for a day and then read it afresh. Print the document out and review it in hard copy, pen in hand (and then go plant a tree). Staring at a piece of paper puts your brain in a different mindset than staring at Word on the computer screen. When you’re staring at your screen your mind is thinking rough draft, or work in progress. When you’re staring at ink on paper your mind is thinking finished product. You’re more likely to be a good editor of your document in the latter mode.


This isn’t an exhaustive list by any means, but it does include the tips that I’ve personally found to have the biggest impact on document quality. At some point I may put together a follow up list with some additional ideas on writing docs that I’ve excluded from this post. I personally apply these ideas to everything I write, including blog posts like this one. I hope that you find this helpful, and if you have additional ideas on either the value of documents or how to produce great ones I would love to hear them in the comments!