It is easy to place a deployment notification too early. The code has reached the server, Git shows the new commit, and the system tells the team: the release succeeded.
But after the Git revision changes, the application can still fail during composer install, frontend build, migrations, cache rebuild, queue worker restart, or Reverb restart. Even if all commands exit successfully, a real HTTP request can still return 500.
For me, a release notification should mean not "the code was copied" but "the new version passed the final application health check".
git pull Confirms Only One Deployment Step
A Git command answers one narrow question: which revision is currently present in the working tree.
It proves nothing about PHP dependencies, database schema, frontend assets, Laravel caches, or background processes.
git fetch origingit reset --hard origin/main DEPLOYED_SHA="$(git rev-parse HEAD)" echo "Code updated to: $DEPLOYED_SHA"Critical Deployment Work Still Remains After Git
In our Laravel deployment, changing the revision is followed by steps that can each make the new version unusable:
- installing production Composer dependencies;
- frontend build;
- database migrations;
- storage link and filesystem checks;
- clearing and rebuilding Laravel caches;
- restarting queue workers;
- restarting Reverb;
- checking process state;
- leaving maintenance mode;
- the final HTTP health check.
Maintenance Mode Creates the Right Boundary
Users should not see a half-updated application during the critical deployment window. The site therefore enters maintenance mode before active application code changes.
If a critical step fails, the deployment must not announce a successful release. The potentially broken version remains behind a controlled 503 page until recovery or rollback.
Start deployment |Enable maintenance |Update code |Install dependencies |Build assets |Run migrations |Rebuild caches |Restart workers / Reverb |Command Success Is Not Application Health
The pipeline can receive exit code 0 from Composer, migrations, and cache commands. These are useful signals, but they only validate their own operations.
A final application-level check should run after all deployment steps, when user traffic can reach the new version again.
curl \ --fail \ --silent \ --show-error \ --max-time 10 \ https://example.com/health \ > /dev/nullThe Health Check Should Verify More Than Nginx
A 200 response from a static file proves that Nginx works, but it does not necessarily prove that Laravel can boot.
A deployment closeout can separately verify Laravel boot, database connection, frontend assets, queue workers, and Reverb. The final HTTP request then becomes the last gate on top of those checks.
Send the Success Notification After the Gate
The simplest rule is that a success notification is a side effect of a successful deployment outcome, not a side effect of a Git command.
The pipeline first passes all checks, records the deployed SHA, and only then creates the release notification.
DEPLOYED_SHA="$(git rev-parse HEAD)" run_deployment_stepsverify_supervisor_processesverify_reverbdisable_maintenance_moderun_final_health_check notify_release_success "$DEPLOYED_SHA"A Failed Health Check Must Not Produce a Success Notification
If the final health check fails, our deployment model re-enables maintenance mode and fails the job.
A separate deployment failed alert may be appropriate at this point, but it must not be a successful release notification. These are different events with different semantics.
if ! run_final_health_check; then enable_maintenance_mode notify_deployment_failed "$DEPLOYED_SHA" exit 1fi notify_release_success "$DEPLOYED_SHA"Include the Deployed SHA in the Notification
A release notification becomes much more useful when it includes an immutable reference to the commit that was actually deployed.
The message then answers not only "did the release finish?" but also "which exact version passed the health check?" This simplifies rollback, incident analysis, and pipeline correlation.
Release successful Commit: abc1234Environment: productionHealth check: passedWorkers: runningRealtime: runningNotify Only After Deployment State Is Final
If the system stores a deployment record or deployed SHA separately, persist the successful outcome first and create the notification afterward.
The notification then does not become the only evidence that a release succeeded. It simply reports an already recorded deployment state.
What to Test
- Git update succeeds but Composer fails - no success notification;
- migrations fail - no success notification;
- queue worker or Reverb is not RUNNING - no success notification;
- final HTTP health check fails - maintenance returns and the job fails;
- all deployment steps and the health check pass - exactly one success notification;
- deployment retry does not create a duplicate notification for the same successful deployment outcome;
- the notification contains the deployed commit SHA that matches the actual release.
A release notification should report verified application state, not the completion of one command inside the deployment.
Summary
git pull, fetch/reset, or checking out a new commit is only the beginning of deployment, not proof of a successful release.
The success boundary should come after dependencies, assets, migrations, caches, background processes, leaving maintenance mode, and the final application health check.
Only then does a release notification honestly mean what the team reads: the new version is actually deployed and verified.



