I saw this question on Metafilter about what CS courses are necessary to be a software developer and thought I would take the opportunity to expound a bit.
The first non-useful answer is: all of it. I went to Oberlin, a college that only grants arts degrees, and received a degree in computer science. In that time, I also took classes in religion, ethnomusicology, physics, media, anthropology, and so on. Anything that seemed interesting and I could schedule, I tried to take. I honestly believe in the value of breadth in many things and depth in a few things. The value is especially apparent in software where solutions frequently need to be tailored to the problem domain and the possible number of domains that can be addressed effectively with software grows daily. If you know about a wide number of things, you can better tailor solutions to those domains. Being a lifelong learner makes it easier to learn new problem domains.
The second answer, more useful perhaps, is the following.
An intro class in a high-level language – you’ll probably find classes taught in Java, C#, Python, Ruby, or C++. My first CS class in college was in Pascal. If you already know a HLL, it’s still worthwhile, especially if it’s a different language. In fact, you might want to look at the entire course book to see if all the classes are taught in only one or two languages. That’s a bad sign, especially for a budding software developer where you should expect to need to learn a new language every few years. I know more than 20 and it is a career skill to pick up a new one in short order.
A class in algorithms and analysis – this is probably the one that I touch on the most without ever really having to crack a book (I do keep the textbook in my office in case), just for the ability to analyze the best, worst and average cases of code I write. I have encountered code in the wild that search for the first/next instance of a string in a stream that was O(n!) – easy to spot if you know what to look for.
A class in design patterns – I don’t know if this is in any college curriculum, but when I started my professional career, there weren’t any formal design patterns. They were informal and maybe differently named from shop-to-shop. I did discover that many of us had invented the same things just to get a job done and the wise ones abstracted them just enough to be able to apply them in many circumstances while keeping them nimble (for example, I had a tight library that was built around the Visitor pattern which was the basis for an entire UI toolkit.
A class in system architecture – in my day, this was called assembly, but I believe that a good solid understanding of the hardware underneath your computer is key to making good high level decisions as well as making you a far better debugger. It is also necessary for writing a compiler.
The Scheme/Functional class – if you’re lucky, your department will have a Scheme zealot. I’ll be honest, I hated Scheme. I’m a very concrete/practical programmer by nature and Scheme drove me crazy. Not because I didn’t understand it – I did. It drove me nuts how fluffy the language was in terms for how it worked and how code I wrote would stall for chunks of time while the garbage collector went to work to get rid of all the things I allocated that I couldn’t prevent. I swear I was the worst offender of using set! and its variants in my code just to avoid wasting time/memory. Still, in spite of the structural language deficiencies, it is a fabulous language for getting you to stop thinking about problems sequentially.
A class in compilers – this was my hardest class, I think. I flunked it. But it was the most valuable class for learning how to debug a high level language from the lowest level. You should get to the point where you can look at machine generated assembly language and mentally decompile it into the HLL that made it. For example, I had a third party library that worked most of the time, but crashed at other times. It turns out that they were using C enums in size-dependent data structure and their compiler chose one size for the enum and my compiler chose a different size. Without being able to understand what a compiler would have done with code to operate on structs that contained this enum, I would’ve been dead in the water. I run into this kind of thing All. The. Time.
A class in operating systems – and by extension, this class should cover concurrency and resource management. You’re writing code and you’re running it on what now? You should understand how an operating system works, how to write device drivers, and especially concurrency. When every machine, including your phone, is shipping with a couple cores, you should understand how to best leverage concurrent programming.
Beyond that, everything else is gravy. Also consider a writing class of some kind. Clear communication is vital and it is one of the things that I use to pick a new hire.