|laziness, impatience, and hubris|
I too am a web developer (primarily). Our shop uses mod_perl (currently for Apache 1, but we've abstracted that out and could use Apache 2).
We have a development/test server. Our svn lives on another box, this gives us about three backups each day (one per developer and one for the svn box).
We have a standard set of templates which are under version control. Each app can also have its own templates (also under version control). Documentation in the templates and in the corresponding app modules is key, but standardization is more important.
We have a home brew application framework (which is open source, but is not ready for public viewing just yet, but look for announcements in the next couple of months). We also have an app generator with its own little language. It makes the sql statements to build our database (in Postgres), the httpd.conf we need to include in our httpd.conf, the Class::DBI subclasses which model our data, the template toolkit wrapper (for navigation) and the controlling modules (with basic CRUD and stubs for other things). There is a normal test suite for the generator, so we don't retest the code it makes later (except to make sure that it compiles).
Once the app is generated, each developer checks out code to their home and hacks away. When something seems right we ./Build test (which for us does compile tests only). Then we restart our personal apache server whose conf has a use lib for our checked out source. This keeps us from affecting the other person(s). If it looks right in a browser, we check in (and possibly announce by turning to the other developer and talking).
If the data model is flawed, we update the description file and regenerate, our generator carefully avoids overwriting code written by hand.
If an app involves cross box communication, we use a second test machine for the purpose. This also allows us to test how connectivity will happen by controlling which firewalls separate machines.
Some of these things would be different if there were more than 2 full time developers or if there were more users of our apps (most of which serve employees). For instance, we might automate testing of the finished pages. Now we just eye ball. Our apps are so similar, and so much of them is generated, that more testing still seems like overkill. Some of our apps haven't had maintanence (neither bug fixes nor feature additions) since I started in March.
For deployment, we ./Build test, then ./Build dist, then move the tar to a staging directory on the prod box. There are multiple prod boxes, but most apps live on only one (that makes things a lot easier and shows the size of our operation). Then we visit the box with ssh and repeat ./Build, ./Build test, ./Build install.