Just to add to the other answers:
When you do a release, you may have two versions of your application and you may want to upgrade. Say, you first have lib/foo-1.0.0/ebin/foo_app.beam loaded. Then you install foo-2.0.0 via your preferred method, I use target_system:install("foo", "/tmp/erl-target"). Now I have /tmp/erl-target/lib/foo-2.0.0/.
I use code:which(foo_app) to see which one is loaded. To load it, in this case I would first start the Eshell as such:
$ /tmp/erl-target/bin/erl -boot /tmp/erl-target/releases/1/start
Afterwards I type:
1> application:ensure_all_started(foo).
{ok,[...,foo]}
To see which one is currently loaded, you type (in the same Eshell):
2> code:which(foo_app).
"/tmp/erl-target/lib/foo-1.0.0/ebin/foo_app.beam"
You will need the foo.appup file in /tmp/erl-target/lib/foo-2.0.0/ebin/ with the following contents:
{"2.0.0",
[{"1.0.0",[{load_module,foo_app}]}],
[{"1.0.0",[{load_module,foo_app}]}]}.
To upgrade, you type:
3> release_handler:upgrade_app(foo, 'lib/foo-2.0.0').
{ok,[]}
If everything was successful, you will see the following:
4> code:which(foo_app).
"/tmp/erl-target/lib/foo-2.0.0/ebin/foo_app.beam"
If I added a new function in foo-2.0.0 which I exported, it will become available, for example.
If you want to downgrade back to the previous version, you will have to have a similar foo.appup file in /tmp/erl-target/lib/foo-1.0.0/ebin/, but with the version numbers swapped.
Please correct me if I am wrong, I am new to Erlang. I did test it myself and it worked for me. This is just to upgrade your application within a release, this is not upgrading the release itself.
If something is unclear, let me know. I am still trying to work this out myself. :)