Every change we do on our DEV DBs is either made as an SQL script files first and then installed to DEV DB or changed in DEV DB directly and immediately exported to SQL script files. Those scripts (organized by application module, object type, etc.) are under source control. When we want to install changes to PROD, we get all changes from source control since last install and append some constant verification scripts to such package (we have an application that does all that and also compiles binaries that have changed too and packages all that for "shipping"; we run it on one of few machines dedicated for only that purpose).
Before installing such package on PROD, we test it on an intermediate local TEST database (one or more for each client DB), which is a copy of PROD (same structure and dictionary data, and a big sample of operational data; it's refreshed on regular basis by importing a dump file made by Oracle Data Pump limited export from PROD DB, not necessarily before/after each install). Usually after that we do a second installation on remote (client) TEST DB (full copy of PROD DB, refreshed also on regular basis), so the client can check the changes by himself.
Yes, sometimes we "loose" some changes, when someone forgets to export from DB, but those are quite rare problems. For such cases we do a limited comparison of structure between DEV and local TEST (comparing only objects which are reported by DB as changed since last install and verifying that those that are changed, are scripted in the package). It does not involve data, so in case of data changes we might miss something. Usually we catch such problem on TEST (local or remote), but sometimes they go to PROD uncaught. Happened twice or thrice in 5 years and fortunately was easy to fix and did not cause major problems for client.