1 · Ship — code → test → deploy
Objective — get a finished local change live the same safe way every time: pick small-vs-big, work on develop, pass the database safety gate, then deploy develop → staging → production and tag the release.
Background
Section titled “Background”Shipping is dangerous in exactly two ways: a migration can drop data, and a bad merge can take the site down. This workflow removes both risks by making the path identical every time — a small change and a big feature flow through the same gate, so you never improvise on the day something breaks.
For the command-only card (Patterns A–D, quick smoke checks), use the Code → test → ship runbook.
The branch model is fixed: you work on develop, promote to staging to test, promote to production to go live, then mirror production into main as a backup. staging and production are real servers; develop and main are local-only.
flowchart LR D["develop<br/>(work here)"] --> S["staging<br/>(test server)"] S --> P["production<br/>(live)"] P --> M["main<br/>(backup)"]1. Decide small or big
Section titled “1. Decide small or big”Before touching code, size the change. A one-line fix and a multi-file feature take different paths into git — getting this right keeps your history clean and your develop branch deployable.
-
Classify the change against the size table.
Size Definition Action Small 1–3 commits, simple change Work directly on developBig 4+ commits, multi-file feature Create a temporary branch - ✅ You know whether you’re working directly on
developor on aNewFeature/...branch.
- ✅ You know whether you’re working directly on
2. Confirm the starting state
Section titled “2. Confirm the starting state”Stale or out-of-sync branches are the most common cause of a confusing deploy. Verify you’re on develop, up to date, and that staging/production carry nothing develop doesn’t.
-
Pull develop and confirm the branches are synced.
Terminal window git checkout develop && git pull origin developgit log --oneline develop..staging # Expected: empty (no commits)git log --oneline staging..production # Expected: empty (no commits)# Expected: on develop, up to date, both log commands print nothing- ✅ You’re on
develop, up to date, and both range logs are empty.
- ✅ You’re on
If a server had admin-panel changes that aren’t in git yet, capture them with Server sync before you ship — otherwise this deploy can overwrite them.
3. Do the work and commit
Section titled “3. Do the work and commit”Make the change, test it locally, and commit with the right type prefix so the history reads as a log of what kind of change shipped when.
-
For a big change, branch first. Skip this for a small change on
develop.Terminal window git checkout -b NewFeature/descriptive-name# Expected: switched to a new branch named NewFeature/descriptive-name- ✅ Big changes live on their own branch; small changes stay on
develop.
- ✅ Big changes live on their own branch; small changes stay on
-
Make the change, test locally, then commit with a typed message. Test in the browser at your local site (e.g.
PROJECT.test) before committing.Terminal window git add [files]git commit -m "🔨 🟪 T3 Add-Feature: [description]"# Bug fix instead: 🔧 🟦 T2 Fix-Bug: [description]# Config change: 🔧 🟦 T2 Setup-Config: [description]git push origin [branch]# Expected: the commit lands and pushes to its branch- ✅ The change works locally and is committed with a type prefix that matches it.
4. Clear the database safety gate
Section titled “4. Clear the database safety gate”This is the non-negotiable gate before any deploy that might include migrations. A migration that drops a table or column can destroy customer data — so you inspect every pending migration and let Atlas lint flag the dangerous ones before they ever reach a server.
-
List pending migrations, then dry-run them to read the SQL.
Terminal window php artisan migrate:status # which migrations are pendingphp artisan migrate --pretend # the SQL each would run — no DB change# Expected: a clear list; if nothing is pending, the gate is already clear- ✅ You can see exactly what SQL would run — or confirm nothing is pending.
-
Run Atlas lint and STOP on a destructive finding. Atlas flags dangerous operations by code; the two that block a deploy are
DS102(DROP TABLE) andDS103(DROP COLUMN).Terminal window atlas migrate lint --env local --latest 1# Expected: no DS102 / DS103. STOP if either appears — back up the DB and get explicit approval first.- ✅ Lint is clean, or a destructive finding was handled (database backed up, explicit approval obtained, tested on staging first).
5. Deploy develop → staging → production
Section titled “5. Deploy develop → staging → production”With the gate clear, run the standard deploy sequence. Each environment gets a deploy, then a human test, before the next one — staging catches what local missed; production is verified the moment it’s live.
-
For a big change, merge the feature branch back into develop first. Skip for small changes already on
develop.Terminal window git checkout develop && git merge NewFeature/descriptive-name# Expected: the feature branch is merged into develop- ✅ All the work is on
developbefore promotion begins.
- ✅ All the work is on
-
Push develop, then promote to staging and deploy.
Terminal window git push origin developgit checkout staging && git merge develop && git push origin stagingdep deploy staging# Expected: zero-downtime release completes on the staging server- ✅ Staging deployed cleanly — then 👤 test on the staging URL and confirm the change works.
-
Promote staging to production and deploy. Only after staging tested clean.
Terminal window git checkout production && git merge staging && git push origin productiondep deploy production# Expected: zero-downtime release completes on the production server- ✅ Production deployed — then 👤 test critical paths on production (login, checkout, the changed feature).
6. Tag and finalize
Section titled “6. Tag and finalize”A live deploy isn’t done until it’s labelled and backed up. Tag the version, mirror production into main, and return to develop so the next change starts from a clean, deployable base.
-
Tag the release, update main, and return to develop. Version scheme: first update after a major is
vX.X.X-a; subsequent ones-b,-c; a significant change gets a newvX.X.Y.Terminal window git tag -a v[VERSION] -m "[Description]"git push origin v[VERSION]git checkout main && git merge production && git push origin maingit checkout develop# Expected: tag pushed, main mirrors production, you're back on develop- ✅ The version is tagged,
mainmatchesproduction, and you’re ondevelopagain. UpdateCHANGELOG.mdif the change was significant; delete the temp branch only with user confirmation.
- ✅ The version is tagged,
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🤖 Branches synced — on
develop, up to date;develop..stagingandstaging..productionboth empty. - 🤖 Committed with type — the change is committed with the right prefix (
T3 Add-Feature,T2 Fix-Bug, etc.). - 🔀 DB gate cleared —
migrate:statusreviewed, Atlas lint clean (or destructive finding backed up + approved). No DS102/DS103 shipped unguarded. - 👤 Staging tested — the change verified on the staging URL before promoting.
- 👤 Production tested — critical paths verified on production after deploy.
- 🤖 Tagged + main updated — version tag pushed,
mainmirrorsproduction, back ondevelop.