This Blog Has Moved

This blog has moved to http://tghw.com/. To read past articles, please use the new site.

Posted December 10, 2011 - View Comments

Lean Development: From Zero to Launch in Six Weeks

Yesterday, we just launched a new product at Fog Creek, WebPutty.

Since this product came about in a fairly unique way for Fog Creek, I wanted to just give a quick account of everything that was involved in taking this project from nothing to completion.

The true start of the project was the middle of May, when plans for Dane and me to start a new product over the summer with three interns were nixed, and instead we were asked to spend a couple of weeks brainstorming new, more adventerous product ideas.

We spent two or three weeks coming up with a few dozen ideas, ranging from solid suggestions we'd already talked about to some crazy, off the wall, we-can-never-break-into-that-market ideas. Each week, we presented the ideas we'd come up with to Joel and Michael. Some of them got panned, some of them got marked as possibilities, but none of them really seemed to jump out. We also started bringing in a few prototypes, since we were both starting to get sick of brainstorming and were itching to get back into code.

Towards the end of those weeks, a former Creeker (and good friend) Jason stopped by the the office while waiting for his new boss to go on the Colbert Report. We chatted with him about some of our ideas, some cool stuff he was doing with LESS, and tools like JSFiddle. During the conversation, another idea occurred to me: we could make a tool like JSFiddle for editing the styles on your website that also hosted it, so you'd get a real-time preview of your changes, and, when you were ready, you would just have to click a button to make the new style live.

The next day, the both of use feeling particularly antsy, we decided to start prototyping what we had unoriginally code-named "CSSFiddle". We had already done one or two other prototypes using Google App Engine with Flask, so we created a new project and got coding. By the end of the day we had a very basic two-pane layout, one for the editor and one for the live-preview, and wired it up so the previews would actually work.

The next week, we presented several small prototypes to Joel and Michael. They seemed to like CSSFiddle, so we continued working on the prototype. That week we added organizational features, like grouping of styles, pages, and sites, and administrative controls for managing multiple users on a single site.

By the second week, we were dogfooding CSSFiddle by making it host its own CSS, and we presented it to the whole company. A week after that, we started having our awesome designers take a look at it and remove all of the ugliness Dane and I had infused into it. By week four, we were spending most of our time cleaning up loose ends in the workflow. Week five was spent focusing on user interaction improvements, things like using visual clues to show what state the editor was in. We also obtained WebPutty.net and rebranded the site from CSSFiddle to WebPutty.

By week six*, we were ready to launch. There were a couple of small features that were blocking launch, which we spent Monday and Tuesday completing.

On Wednesday, we did our final round of tests on the live site. Everything looked pretty good. The only possible hitch was that Apple had just released OS X Lion, so we took some time to decide whether our launch announcements might get lost in the noise. Seeing that Lion had not attracted too much attention on Hacker News, we decided to go ahead with the launch.

Dane published his blog post on the Fog Creek blog, while I wrote posts for Hacker News and Proggit. Dan, our marketing lead, started posting on Twitter and pinged Joel on the launch.

Dane's blog post faired very well, staying at or near the top of Hacker News for most of the day. (My posts did not fare so well.) Joel's tweet also did well, and the URL of the blog post got tweeted thousands of times, including some high profile accounts like Smashing Magazine and ReadWriteWeb.

So where does the project stand now? First, we've gotten a lot of good feedback, which means we'll be writing more code to fulfill some of the more popular feature requests. What about monetization? To be honest, we haven't decided on that yet, but even with some huge spikes in traffic, the hosting costs are pretty minimal.

We could have spent a lot longer brainstorming, and maybe we would have come up with a better idea, or maybe we just would have forgotten this good one. We could have spent more time developing the app, adding as a billing system and all the other features we'd planned. But then we wouldn't be anywhere. By getting impatient and diving in, at least we're somewhere. And that's usually better than being nowhere.

 

* Technically, it was week seven, but both Dane and I had taken about a week's worth of vacation during that time, meaning that only 6 weeks of each of our time went into the project.

Posted July 21, 2011 - View Comments

Installing LastPass on the Cr-48 and ChromeOS

A couple days ago I got a new Cr-48 laptop as a part of the Google ChromeOS pilot program. Until now, I hadn't had much time to play with it. Now, I have a few hours, as I was a little late checking in my bag and the Delta representative I was talking to was not willing to help me out. So now that I have the time, I'm getting it set up, so I can use it as my main laptop for this trip home for the holidays.

Browsing for a while, I decided to install LastPass, which I started using to help me improve and manage my passwords after Gawker got hacked. When I went to LastPass.com, I was excited to see that they had a ChromeOS version, and had already detected it. I clicked to download, but ran into an error: "Extension Install Failure: Extensions cannot install plugins on Chrome OS". Well that sucks.

I just figured I would have to use the website, which is less slower and less convenient. Fortunately, after browsing around LastPass a bit, I noticed that they had different versions of the extensions. Some say "Plus Binary Features", while others say "No Binary Features". I tried the binary-less install, and it worked! There are a few things missing, like automatically logging out after a certain amount of idle time, but I have it and it's working as advertised.

To install it, just go to https://lastpass.com/misc_download.php and click "Linux". Then just download "Chrome (No Binary Features)". That's it, now all of your passwords are at your fingertips.

Posted December 22, 2010 - View Comments

Cheeky Python: A Redis CLI

Recently, at work, we started using MiniRedis as a lightweight store for some job queuing on Kiln's backend. We had originally planned on using the full Redis, but because we have to deploy licensed Kiln on Windows, we had to come up with our own solution.

So far, it's been working very well for us. The Redis command set is pretty small and very straightforward, which makes it easy to clone. The only annoyance we've run into is that the stable command line interface (CLI) for Redis only speaks the 1.x protocol, while MiniRedis only speaks the 2.0 version. The redis CLI also does not run on Windows. This is a bit of a problem, since we're still working on our queuing system and we need to do testing.

To get around not having a client to use, Ben would telnet in to the MiniRedis port and type out the commands manually. It ended up looking a bit like this:

I, on the other hand, would fire up Python and, using the redis-py library (which is a very nice client library), issue commands directly from there. Neither option was very convenient.

So one day, tired of having to do all the imports and set up the connection, I decided to put together a CLI using Python.

import cmd

class RedisCli(cmd.Cmd, object):
    pass

if __name__ == '__main__':
    RedisCli().cmdloop()

The basic CLI is very simple. Python's cmd module takes care of all the hard parts for you. If you run this script, you'll get a prompt (Cmd) that supports one command: help. Unfortunately, that's all you have. You can't even exit gracefully. So that's the first thing I added:

class RedisCli(cmd.Cmd, object):
    def do_exit(self, line):
        return True
    do_EOF = do_exit

The cmd module lets you define commands by writing a do_foo() method which takes the line the user typed in. The above code gives you the exit command, and also makes EOF (Unix: Ctrl-D, Windows: Ctrl-Z) exit for you. That's helpful, but it doesn't really add much in terms of actual functionality. For that, we import the Redis client libraries

from redis import Redis

initialize the connection

class RedisCli(cmd.Cmd, object):
    def __init__(self, host, port):
        self.redis = Redis(host=host, port=port)

and add some of the Redis commands

    def do_get(self, line):
        print self.redis.get(line)

    def do_set(self, line):
        key, value = line.split()
        print self.redis.set(key, value)

This is nice, because help will now list get and set. But Redis has many more commands available. One option would be to continue adding all the Redis commands until you had the full set specified and properly parsing the command line. That's pretty time consuming, brittle, and just plain boring. Personally, I don't have the patience.

Fortunately, the cmd module has a default() method which is called for any unrecognized functions. That means we can get rid of do_get and do_set and replace them with:

    def default(self, line):
        parts = line.split()
        print getattr(self.redis, parts[0])(*parts[1:])

Huh? Let me explain: getattr() takes an object and an attribute name, and returns that attribute if it exists. To illustrate, calling getattr(obj, 'foo') is the same as calling obj.foo. In this case, we're assuming the user typed in one of the functions defined in the Redis object. We use getattr to get that function, and then we pass the rest of the arguments to it using Python's *args syntax. This sidesteps the problem of not knowing how many arguments the commands take.

Unfortunately, this approach is prone to errors. For example, the user can type in __init__, which will call the Redis object's constructor again, overwriting our connection. Someone could also try to call foobarbazbat, which does not exist in the Redis object and will throw an error. Lastly, we've also lost our list of commands when you type help.

To fix this, we're going to have to do some spelunking in the Redis object. Fortunately, Python's dir() function returns all of the Redis object's attributes. We can then iterate over them, filter out any that start with an underscore (Python's convention for private attributes) and make sure they're callable. We then use setattr to create a function in our own class that calls into the Redis object and prints the result.

    def __init__(self, host, port):
        super(RedisCli, self).__init__()
        self.redis = Redis(host=host, port=port)
        for name in dir(self.redis):
            if not name.startswith('_'):
                attr = getattr(self.redis, name)
                if callable(attr):
                    setattr(self.__class__, 'do_' + name, self._make_cmd(name))

    @staticmethod
    def _make_cmd(name):
        def handler(self, line):
            parts = line.split()
            print getattr(self.redis, name)(*parts)
        return handler

Notice here that _make_cmd is creating a new function inside of it, and returning that function so we can set the do_foo of our own class to the function that calls self.redis.foo(). Likewise for any callable function in the Redis class.

Now if we type help on our command line, we'll get a list of all functions in the Redis object. Also, if we try to access anything private, like __init__, we'll be told that syntax is unknown. This also means that our default() method is no longer necessary, since we've already enumerated everything that we could possibly call on the Redis object.

You'll notice, however, that help lists all of the functions as "Undocumented". It would be really nice if we could also get documentation for each of these commands. Now that we can easily list all of the available commands, we could write the documentation ourselves by specifying a help_foo() function for each command. However, this is boring and like I said before, I don't have that kind of patience. It also turns out that writing our own documentation would be redundant, as the authors of our Redis client library have done a good job documenting each function in the form of docstrings:

def get(self, name):
    """
    Return the value at key ``name``, or None of the key doesn't exist
    """

Python takes these docstrings pretty seriously. In fact, they become an attribute of the function itself, called __doc__. This is great for us, because it means we can pull those docstrings into our CLI and make them documentation for our commands. We use the same method as before to dynamically add help_foo() methods to our own class for every function that has a docstring:

    doc = (getattr(func, '__doc__', '') or '').strip()
    if doc:  # Not everything has a docstring
        setattr(self.__class__, 'help_' + name, self._make_help(doc))

    @staticmethod
    def _make_help(self, doc):
        def help(self):
            print doc
        return help

So now if we type help, we'll get a list of "Documented" and "Undocumented" commands. If we type help get, it'll tell us "Return the value at key ``name``, or None of the key doesn't exist." Awesome!

This is getting to be a useful little CLI. In addition to having a documented list of all commands available, we also get tab completion for our commands, so if we type l<TAB>, we get the list of all list commands. (On Windows, you'll need the pyreadline module installed for this to work.) But what if we could also autocomplete our keys? Some of our keys get pretty long, and typing them out is a pain. We could define a complete_foo() method for each of our functions, but all we're ever going to be completing are the keys, so we can just use the completedefault, which is a catchall completion, to grab our keys for us.

    def completedefault(self, text, line, start, end):
        return self.redis.keys(text + '*').split()

Once we've added this, we can type get bar<TAB> and we'll get all of the keys that start with bar.

We're in the home stretch now. Just to make things a little nicer, let's modify the prompt and the intro message so the user knows they're in the Redis CLI:

    def __init__(self, host, port):
        ...
        self.prompt = '(Redis) '
        self.intro = '\nConnected to Redis on %s:%d' % (host, port)

And finally, because this is a tool we want to be able to use with different servers, let's add the ability to specify a host (-h or --host) and port (-p or --port).

import getopt

if __name__ == '__main__':
    opts = dict(getopt.getopt(sys.argv[1:], 'h:p:', ['host=', 'port='])[0])
    host = opts.get('-h', None) or opts.get('--host', 'localhost')
    port = int(opts.get('-p', None) or opts.get('--port', 12345))
    RedisCli(host=host, port=port).cmdloop()

To finish, we just add some error checking so we don't get bailed out with an exception if we happen to make a typo. Here's the final script:

import cmd
import getopt
import sys

from redis import Redis
from redis.exceptions import ConnectionError

class RedisCli(cmd.Cmd, object):
    def __init__(self, host, port):
        super(RedisCli, self).__init__()
        self.redis = Redis(host=host, port=port)
        self.prompt = '(Redis) '
        self.intro = '\nConnected to Redis on %s:%d' % (host, port)
        for name in dir(self.redis):
            if not name.startswith('_'):
                attr = getattr(self.redis, name)
                if callable(attr):
                    setattr(self.__class__, 'do_' + name, self._make_cmd(name))
                    doc = (getattr(attr, '__doc__', '') or '').strip()
                    if doc:
                        doc = (' ' * 8) + doc  # Fix up the indentation
                        setattr(self.__class__, 'help_' + name, self._make_help(doc))
        try:
            # Test the connection. It doesn't matter if 'a' exists or not.
            self.redis.get('a')
        except ConnectionError, e:
            print e
            sys.exit(1)

    @staticmethod
    def _make_cmd(name):
        def handler(self, line):
            parts = line.split()
            try:
                print getattr(self.redis, name)(*parts)
            except Exception, e:
                print 'Error:', e
        return handler

    @staticmethod
    def _make_help(doc):
        def help(self):
            print doc
        return help

    def completedefault(self, text, line, start, end):
        return self.redis.keys(text + '*').split()

    def do_exit(self, line):
        return True
    do_EOF = do_exit

    def emptyline(self):
        pass  # By default, cmd repeats the command. We don't want to do that.

if __name__ == '__main__':
    opts = dict(getopt.getopt(sys.argv[1:], 'h:p:', ['host=', 'port='])[0])
    host = opts.get('-h', None) or opts.get('--host', 'localhost')
    port = int(opts.get('-p', None) or opts.get('--port', 12345))
    RedisCli(host=host, port=port).cmdloop()

And there we have it. A full-featured, robust, well-documented Redis CLI in about 60 lines of code. For comparison, the C version is over 500 lines of code, and has no help documentation or code completion.

The best part, though, is that this doesn't really know anything about Redis at all. The only parts that are aware of Redis are __init__(), which sets up the Redis object, and completedefault(), which gets our keys. That means that you could easily adapt this script to be a CLI on top of any client library you have.

Posted July 20, 2010 - View Comments

Your Coworkers Make You More Productive

This isn't a surprise to almost everyone who reads this blog, but I returned to work back in November after taking a two month sabbatical of sorts. It was an interesting experiment, and one I plan on repeating at some point in the future.

I've been wanting to write about something I learned while on break, but since returning to work, I've basically been sprinting, along with the rest of the Kiln team, through beta and towards launch. I have a little downtime today, unless beta bugs come in, so I'll try to get it written up. Better late than never...

The Lesson: Developing in a Vacuum is Hard

Yeah, you can't hear over all the noise! Ba-dum...sorry....

During my break, I started working on a new project combining two of my passions, software and photography. A couple weeks in, I found myself frequently getting stuck in designing and implementing the features. I've experienced development blocks in the past, but they were usually related to code I didn't want to dive into – oh, say, billing code. This was new, fun code. Features code.

I started thinking about why I was getting stuck. It became clear that I was getting stuck on decision points. I would come to a place where I could think of a couple of ways to do something, and I'd start hemming and hawing, without actually doing anything. After a while, I would force myself to sit down and focus, think through the problem at hand, and decide on a way to do it. Usually, I would come up with a good implementation, and I'd be on my way again. Other times, I'd find a problem I hadn't considered, and I would go back and try one of the other options. Once I got past the block, I would be fine – at least for a while. But then another fork in the road would appear, and coder's block would set in.

After spending some more time thinking about my coder's block, and discussing it with friends, I began to realize what was missing: a sounding board. When I was at work, and I got to a decision that I was stuck on, I'd IM or talk to Ben about the merits of the different options. Often, he wouldn't be completely paged into the problem I was working on (he had his own work to do), but just the process of explaining the different options would usually be enough to help me make the decision and get back to work. Sometimes, Ben would have helpful insights as well, which made our interaction even more valuable.

Since I was on my own, explaining the problem to someone else really wasn't an option. But then again, it wasn't really necessary either. There's a technique I've known of for a while called Rubber Duck Debugging. Here's how it works:

Rubber Duck

  1. Beg, borrow, steal, buy, fabricate or otherwise obtain a rubber duck (bathtub variety).
  2. Place rubber duck on desk and inform it you are just going to go over some code with it, if that's all right.
  3. Explain to the duck what you code is supposed to do, and then go into detail and explain things line by line.
  4. At some point you will tell the duck what you are doing next and then realize that that is not in fact what you are actually doing. The duck will sit there serenely, happy in the knowledge that it has helped you on your way.
    Source

I wasn't really doing much debugging – most of my work was on new features – but it still helped. I would sit myself down (I didn't actually have a rubber duck handy) and explain, out loud, the various options, the pros and cons of each, and what I would need to do to implement each feature. Once I got through explaining it all to myself, I realized the decision was clear, and it became much easier to keep my momentum and continue working. It's not quite as good as having someone else to explain it to, but it's much better than being blocked.

Now that I'm back at work, I've gone back to bouncing ideas off of my coworkers, as they are incredibly bright and often have good suggestions. I think I do it a little less often, now that I can work through some of those decisions myself, but their input is still invaluable. I've always enjoyed working with them, but now I have even more appreciation for how important they really are in my daily work.

Thanks to evilerin for the photo " AAAARRRGGGHHH"

Posted February 9, 2010 - View Comments

Career Break

Fishing at Blenhiem by witI didn't go into work last week. I won't go in this week either. In fact, it may be a month or two before I go back. I'm on sabbatical.

Well, kind of. Typically, one gets paid for a sabbatical, if only half of the normal income. As Wikipedia informed me, I'm taking more of a career break. Either way, I've decided to take some time off to deal with a few things that have been bothering me lately.

The biggest issue I've been dealing with is burnout. It seems odd to be saying that - for the past six months I've been working on a brand new product that I helped conceive of and prototype a little over a year ago. I got to work with fantastic interns and some of the best developers I know on an interesting, useful project that I know many people will enjoy using. We got to use a modern framework with good tools and clean interfaces. In many ways, it is a dream project. But I left before it was released. The culprit is billing code.

It started right after my first internship in 2005. Copilot had just launched and all of the other interns had gone back to school. I stayed behind, deferring my Stanford admission three months, to keep the product alive and wrap up the few bugs and features that hadn't been implemented during the summer. On the top of the list: a subscription payment system. The idea was pretty simple, a tiered set of subscriptions based on minutes per month -- similar to a cell phone plan, minus the evil. The problem was, we could not find any payment processors at the time that could deal with variable monthly subscription fees. So we had to implement it ourselves, credit card database and all. I did the majority of the work; my econ second-major came in handy. Before I left at the end of the year, the new subscription system was in place and people were signing up.

money roll by zzzackIt did well for Copilot. Sales doubled and doubled again. When I returned, I took over billing again. I help our support team work with customers billing issues, I fixed bugs in the system, and added little features to make some of the small pain points go away. Then about a year ago, we noticed Copilot sales were starting to plateau, so we set out to add new features and restructure the billing system. For my part, this involved a near-total rewrite of the billing system, sending me deep into the details of billing cycles, prorating, plan switching, and other tediously boring subjects. I spent months refactoring and months testing and bug fixing.

The problem with billing systems is that, technically speaking, they are not very difficult to implement, but they must be perfect. New features garner little praise, but errors will easily result in lost customers. And I had became the in-house subscription billing expert. Even after moving to the new project, I could be interrupted at any time if a customer got into a weird state or saw what they thought was an error on their statement. And of course, the new product would need billing code of its own. It had become inescapable.

Time Card by TheGooglyThe other main issue was personal projects. As you know, I put a lot of stock in personal projects. I feel that they are the best way to improve yourself as a developer, and a good way to keep you sane when your work projects head south. Unfortunately, the employment contract I signed as an intern (which still applies as a full-time employee) is ambiguous about IP ownership with regards to personal projects. In other words, it would be possible, though unlikely, that the company could take ownership of something, even though I had worked on it entirely in my own home, with no company equipment or expertise. (Unfortunately, New York does not have provisions like California and six other states, which prohibits employers from taking ownership of outside projects.) While I have no expectation that would actually happen, the ambiguity makes it difficult to justify spending any time on projects that I care about. That limited my outside work to projects I'd had gotten excluded about a year before, when the policies had not been so rigid.

So I took some time off. So far, I've been working on things that I've had to put on the back burner for the past year, including a big new update to the eBay Toolbar. Working from home has been relaxing. I can set my own schedule, and work without interruption. The biggest thing I miss is the social interaction with my fantastic coworkers. I may be returning in a few weeks, or I might take another month off. I'll be updating as I get further into the break.

Thanks to wit for "Fishing at Blenhiem", zzzack for "money roll", and TheGoogly for "Time Card".

Posted September 14, 2009 - View Comments

How to Get a Job at Fog Creek (and Other Selective Software Companies), Part 3: Programming Interviews

This is the third (and final) installment of a series on how to get a job at Fog Creek Software (and other selective software companies).

Assuming you do well on the design question, you will be scheduled for a second phone interview. This one will involve code. We use our remote assistance software, Copilot, to log in to your computer so you can use the editing tools you're comfortable with. (Of course, if you're not comfortable with that we can always fall back to EtherPad, which is also pretty awesome.)

Before we get into any specifics, make sure you've read the post on phone interviews. Everything I mentioned there carries over to coding interviews, especially the part about talking through what you're thinking. It can be easy to jump into a programming problem and just start writing down your answer without explaining your process. While this may work for actual programming, it defeats the point of an interview. You need to make a point of communicating your solution to your interviewers. It will show them that you have a good, thorough thought process and that you can work with other people.

Speaking of being able to work with people, try to remember manners and common courtesies to everyone you interact with during the process. Our office managers may not be asking you technical questions, but if you treat them rudely or disrespectfully, you can be sure that we'll hear about it. (If you haven't learned it yet, learn it quick: the office manager is, by far, the most important person in the office.) They don't want to work with jerks, and neither do we. Also remember to respect the space you're in. I try to have fun and interesting objects in my office for visitors, but going around, picking up, playing with, and moving EVERY SINGLE OBJECT in my office does not make me want to work with you, no matter how good the solution you're mumbling to yourself is.

Not that a candidate has ever done that...

A programming phone interview will be nearly identical to a in-person interview, so I'm addressing both here. The interviewer will talk with you about your background for a bit, then he will jump into programming questions. Personally, I have one go-to question that I use for both phone and in-person interviews. This allows me to judge candidates based on their relative performance, as well as to help those that might get off track a little. Other interviewers have a few questions that work through progressively. A little hint: if an interviewer asks you several different programming questions over the course of an interview, there's a good chance they're related. See if you can build on your previous answers.

Whether you get one question or several, you will probably arrive at the final solution through an iterative process. Don't spend too much time perfecting the first code you write. Most likely, you've not taken something major into consideration, and are not yet headed in the right direction. It's better to quickly find any fundamental flaws in your code than to have a beautiful, but incorrect solution. As you progress towards a better solution, start thinking about the little stuff like error checking, corner cases, and typos.

When you're done, give it a look over just to be sure. Then, talk your interviewer through the full execution path a few sample inputs. You may think your solution is clear to them -- and it probably is -- the sample input is not for them, it's for you. There are still bugs in your code. Nobody writes perfect code in interviews. You're talking to some stranger who put you on the spot, solving a problem you were just introduced to a few minutes ago, and your solution could determine whether or not you get the job. It's a stressful situation. So run a few sample inputs, find those bugs, and impress the interviewer with your thoroughness. I've interviewed so many people that showed a lot of promise, but fell flat at the end because they refused to test their code, despite several glaring errors.

So what's the best way to prepare for all of this? Programming Interviews Exposed is a good, if somewhat basic primer on coding interviews. Joel's Guerrilla Guide to Interviewing v1.0 and v3.0 have some sample questions that we actually used to ask during interviews. You can also search for "programming interviews" on Google and find a lot of sample questions.

All that will help, but there is one way that you can really improve your chances in an interview: Learn C.

Hey, I thought you guys didn't care what language the interview was in, what gives?!

It's true, we don't really care what language you use for the interview, as long as it's common enough to be suitable for communication. If you want to use Java in an interview with me, go for it.

But here's the catch: most other languages make things easier. While that's good for being more productive, it makes it harder for you to demonstrate your skills the interviewer. If you can sit down and reverse the words in a string, in place, all with pointer arithmetic, I'm going to start thinking you might have an idea of what you're doing. If you do it in Java and forget to mention that the language you chose makes it impossible to do it in constant memory, you just earned yourself one demerit.

If you don't already have one, go find yourself a copy of K & R and read it cover to cover. Once you're familiar with C, find a set of questions (like this one from an MIT course on programming interviews) and work out the solutions in C, using pointers. Treat it like production code, come up with weird inputs, test it all. Then find a CS buddy and challenge them to find bugs. You may not get to the point where you are comfortable doing a real interview in C, but the thought process and preparation will help you regardless.

By being deliberate and methodical, and making a point of communicating your thought process to your interviewers as you go, you eliminate the common pitfalls and increase the chances that the interviewer will help nudge you in the right directions. And try to have some fun; after all, programming is what you love doing, right?

Posted September 1, 2009 - View Comments

How to Get a Job at Fog Creek (and Other Selective Software Companies), Part 2: The Phone Screen

This is the second installment of a series on how to get a job at Fog Creek Software (and other selective software companies).

So you've wowed us with your cover letter and resume. Your kernel work on OpenBSD shows us that you are passionate and hardcore. Despite marginal grades in college, you've shown us how incredibly bright you are. And we know you care about the job, as your cover letter was witty, well-written, and clearly intended for us. Now it's time for a phone interview.

We generally do two phone interviews. The first will be a design question. While you won't be writing any actual code for this interview, you will be working with some core concepts like algorithms and data structures. Our intent is to test your problem solving abilities given a problem and a set of constraints. To that end, the most important thing to remember is to talk through your thought process. Sitting silently for minutes on end is a great way to convince us that you are currently Googling the answer.

Most interviewers will just have one big problem for you. The problem is always iterative; the intuitive solution will probably work, but it will have deficiencies, and you'll have to improve your answer. This is intentional, so don't get discouraged when the interviewer starts pointing out all the various problems. He is actually giving you hints about what to focus on to improve the solution. Even more importantly, don't get too stubborn about your ideas. It's a fine line. An interviewer might challenge a correct answer to make sure you can explain it well, and you should stick to your guns. But there's also a chance that you're wrong, and while there is no single correct answer to most of the questions, there are incorrect solutions, and more than likely, your interviewer has seen them before and is trying to help you see your mistake. In either case, review your answer with a critical eye. Work through the solution out loud, and see if you trip yourself up. Pay attention to the interviewer's reactions for a little extra help. If you get to the end and neither of you sees and problems, then you're probably on the right track.

So let's say your interviewer asks you a question about designing a data structure to hold passenger information for a train scheduling program. The interviewer wants to know the best way to store how many passengers get on each train (denoted by its number) at each station (denoted by which mile on the track it's at) along each line (denoted by number), and on which day of which year. (Creating good interview questions is actually quite difficult. Please excuse this softball question.)

Immediately, a multi-dimensional array jumps to mind. You put the train number on one axis, the stations' mileage on another, the line on another, and the date on the final axis, with the passenger count as the data. Piece of cake, all the data is in there, easily accessible by any metric in constant time. Done, right? Of course not. Your interviewer will probably ask you to find any problems with your design. If you're quick, in this very poor example (seriously, I don't want to get any comments about it), you'll realize that you've created a very sparse matrix, which gets very big very fast, without actually holding all that much data.

How can you improve upon it? Well, in your excitement about your brilliant solution you forgot to consider how the data is going to be used. Remember to ask questions and try to get all the details and specifications up front. In this case, you probably don't need to be able to access the data from every axis. That would allow you to use a hash table of hash tables of hash tables to keep constant time lookup while decreasing memory overhead.

Of course, most people would probably get to relational databases faster than this (extraordinarily bad) example, but it is the type of iteration that you should be going through. If you happen to know, or stumble upon, the correct answer early in the question, the interviewer will usually be prepared to extend the question by asking about more specifics, corner cases, and enhanced features. Just keep working through each new part, building on your solution.

If you keep the discussion alive, talking through the problem, and revisiting your solution trying to find any pitfalls, you'll help your interviewer understand your approach and increase the chances that you'll work your way to a good solution -- and another interview.

Stay tuned for part three, which will cover programming interviews.

Posted August 26, 2009 - View Comments