First, communicate with the existing developers as much as possible. If there's an IRC channel, it really helps to hang out in the IRC channel and get known there, especially if it's a channel where you might be able to be helpful to other people. If there's no IRC channel, definitely join the mailing lists and post now and then so you get your name known. (Of course, it helps if you try to be friendly, polite and helpful in all these dealings, so everyone forms a good opinion of you and is more inclined to help you get your contributions checked in.)
Hanging out on IRC or mailing lists may also help you in getting familiar with the code. Some mailing lists talk about recent code changes, or proposed API changes; you can read what they say there, and it may be useful to find the appropriate part of the code and follow along.
If the project has a bug system like bugzilla, absolutely sign up for it, file bugs (trying hard to write good bug reports) and read existing bug reports. That's an important part of a project community, and you'll also learn a bit about which developers are more helpful to outside contributors and what sort of contributions get taken.
But ultimately, for getting familiar with existing code, there's no substitute for jumping in and getting your hands dirty. It's probably easiest to choose a particular bug you want to fix, or a simple feature you'd like to implement, so you have a particular goal in mind which guides you in looking through the source.
It's definitely worth asking the other developers about whether your chosen feature/bug is a good choice. There are lots of reasons for this:
Once you have a goal, grep -i is your friend. Pick some keyword involved with your chosen bug/feature, then grep -i in the source code to find places to start looking. Most likely, the first file you find with your grepping won't be the right one; that function will call a function somewhere else, and so you're led along a chain of calls, and meanwhile, you're getting familiar with the project's source as well as solving your own specific goal. Tools like emacs or various IDEs can help a bit in this, but use whatever you're most familiar with.
Once you have your code written, think about how it affects the project. Does it add a feature that perhaps not everyone will want? Consider hooking it in to the project's preferences system, if there is one. That avoids the impression that you're trying to force your feature on everyone, and also shows that you've gone to the trouble to understand their pref system. Is it a fix for a bug that possibly not everyone considers a bug? Take that into account when submitting it, and again consider using prefs, or at least offer to hook it in in the message accompanying your patch submission.
Be sure to follow the project's existing conventions for things like indenting. Don't submit code indented with tabs if they use three space indents, and if they have a convention for where to put opening braces, follow it. Most projects don't have published coding style documents, but as long as your inserted code is consistent with the code around it, you should be okay. Don't reindent existing code unless you're confident that they'll want that (e.g. if there's a new style guideline and they want to reindent code to follow the guideline).
Most projects want contextual diffs: diff -c or diff -cN if you're adding new files. If the project uses bugzilla, file a bug and submit the patch as an attachment. If not, post a message to the mailing list and attach your patch as an email attachment of type text/plain (don't put it inline in the message body, since it might get line-wrapped and it will then be much more work to apply it; making it text/plain means that everyone can read it easily in their mailer, without having to save it as a file). If the patch is very large, then consider putting it on a web site and sending a link to it rather than posting it as email.
Depending on the patch, it may be helpful to include a few design comments: "I fixed it in foo() rather than in foo_sub() because that seemed more consistent with the way the gorpify() functions worked." Show that you're trying to be consistent with the existing design. Of course, if you aren't attaching to an existing bug report, include a clear description of what you're fixing or adding, and why.
Finally, be willing to rewrite a time or two. It may turn out that they want little things tweaked in the patch before it actually gets checked in; that's normal, so don't take it personally if the first patch isn't taken as-is.