The 1.4.0 release introduces six breaking changes that affect most existing installations. Chief among them is the new slug system, which replaces underscores with hyphens and enforces lowercase across all URL segments. This guide walks through each change, provides before-and-after code comparisons, and includes a migration script that automates 80% of the upgrade process.
The slug migration is the change with the widest blast radius. Every URL on your site that contains an underscore will break unless you run the migration script. JekCMS 1.4.0 includes a CLI command: php tools/migrate-slugs.php --dry-run lists every affected record before making changes. Run it on a database backup before touching production.
Config Superglobal Removed
The second significant change is the removal of the global $config superglobal. Configuration values must now be accessed through Config::get('key') or the equivalent helper function config('key'). Any theme or plugin that reads $config directly will fail with an undefined variable error on 1.4.0. Search your codebase with grep -r "\$config\[" . before upgrading.
Database-Based Session Storage
Session handling has changed from file-based to database-based storage. This improves reliability on multi-server setups and eliminates the permissions issues that caused intermittent login failures on some shared hosts. The migration requires running php tools/migrate-sessions.php before the first request hits the upgraded installation. Users already logged in will be signed out once.
The Migration Script
The migration script at tools/migrate-1.4.php handles slug rewriting, internal link replacement in post content, cache flushing, and sitemap regeneration. It logs every change to logs/migration-1.4.log. The 80% automation figure excludes custom post type slug remapping, which requires a type_map configuration file that you provide.
Three More Breaking Changes
Beyond the slug system, config superglobal removal, and session storage migration, three additional breaking changes affect specific use cases.
Change 4: Media Path Format
The media path stored in the database no longer includes the uploads/ prefix. Previous versions stored paths like uploads/images/2026/01/photo.avif; version 1.4.0 stores images/2026/01/photo.avif and prepends UPLOADS_URL at render time. This prevents the double uploads/uploads/ bug that occurred when sites migrated between domains. The migration script handles this automatically, but custom queries that concatenate paths must be updated.
// Before (v1.3)
$url = SITE_URL . '/uploads/' . $post['featured_image'];
// Result: https://site.com/uploads/uploads/images/photo.avif (BUG)
// After (v1.4)
$url = UPLOADS_URL . '/' . $post['featured_image'];
// Result: https://site.com/uploads/images/photo.avif (CORRECT)
Change 5: Hook Priority System
Hook priorities changed from a 1–100 integer scale to a 1–999 scale. Any hook registered with a priority above 100 in v1.3 will execute at a different relative position in v1.4. The migration script scans functions.php files for priority values and warns about potential conflicts, but does not modify them automatically because the correct priority depends on the theme's intent.
Change 6: Deprecated Helper Removal
Twelve helper functions deprecated in v1.2 have been removed. The most commonly used were the_title() (replaced by get_post_title()), the_content() (replaced by get_post_content()), and site_info() (replaced by Config::get('site.name')). Running php tools/check-deprecated.php scans your theme and lists every occurrence of a removed function.
Rollback Procedure
If the migration fails or produces unexpected results, the rollback procedure is straightforward:
- Restore the database from the backup taken before migration
- Replace the
core/directory with the v1.3 version - Clear the
cache/directory completely - No configuration file changes are needed — v1.3 config files are forward-compatible
The migration script creates an automatic database backup at backups/pre-migration-{timestamp}.sql before making any changes. This file is a complete mysqldump output that can be imported directly. On a 500-post site with 2,000 media entries, the backup file is approximately 45 MB and the entire migration takes 12–18 seconds.
Post-Migration Verification Checklist
After running the migration, verify these items before considering the upgrade complete:
- Visit 5 random post URLs and confirm they load without redirects
- Check Google Search Console for crawl errors 48 hours after migration
- Verify that
sitemap.xmlcontains only 200-status URLs (no redirects) - Test the admin login flow — sessions have been reset, so every user must log in again
- Run
php tools/check-deprecated.phpone final time to confirm zero warnings - Confirm that API integrations (n8n, webhooks) still authenticate successfully with JWT tokens
Handling Custom Post Types During Migration
The migration script's 80% automation figure excludes custom post types because their slug remapping rules vary by implementation.
If your site uses custom post types — such as portfolio items, testimonials, or product listings — you need to create a type_map.json file in the tools/ directory before running the migration. This file maps each custom post type's old slug pattern to its new format. The structure is straightforward: each key is the post type identifier, and each value is an object with old_prefix and new_prefix fields.
For example, a portfolio post type that previously used portfolio_item_ as its slug prefix would need a mapping to the new hyphenated format portfolio-item-.
The migration script reads this file, applies the slug transformations to all posts of that type, updates internal links in post content that reference those slugs, and regenerates the sitemap entries accordingly.
Without the type map file, the script skips custom post types entirely and logs a warning for each one it encounters. You can run the migration, create the type map afterward, and re-run the script — it is idempotent and will only process records that have not yet been migrated. Sites with no custom post types can safely ignore this step; the script detects their absence and proceeds without prompting.