We want to maintain 3 webservices for the different steps of deployment, but how do we define in our application which service to use? Do we just maintain 3 web references and ifdef the uses of them somehow?
Don't maintain the differences in code, but rather through a configuration file. That way they're all running the same code, just with different configuration values (ie. port to bind to, hostname to answer to, etc.)
My recommendation would be to keep this information in configuration files for the application. Even better would be to inject the appropriate values for a given environment into the configuration during the build process, assuming your build process has some kind of macro-replacement functionality. This way you can create a targeted build for a given environment and not have to change the configuration every time you do a build for a different environment.
Put the service address and port into your application's configuration. It's probably a good idea to do the same thing in the service's config, at least for the port, so that your dev service listens on the right port. This way you don't have to modify your code just to change the server/port you're hitting.
Using config rather than code for switching between dev, stage, and production is very valuable for testing. When you deploy to production you want to make sure you're deploying the same exact code that was tested, not something slightly different. All you should change between dev and production is the config.
As others have mentioned you'll want to stash this information in a config file. In fact, I'd suggest using a different configuration file for each environment. This will address the inevitable problem of having multiple settings for each environment, e.g. you might have separate settings for the web service URL and web service port or have some extra settings to deal with https/security.
All that said, make sure you address these potential issues:
If the web service does anything particularly essential to the application you might want to marry the application to web services in each environment (i.e. have a version of your application in each environment). Certainly, any changes to the interface are easier when you do it this way.
Make sure it's obvious to someone which version of the web service you are speaking with.
All the stuff that can change from dev to test to prod must be configurable. If you can afford to build the process that updates these variable things during the installation of your product -- do it. (Baking the customizations into the build seems like an inferior idea -- you end up with a bunch of different incompatible builds for the same version of the source code)
When I last worked on a project with a web server, we dealt with this problem as follows:
msbuild /t:deploy
would build & deploy to a test environment that was partially shared by the team, and partially dev-specific. The default value for$(SERVER)
was$(USERNAME)
.msbuild /t:deploy /p:server=test
would deploy to the shared test environment, which non-devs could look at.msbuild /t:deploy /p:server=live
would deploy to live server. I think I added an extra handshake, like an error unless you had/p:secret=foo
, just to make sure you didn't do this by accident.