My understanding (from some experience) is that there are three ways to program an ATMega328 chip (and these apply pretty much to the entire range of chips)
1) high voltage serial/parallel) programming
2) low voltage "in-circuit" serial programming
3) programming via a bootloader.
High voltage programming
This is the "best" way to program an AVR chip. All of them support it, it can be done very quickly, and you can do it even if you have stuffed up the "fuse bits" so that programming via other methods is not possible.
However, almost no hobbyist uses this.
See
here for more information.
Low voltage (in-circuit) serial programming
This is probably the most popular way of programming bootloaders to ATMega chips.
It is possible to program the part in such a way that you can't use this method to program it again. (See
here). You need to take care if you're changing the "fuse" settings for a special bootloader or for programming where you're not using a bootloader.
This method of programming allows you to program the chip so that it used the internal RC oscillator (so you don't need a crystal). I rarely use an ATMega chip with a crystal any more. It makes bare-bones use of the device much easier.
This is usually done using the 6 pin ICSP header that you find hidden on many Arduino boards, and built in to any project I make (well, *you* don't see those).
Here is an example of a tiny programmer made using this method.
You'll find plenty of tutorials for this and many of them look like
this. However they are not what one would call minimalist.
My preferred method is
here. This isn't the one I normally point to (my Google-fu is failing me) but it illustrates the idea. What is important here is that you'll be using non-standard (at least non-standard for an arduino) boot loaders and fuse settings so you can get away without a crystal.
Here are the more official instructions, which concentrate on programming the device from another arduino, but really they're just using the equivalent of the 6 pin header and using the arduino as a "cheap" ICP device. "Cheap" means you probably have it so you don't have to buy one -- cheap in time.
So, practically, I use one of
these. Note that it has a 10 pin header. Some have 6 pin headers built in (like
this). However, I find it generally more convenient to use an adapter like
this.
Oh, and they provide power as well.
They tool a while to arrive when I ordered them the first time, so I used an arduino as the programmer. If you do this (using the sample code in the arduino environment -- I use
Arduino ERW -- then you may have to wire in an extra resistor to
prevent the board resetting while you use it this way.
If you go down this route you'll need settings in the environment, bootloaders, and fuse settings. I think the links will provide eough info, but if you go down this route, contact me and I'll give you a place to start. It will save you a lot of effort.
Programming via a bootloader
This is what the vast bulk of arduino board users use. When it boils down, it is programming via RS232. The board may have a 5 or 6 pin (in a single line) header, or it may have a serial port (which is essentially the same thing), or it may have a USB interface. The usb interface uses a usb to serial chip, so it's again, just another way of using RS232.
Once you've programmed a bootloader, you can use this method.
Look for an FT232L board. They can be
really cheap.
The only differences between the various bootloader methods is resetting and power.
Plain RS232 won't provide power to the board, and may not auto-reset.
5 pin headers provide power, but not auto-reset (and they're rare). 6 pin headers provide auto-reset.
The FT232 boards typically have a 6 pin interface.
These sound great, but they're not as useful as the in-circuit programming.
Oh, and be aware that the 6 pin header for serial programming via a bootloader has nothing in common with the 6 pin header for in-circuit programming.
Also, for any programming method which provides power, you may need to design your board so it doesn't try to run high power peripherals if the board is powered from the programming interface. I generally do this using a schottky diode between the main power rail and the arduino power pins so ICSP power (or indeed power from the serial programming connector) cannot feed back to power other things, but this method may not work for you.