4. Complete Migration Checklist
PHASE 0 — Assessment & Planning (Days 1–3)
Before creating any environment or writing any code.
Read Frappe v14, v15, v16 GitHub changelogs and CHANGELOG.md for all three versions
Run FRAPPE_DEPRECATION_WARNINGS=1 on v14 production; capture all warnings
Audit all custom apps: hooks.py, Python API calls, JS API calls, Vue components
Identify all scheduler_events and validate cron expressions against v16 syntax
Identify all third-party pip packages; check Python 3.11 compatibility
Confirm Redis version (7+ needed for v16), MariaDB version (10.6+ min), OS (Ubuntu 24.04+)
Define go/no-go criteria for V15 UAT and V16 UAT separately
Write rollback plan for each hop (V14 rollback from V15 attempt; V15 rollback from V16 attempt)
Provision two staging servers (one for V15, one for V16)
Take V14 production backup; verify backup integrity via test restore
PHASE 1 — V15 Staging Bench Setup (Days 3–6)
ENVIRONMENT 2: Validate V14 → V15 hop.
Install Python 3.10+ via pyenv on V15 staging server
Install Node 18+ via nvm on V15 staging server
Install Redis 6+ on V15 staging server
Install MariaDB 10.11 on V15 staging server
Install latest bench CLI: pip install frappe-bench
bench init frappe-bench-v15 --frappe-branch version-15
bench get-app for all apps on their version-15 branches
bench get-app for all custom apps on v15-compat branches
Create site and restore V14 production DB dump
Convert DB to utf8mb4: ALTER DATABASE ... CHARACTER SET utf8mb4 ...
bench --site <site> migrate — monitor and log all output
Resolve all migrate errors; re-run until fully clean
bench build --production — resolve all asset build errors
bench setup supervisor --yes && sudo supervisorctl update
Smoke test: login, create/submit documents, trigger background job, check scheduler
PHASE 2 — V15 Custom App Remediation (Days 5–9)
Fix all custom apps for V15 compatibility.
Create v15-compat branch in every custom app repository
Rename jinja_filters hook to jinja in all hooks.py files
Replace on_login / on_logout hooks with on_session_creation / on_session_update
Fix all frappe.db.sql() calls missing explicit values tuples
Fix frappe.db.set_value() calls relying on implicit auto-commit
Remove all frappe.utils.make_esc() usages
Update website_route_rules entries with required v15 fields
Update app_include_js / app_include_css paths for v15 asset path structure
Run pytest suite on V15 staging for each custom app
Fix all failing tests before proceeding
PHASE 3 — V15 Staging UAT (Days 9–11)
User acceptance testing on V15. Gate before V16 work begins.
Conduct full functional testing across all modules on V15 staging
Test all custom print formats and PDF generation
Test all custom reports and dashboards
Test all email notifications and webhooks
Test all third-party integrations
Conduct UAT with key users and collect written sign-offs
Take clean V15 DB backup after successful UAT: tagged v15-clean
Verify V15 backup can be restored cleanly (test restore to temp DB)
Obtain management go/no-go approval to proceed to V16 phase
PHASE 4 — V16 Staging Bench Setup (Days 11–13)
ENVIRONMENT 3: Validate V15 → V16 hop.
Provision second staging server (or repurpose V15 staging after V15 UAT)
Install Python 3.11+ via pyenv on V16 staging server
Install Node 18+ via nvm on V16 staging server
Install Redis 7 from official Redis apt repository
Install MariaDB 10.11 on V16 staging server
bench init frappe-bench-v16 --frappe-branch version-16
bench get-app for all apps on their version-16 branches
bench get-app for all custom apps on v16-compat branches
Create site and restore the V15 clean DB backup (NOT the original V14 backup)
bench --site <site> migrate (v16 patches only — much fewer than a direct v14→v16 jump)
Resolve all migrate errors; re-run until clean
bench build --production (expect more errors here due to Vue 3 / Vite changes)
bench setup supervisor --yes && sudo supervisorctl update
Full smoke test across all modules
PHASE 5 — V16 Custom App Remediation (Days 12–15)
Fix all custom apps for V16 compatibility.
Create v16-compat branch from v15-compat (not from main) for each custom app
Migrate all Vue 2 components to Vue 3 Composition API
Convert all frappe.call() from callback style to Promise .then() chains
Add vite.config.js to each custom app; remove webpack.config.js
Update hooks.py: app_include paths for Vite dist output, cron strings, doc_events priority
Fix frappe.db.exists() usages relying on return value as doc name
Fix frappe.get_all() calls with or_filters as positional argument
Update frappe.whitelist() decorators to include methods=['GET'/'POST']
Replace frappe.session.data.user_type with frappe.get_user().doc.user_type
Update all frappe.utils.dateutils imports to frappe.utils.date_utils
Resolve all Python 3.11 package conflicts in requirements.txt
Run pytest suite on V16 staging for each custom app
PHASE 6 — V16 Staging UAT (Days 15–17)
Final user acceptance testing before production cutover.
Full functional testing: all modules, all workflows
Test all background jobs end-to-end (enqueue, process, result)
Test all scheduler jobs by triggering via bench execute
Test all custom print formats, PDF generation
Test all email notification templates
Test all REST API / webhook integrations
Test file upload and attachment handling
Performance test: simulate peak concurrent users
Conduct UAT with key users; obtain written sign-offs
Run second V16 staging migration from scratch (fresh V15 backup → V16 migrate) as final rehearsal
Finalise production cutover runbook with step-by-step commands and timings
Obtain final management go/no-go for production cutover
PHASE 7 — Production Cutover (Day 18)
Two-hop production migration: V14 → V15 bench → V16 bench.
Announce maintenance window; enable read-only notice for users
Take FRESH full backup of V14 production: mysqldump + bench/sites/ snapshot
Verify fresh backup by checking row counts against known totals
bench set-maintenance-mode on for all production sites
Stop all workers: sudo supervisorctl stop all
--- HOP 1: V14 Production DB → V15 Production Bench ---
On V15 production bench server: restore fresh V14 backup to MariaDB
Convert DB to utf8mb4 (if not already done on production DB)
bench --site <site> migrate (V15 patches) — log all output with timestamps
bench build --production on V15 bench
Quick smoke test on V15: login, open key DocType, verify data integrity
Take V15 bench backup: mysqldump tagged v15-prod-cutover
--- HOP 2: V15 Production DB → V16 Production Bench ---
On V16 production bench server: restore v15-prod-cutover backup to MariaDB
bench --site <site> migrate (V16 patches) — log all output with timestamps
bench build --production on V16 bench
bench setup supervisor --yes && sudo supervisorctl reread && sudo supervisorctl update
bench setup nginx --yes && sudo nginx -t && sudo systemctl reload nginx
bench set-maintenance-mode off
Full smoke test: login, create/submit documents, trigger background job, generate PDF, send email
Monitor bench logs in real time for 30 minutes post-go-live
Announce go-live to users; begin hypercare period
PHASE 8 — Post-Live Stabilisation (Days 19–20+)
Hypercare, issue resolution, performance tuning.
Daily log review for first 5 business days: bench logs --all
Monitor background job queue depth in Frappe desk daily
Address all P1 issues within 24 hours; P2 within 72 hours
Run bench doctor daily for first 3 days post go-live
Confirm all scheduled jobs firing on correct schedule
Tune worker concurrency based on observed queue lengths
Schedule user re-training sessions for changed workflows (Vue 3 desk UI differences)
Update all internal developer documentation to Frappe v16 APIs
Decommission V14 and V15 staging benches after 30-day post-go-live stability period
Conduct migration retrospective; update this document with lessons learned