Or sometimes I probably just used the existing image. I don't remember. Unless your code depends on something being not defined, or you deleted a lot of old code, it wouldn't really make any difference.
If I ever had a lot of code, I could have built the image offline (though Lisp compilers are pretty fast). If I had a lot of code to build and database migrations to handle on a live database, it could have gotten hairy. I guess that's just the price I would have had to pay for the added flexibility of being able to build my image offline. IME, complex database migrations in production can get hairy on any language/platform.
It's not really that much different from Python or Ruby, is it? They have a "repl" and an "image", except they're much weaker and not formally defined, or portable, or able to be saved or loaded, or anything like that. But if Python 4 was released today and Guido said "there's now functions to load and save images!", it's not like every Python deployment in the world would stop working. Everyone would have a new mechanism they could use, or not, as they liked, but it wouldn't affect their existing procedures.
EDIT: To clarify, I know that Lisp and Smalltalk have a fuzzy-to-nonexistent line between code and data. What I want to know is how you'd manage that in practice. If I'm changing an existing piece of software, I don't want to expose production users to its incomplete state, so I can't change the running production site directly. (Right?) But if I'm developing locally, or even in a staging version, I don't want to transfer my test/sample data.
Maybe a better question is, "What is a typical workflow like for modifying production software?"
To quote an old lisp saying, code is data is code.
If I want to do an image deploy, what I'll do is load the libraries into the image as part of the build, set up a 'main' function that the executable will trigger, then main will read any config files and go. This image will be an actual executable for the OS. The receiver will execute the executable and not know the difference.
edit: In terms of version control, each deploy to production would generate a tag.
Though if you were making a program that should be deployed at the end user you would properly make it read and write files or communicate with a server over the internet and obviously that kind of data cannot be put into the image.
In a modern Common Lisp web server set up, you'll likely have a SWANK connection available for debugging and patching. You will have to write live-patching code to correctly lock, drop/unbind old definitions, rebind new definitions, and unlock. It will likely remind you of a DB migration script.
For a non-live upgrade, you'll have a process that starts up the new server image, gets it ready to receive connections, then switch the routing & drop the old server. It's not too different from other systems, IMO.
I think that may have been part of why the Lisp Machines died - not only were they obscenely expensive, but they were extremely expensive single user machines. This is fine if your entire development team is one MIT supergenius but becomes problematic when you merely have a handful of rockstars.
Typical lisp development today usually involves using git and loading files into the image using SLIME.
And, yes, LispMs were very expensive and cpu-performed less well than a risc box.