Everything has an automated test that, at the bare minimum, verifies that it will compile. Preferably, everything is tested to at least 95% coverage, with 99% being the goal.
Everything is developed and tested in the developer's environment. This may be a separate machine or it may be a separate port on the same machine. I've done both and there's no benefit vis-a-vis development process. If you check something in, you're asserting that you have done at least some manner of testing. In other words, HEAD will, at the very least, compile.
Either the full test suite is run whenever a checkin occurs (ideal) and/or it runs nightly on some smoke server (good). Best is both.
Did I mention that is under source control? This includes anything that your application might need in order to run.
Any external items (CPAN modules, system commands, etc) and their versions (min and max) are marked as dependencies somewhere in the installation script. It is so annoying when you try and install something and it breaks because it forgot a dependency. (The automake19 port on FreeBSD 5.3 has this problem.)
Everything that happens in the application is logged. Every request, who made the request and from where, how long it took, and if there were any errors. If you have the space (and you probably do), log the response as well. Tracing errors is a lot easier when you can see exactly what your app sent back in response to what.
If an error occurs on prod, it should, at the very least, email someone. Preferably, someone gets paged. Customers love it when you call them 15 minutes after the site says "I can't let you do that, Dave.", apologize for the inconvenience, and inform them of both a workaround and that the problem has been logged as a bug. If you have a pager, rotate it. That way, everyone gets experience.
If you don't have code reviews, write each other's tests.