Before I get started on this, I don't want to mislead you into thinking that I didn't like the University of Texas (UT) or that I didn't feel like I got a good education in UT CS. I've already established, in fact, that I loved my time at UT. I use a lot of the stuff I learned in school all the time (though some of my non-major courses, like Electricity and Magnetism, have been less useful). However, there are some things that I don't think any American university (possibly not even universities around the world) teach you that are invaluable in the real world. I've only been working full time for a little over a year, but there are 3 key lessons that I didn't learn until I got into the real world though they're invaluable skills. Aside from what you do learn in school, I feel like you can't excel as a software developer without these things at any good company (if you're at a company that doesn't value these things, you're probably at the wrong place). You don't learn them in school because they're hard to teach, but if you can learn these things before you get out there then you have a leg up on the competition. To give you a brief overview: after I talk about those things I'll give some honorable mentions to other useful talents and then I'll go over the things I'm really glad I did learn.
Lesson #1: Working Well in a Team
Even though I was part of the executive body of a student organization, did peer programming for multiple courses, and worked two internships, I didn't really have a good understanding of dealing with team dynamics. While every team has its own way of doing things, there are general ideas that are applicable to all teams. It's important to ask questions when you can't find the answer to something in a reasonable amount of time, but it's also important to be polite and non-obtrusive is asking for help. Doing code reviews is a delicate balance of being constructive in your criticism because you're reviewing something that a team mate likely worked very hard on. You have to learn to trust your team mates' decisions and their work, or else maybe you're on the wrong team (if you're at a good company though, it's more likely that you're just being paranoid).
What I'm really getting at is that a one-man team isn't going to go nearly as far as a team of competent engineers. As great as rockstar programmers are, they're not going to be able to get as much done as a team of great engineers. Aside from the bandwidth of a single person being less than a team, a rockstar programmer can always improve himself by feeding off the talents of others (it's a good relationship though because everyone else learns from him). Your success as an individual hinges on your ability to work well with your team. It's amazing how much you can improve the design of something by walking to the right teammate's desk and having the right conversation with them (i.e. talking to him in an optimal way). Tackling a big problem alone is inefficient and is not likely to lead to the best solution in most cases. We once had a major operational issue where I felt like we hit a brick wall, but then a team mate came in with one great idea to help, and that inspired another out of me, and we kept brainstorming to a point where we turned a seemingly impossible situation into one with a path to resolution.
Part of working well on a team is not getting too big for your britches. Sometimes, you have to do work you don't enjoy. The way I've learned it is, if you're spending at least 80% of your time doing what you enjoy doing, then you're sitting pretty. Getting to 100% is practically impossible though. Tasks shouldn't be seen as only for a higher seniority person or a lower seniority person, and you should be willing do whatever needs to get done to facilitate your team's goals for that sprint/month/year/whatever. You should also trust that everyone else on your team is humble and share useful information proactively rather than waiting them to come to you for help.
Of course, a team is only as strong as its weakest link. If you have one person who's too lazy to find answers before asking teammates or who just writes terrible code then the team will never work as well as it could without him. You have to be able to recognize the warnigns signs of a weak link and either try to remedy what's causing him to bring the team down (or even just yourself if he only affects you), or talk to your manager about whether this person is the right fit for your team.
I've only scratched the surface here of what it means to be a good team player. Some of these things seem conceptually obvious, but they're not that easy in practice and require, well, practice. The closest I've seen to a class in school teaching these skills is a Software Engineering course at UT where you and a team of people create a product from start to finish and then present to investors at the end of the semester. The bottom line is that you cannot be successful if you aren't willing to learn how to work well with a team and take advice from peers and mentors and managers on how to improve in that capacity, as well.
Lesson #2: How to Effectively Communicate
Working well in a team and effectively communicating definitely aren't mutually exclusive talents, but effective communication goes far behind just dealing with your team. Being a truly great engineer is more than just hacking away behind a keyboard, it's being a role model and a respected leader. How many great, respected leaders do you know who aren't also very good at communicating (in writing and in speaking)? Steve Jobs isn't CEO of Apple for his good looks.
In any software company there are always customers. Understanding how to deal with cusomers is paramount. I know what you're probably thinking, isn't that a manger's job? Yes and no. Managers tend to distill customer concerns and feedback so that developers can focus on getting products done, but they can't handle every little customer communication. When you have operational issues at 2AM, you can't wake up your manager to figure how to speak to your customers about it. When a customer stops you in the hallway to ask you a question, you can't just shrug your shoulders and point to your manager's office. Dealing with customers is a delicate balance. You want to tell them exactly what they need and want to know, and nothing more (or less). You want to use the right words because, especially in written communications, you don't want something lost in translation (in connotation and just technical terminology). I could go on and on, but the point is that you're not going to move forward in your career if your customers don't like you. Granted, there are likely to be a few who don't, but the rest really should or else maybe you're not doing your job.
Another area where communication is important is in design discussions. Getting your point across with peers with no tools other than your brain and a whiteboard is absolutely critical to your day-to-day. This also includes being a good listener and taking other people's points into consideration. People like being heard and hate being talked over.
Then there are technical talks. Being a rockstar engineer means that you're good at sharing your knowledge with others. If all you do is absorb knowledge and stow it away then you're not of much use to a company because you immediately become a single point of failure and a flesh covered encyclopedia. Sharing with others helps prevent co-workers from re-inventing the wheel, which makes your company more efficient, which saves money, which drives better software development.
You have to give presentations in school and write some papers, but the workplace has become inundated with communication via e-mail, meetings, teleconferences, instant messaging, and whiteboard gatherings. Each type of communication follows slightly different rules, and college only scratches the surface of any of them. An e-mail you send to your professor is not nearly the same as an e-mail you send to your director. Still, there is no ettiquette taught in school. Writing a short story or an essay is not the same as writing readable documentation for your software. This is something that's really hard to learn in the classroom, and really requires experience via internships and co-ops. Another useful place to start out is joining your local Toastmasters chapter.
Lesson #3: Proper Unit and Functional Testing
Last, but not least, quality assurance (QA) is the bedrock of any good software development shop. Being a QA engineer may not seem as sexy as being a software developer, but it's just as important (if not more so). Beautiful software that doesn't work, or only works some of the time and explodes at others, is pretty useless. If someone was trying to sell you an iPod Nano and told you that it usually worked great but had good odds of literally exploding without warning, would you honestly want it?
Of course, QA is really everyone's job on a team. A QA engineer can only do so much. You have to write good unit tests and functional tests so that your code passes some nominal bar of what you expect it to do. If you just write your code and don't run it, how do you know if it worked? Even more important: how will someone else know it works if you don't have unit tests proving your intentions for the code? And probably even more importantly, how will someone who modifies your code a year later know if they broke some key functionality in a module of 500 lines of code if there are no unit tests to verify its core functionality? Unit tests are designed to test each part of a module in as small of units as it can be broken into and functional tests are intended to test an entire module. QA engineers are then typically resonsible for integration tests (testing that your module works well with the rest of the system), but it helps if you understand the integration testing, as well. Another fringe benefit of testing, as a whole, is that it forces you to take another look at your code before you push it out for code review and is likely to help you catch a lot of careless bugs.
When I graduated, I conceptually understood unit testing, but had little experience with it and not really a full understanding of functional or integration testing. Granted, my degree was in computer science and not software engineering, but it wouldn't have hurt to have learned more about proper unit testing technique. My most rigorous edification on unit testing was in Operating Systems where our teacher had some tests for our project (a File System) based loosely on the requirement he had given us and we were responsible for writing our own unit tests to match the ones he wrote that we weren't allowed to see. I thought this was really unfair (especially since I failed the project), but he thought it was perfectly reasonable. He said that in the real world you'll get ambiguous problems and have to still come up with a good solution. This is true, except for that in the real world you also do iteration to account for hidden requirements and you don't get fired (what I consider the equivalent of an F) because you missed one of these hidden requirements. Also, you have a whole QA process and a team of engineers working with you to ensure that you don't make any false assumptions and miss something important, as opposed to a classmate who may or may not be very helpful to you (mine happened to be awesome, by the way). The point is that he took the wrong approach, in my opinion. He had the right intentions, but poor execution.
The right thing to do is to first teach students how to unit test. Until I came to Amazon, I didn't know about things like TestNG, EasyMock, and JMock that are very important to writing unit tests for many cases that I tend to run across. I don't feel like universities, as a whole, emphasize testing enough. There should be some instruction on technique rather than throwing students to the wolves and letting them hope for the best. To expect students to write good software for big projects without showing them the tools necessary for testing (or pointing them, at least, to the documentation for them) is like expecting someone to write a program in Java who's never programmed before in their life.
I also had never heard of Test Driven Development (TDD) before I came to Amazon. Granted, that's not as important as unit and functional testing, it would've been nice to know. There should be a required course in CS curriculum around just general Software Engineering like advanced use of debuggers, profilers, TDD, unit testing, etc. I cannot stress enough how valuable good testing is to the long-term success of a developer.
There are some other things that I also wish I learned in college, but some of them were available in school I just didn't have the time for them while others simply aren't absolutely critical to success for everyone but can be very important.
- I took an optional course on Linux in school, which was a very good choice, but it was very basic. I wish I had exposure to more advanced Linux concepts before I got in the industry, like NFS and rpm and advanced Linux commands. Linux is definitely the best OS for developing software, in my opinion. Macs are good, too, but mainly because of having that command line. However, most Linux distributions are most stable than OS X, which is crucial to development.
- I definitely didn't have to write as many scripts in college as I wish I had to. Knowing a couple of good scripting languages can save you countless hours of time in automating something without writing a bunch of code in a heavy-weight language like C or Java. When it comes to doing something quick and dirty for an operational issue, the poor performance of many scripting languages isn't as critical as being able to write something quickly that just works. I'm trying to invest time in learning Python or sed, but so far I've been limping along with my limited knowledge of Bash and Perl, which is still somewhat intimdating to me. We had courses on Perl, Python, and Ruby, but I just never had free hours for them.
- Being in a top CS program definitely makes you feel smarter than it should. If you've been in such a program and don't agree, then you're in denial. Let's face it, at one time or another, a CS student at a good school, whether or not he makes good grades, feels smarter than other people. Given that it has a unique vernacular compared to other majors, that already makes CS majors sound smarter, but making good grades boosts their ego even more. The cold hard truth is that you're not special when you come out of school, you have to earn it. This isn't to say that you won't get respect, but you'll lose it very fast if you don't humble yourself. There will always be someone smarter than you, and so you have to be able to take constructive critcism and realize that there's always more that you can learn if you're willing to.
- There are no grades in the real world. You can't easily measure your success. You have performance evaluations every year, most likely, and that's about it. This drove me crazy for a while because I didn't know how well i was doing. I had to learn to glean this from one-on-ones with my manager and feedback in code reviews and such. There are a lot of measures of success in the real world and none of them come with an easily attainable letter or a number. It's all about what you want to get out of your career and what your job demands of you.
- Simply shipping a product isn't all that matters. Aside from the cliched saying that the journey is more important than the destination, it's important to realize that getting the right results is key. You can't rush something out just to meet a deadline, because that's a short term gain for a long term loss. You have to be willing to make the right choices now so that you don't have to apply band-aids later. You need to also ship the right software based on what your customers want, not what you think is cool.
What I'm Glad I Learned
Of course, there are some things that I'm really glad I learned in school - things that I can't do my job without. There's no question that I wouldn't be half the developer I am today without my college degree. These are the things that I use all the time:
- It's really mind-boggling how often I come back to concepts I learned in Operating Systems. Aside from just multi-threading there's technical stuff you learn that's absolutely invaluable in debugging really hard problems, like memory management. If you don't know how a file system works, for example, how will you figure out that you've run out of disk space because you're out of available inodes? How will you figure out why your jobs are running slow if you don't know what swap memory is and why it could be getting filled up?
- Computer Architecture was another shocker. The concepts of the stack and pipelining alone are key ideas (the latter of which is fundamental for discussing performance in large systems), but knowing in great detail how a computer works helps you understand what kind of metrics to monitor in your systems so that you know when you're in trouble before you're in trouble. You can't drive to root cause without understanding what they really mean either.
- It almost goes without saying that courses in data structures and algorithms are the fundamentals of programming. Having a cursory knowledge of the building blocks is a pre-requisite to writing good code. Even past that, there's also the importance of learning design patterns and using them in brainstorming during design discussions.
- I have to admit that my 4 semesters of theory definitely have paid off. Automata theory alone is a goldmine when it comes to understanding at least regular expressions and context free languages. This is not to mention somewhat formally proving something in your head before you convince your boss that something is true. Of course, you'll need to distill your proof into English, but all the same I often use techniques I learned in automata theory as building blocks for my arguments.
- Obviously, learning programming languages themselves were important. I don't think that really requires explanation.
- I think that all CS programs should mandate a course in network security and privacy. How can you write safe software if you don't understand how it can be attacked? How can you figure out why your website is down due to a Denial of Service attack if you don't know what one is? How can you write a threat model for your software if you've never written a buffer overflow exploit? Aside from the obvious benefits, you also get a much deeper appreciation for the memory stack in a security course.
- Learning how to pair programming was a more than welcome skill as it's definitely necessary sometimes at work and was a good stepping stone for understanding how to work in a team even if it wasn't the full picture.
- A big part of any CS program is having to debug your software, and I don't see how anyone can be a developer without these critical thinking skills. They've served me well and are growing constantly. Without the foundation I got in college though in learning GDB and how to use the Eclipse debugger and marathon sessions at 4AM trying to squash bugs I don't know where I'd be today. Debugging is a huge part of my job in development and operational support.
- I took a course in management that I'm really glad I took because it taught me some great concepts regarding leadership and helps me understand stuff that my manager and her manager and her manager's manager do/say.
There's a lot of things a CS program and a good University can and will teach you that are very important in the real world, but there are some things you just can't learn in the classroom. I hope I've inspired some people to think about what they can do to learn the skills they might not get in school and set goals in internships and co-ops to focus in on these things. When it comes down to it, these are the skills that separates awesome developers from good developers. If you really want to succeed, there's no way around them.