Lennart Poettering, the Red Hat developer based out of Berlin who's responsible for key open-source projects like PulseAudio and systemd, has offered a presentation on key facts you need to know about becoming a free software developer/hacker...
I'd like to add: many hackers find it therapeutic to start their own project, or do something entirely of their own design. Lennart rightly points out that little patches are the best place to start. But there is a middle ground that I think will help many developers grow as well -- it's a stepping stone that some people forego, but many developers find useful during the early stages of their free software career.
Write plugins to existing software.
Many existing free software projects are modular, and have an officially supported plugin system. For some of these projects, the plugin API is wonderfully stable for a long period of time. Good, useful plugins benefit a lot of users and enhance the software, so you can benefit yourself as well as others in a user-visible way.
Writing plugins is kind of a middle ground between contributing small patches, and writing entire major projects on your own. A plugin is usually a more serious contribution than a simple bugfix patch (most of which are just a couple lines); in a plugin, you're often adding entirely new functionality, which usually means you have to bring in new library dependencies, and work on some kind of TUI or GUI for your plugin. So you get to go through the paces of how it feels to write a major project, but in the end, you usually only chunk out a couple hundred to one or two thousand lines of code (as opposed to bugfix patches, which are generally smaller, and major projects, which run into the many tens of thousands of lines and upward).
I myself have written some plugins, and have been now considering ideas for starting a new project from scratch. I've submitted dozens of small patches to various projects and written 2 solid plugins that were contributed upstream to 2 different projects... I honestly don't think I would be experienced enough to start a major project had I not worked on the plugins. Both of my plugins (despite being in different languages) run between 1000 and 2000 lines of code. I guess plugins just kind of work out that way.
By looking at a slightly larger codebase that is entirely your own creation, you can get a feel for how to better structure your programs for easy maintainability. You will need these skills in massive quantities for a large project, but in order to apply design patterns many times to a large project, you first have to know how to apply them once to a smaller project.
I know that many university programs also try to grow your codebases slightly as your degree progresses, but in my experience, the size of the projects they expect is still rather small. So it's a big deal to gain experience and climb the code-scale-ladder of sorts, beyond the typical university project level of a couple hundred lines (just enough to demonstrate a concept).
So maybe for people who are of only average ability (like me), a more comprehensive track for contributing would be:
Tier (1): Contribute a bunch of small patches. 1-liners, 10-liners, 50-liners; usually bugfixes; occasionally very minor feature adds. Anyone can do this.
Tier (2): Write a plugin to a well-known program, which does something entirely new that is complementary to the existing functionality and enhances the user experience. Avoid cluttering the UI as much as you can help it. It's very important that you don't "stay" at this tier, because maintaining your work is what helps to build your reputation. Conversely, if you contribute a drive-by plugin patch and never work on it again, your reputation will dwindle. So if you've come this far, definitely look at Tier 3:
Tier (3): Maintain your plugin(s) as the underlying software changes. Learn the process of refactoring your code to adapt to a new API underneath. Learn how to be a good maintainer. Learn from upstream core developers that you interact with. Fix bugs in your plugin (because most plugins running >1000 lines of code are going to have bugs). Almost anyone with a serious interest in programming -- even as a hobby -- should be able to reach this tier, with persistence.
Tier (4): Use your experiences from the previous tiers, plus some domain knowledge you've accrued along the way, to start your dream project -- something you really think would be beneficial to a lot of people (or at least beneficial to you or your company). Don't pay much attention to the fact that other projects exist that do a similar thing or serve a similar function; after "graduating" from Tier 3, if you think you have a better design, you probably do. The unique factor in this tier is that your only dependencies are on the system itself (libraries you depend on, the kernel, etc) -- other than that, you are writing a program from scratch. We already have tens of thousands (perhaps more) of people on the planet who are at this level, and there's room for a lot more.
Tier (5): Get hired to maintain the project you worked on in Tier (4). A great many people should be able to get to this tier, given the recent push to commercialize a lot of free software (cf. mobile/embedded + FOSS).
Tier (6): Where only a few elite hackers get to: become someone as central to the community as Linus or Al Viro or, indeed, Lennart Poettering.
Note that for people interested in extremely complicated projects such as the graphics stack or the kernel, the definitions of the term "plugin" and "dream project" change a bit. For Tier 2 you'll pretty much be adding a minor (but noticeable) feature to either the core project or one of its plugins. For Tier 4 you'll be extending the project in a dramatically new way, like introducing entirely new hardware support, or new OpenGL API support, or contributing your own shader compiler (to give a few examples). For someone interested in the kernel, though, it doesn't make sense to propose (or expect) that you would start your own kernel from scratch at Tier 4.... so most probably you would create a new module, or contribute a major, transformative feature to one of the core subsystems. Similar for Mesa.
Yes, the complexity of the "tiers" increases at an exponential rate (or at least by factors of 100). But hopefully the knowledge you gain from the previous tier is enough to help you reason out how to make the leap to the next, and be productive and put out good work. The reason why I introduced so many tiers (where Lennart pretty much just proposed 2) is because I think most people of average / slightly above-average intelligence are not able to start writing massive projects like pulseaudio after just spending a few months writing bugfix patches. A larger (but not huge) contribution is needed first, to help us see how to build up to the magnus opus.