Answer by Adam D’Angelo (Quora, Founder and CEO):
Overall, you have to get into the mindset of thinking like an adversary who is trying to cause your code to fail. Whenever you decide to get the first element in a list, you need to automatically think “could the list be empty?”. Whenever you start or finish writing a while loop, you need to think “is there any way this could possibly be an infinite loop?” To a large extent you’ll get this with experience, but there are some ways you can make sure you build up that experience more quickly.
One thing you can do to start is to use assertions and other techniques to “fail fast”. This will make sure that edge cases that you have forgotten about will show up as errors that get your attention during testing, rather than silently surviving as bugs. For example, at the top of a function that reverses a list, you might want to assert that the variable is actually a list. That way, if there’s an edge case somewhere else that results in the reverse function being called with null as an argument, you’ll detect it more quickly.
Writing unit tests can also be pretty helpful, and they also help you change your mindset toward testing and edge cases.
There are certain techniques that make edge cases less likely to occur in the first place. For example, using for loops instead of while loops gives you less flexibility and therefore fewer chances to make a mistake. Writing functional style code (recursively traversing data structures, copying data instead of updating it in place, etc) generally results in fewer edge cases. If you were writing a function to sum up all the numbers in a list, you might want to make it so that it intentionally works for the base case where the list is empty and the result is zero for that case, instead of starting the sum as the first item in the list and adding the rest to it.
If you really want to invest a lot of time in getting better at this in particular, the algorithm competition in Topcoder has a challenge round where you look at other people’s solutions to problems and get points for finding edge cases that break their solutions. Participating in that for a few months made me significantly better at thinking through edge cases.