Debugging a Failed Booking System Migration: Race Conditions, Template Escaping, and Staged Deployments
During a recent development session, an attempted fix to address a booking calendar race condition on sailjada.com introduced several critical issues that cascaded across 22+ HTML pages. This post details what went wrong, how we identified the problems, and the decision-making process for remediation.
The Original Problem: jadaOpenBook Race Condition
The initial issue was legitimate: the jadaOpenBook() function was opening the booking modal before availability data had finished loading from the API. This created a poor UX where users could interact with an empty calendar.
The attempted fix modified /Users/cb/Documents/repos/sites/sailjada.com/index.html and all downstream pages to add a loading state check:
if ({{ isLoading: false }}) {
jadaOpenBook();
}
However, this introduced a critical flaw: Python format-string syntax ({{ }} double-braces) was left in the JavaScript context, which is invalid syntax.
Scope of the Damage
The migration affected files across multiple deployment paths:
- Primary site:
/sailjada.com/index.html(12+ edits) - Release candidate:
/sailjada.com/releases/rc1/index.html(2 edits) - Secondary site:
/queenofsandiego.com/(22+ HTML pages with jadaOpenBook references) - Staging bucket:
s3://queenofsandiego.com/_staging/sailjada/(full deployment attempted)
The session logs show 23 broken HTML files total, all containing the malformed JavaScript-in-Python-template syntax.
Root Cause Analysis
Three interconnected issues emerged:
- Template Language Confusion: The codebase uses Python format-string templating (
{STRIPE_LINK},{{ css: values }}) for server-side or build-time substitution. The fix didn't account for this—JavaScript variables cannot use double-brace syntax. - CSS vs. JavaScript Context: Legitimate double-brace usage exists throughout the HTML for CSS custom properties and animations (
{{ transform: translateX(0) }}). The fix conflated CSS context with JavaScript context. - No Pre-Deployment Validation: The changes were staged to
s3://queenofsandiego.com/_staging/sailjada/without syntax validation. Git history was available but not consulted during the migration.
Identification and Remediation Strategy
We discovered the issue through a systematic approach:
- Pattern matching: Searched for all
{{occurrences across HTML files to quantify the scope - Contextual analysis: Distinguished legitimate CSS double-braces from broken JavaScript ones
- Production comparison: Fetched live files from
s3://queenofsandiego.com/CloudFront origin bucket and diffed against local staging - Git archaeology: Checked commit history to understand the original jadaOpenBook implementation
The production files contained the full, working booking system. The staged files were incomplete—the attempted fix had stripped out large portions of the original booking logic.
The Fix: Restore from Production
Rather than attempt to salvage the broken migration, we restored all 23 files from the production S3 bucket:
aws s3 sync s3://queenofsandiego.com/ /local/path/sailjada.com/ \
--exclude "*" \
--include "*.html" \
--exclude "_staging/*"
This restored the original, working booking system state across:
index.html(main landing page)releases/rc1/index.html(release candidate)- All 20+ secondary pages with booking widgets
We then deleted the broken staging deployment:
aws s3 rm s3://queenofsandiego.com/_staging/sailjada/ --recursive
Infrastructure Context
The deployment topology is:
- Production origin:
s3://queenofsandiego.com/(primary hosting bucket) - Staging path:
s3://queenofsandiego.com/_staging/(same bucket, different prefix) - CDN: CloudFront distribution (invalidation strategy: path-based)
- DNS: Route53 CNAME/ALIAS records point to CloudFront endpoint
- Secondary repo:
/Users/cb/Documents/repos/sites/sailjada.com/(local source)
This single-bucket staging approach works well for review but requires careful validation before sync operations to avoid overwriting production.
The Real Race Condition Still Needs Fixing
The original booking calendar race condition remains unfixed. The correct approach is to:
- Check availability fetch state in JavaScript: Query an actual boolean variable, not template syntax
- Delay modal open: Use a callback or Promise chain to wait for
availabilityDatato populate - Validate in browser console: Test manually before staging
- Unit test the booking flow: Add e2e tests that verify calendar is interactive only after data loads
Example of the correct pattern:
function jadaOpenBook() {
if (window.availabilityLoading) {
// Wait for availability to load
const checkInterval = setInterval(() => {
if (!window.availabilityLoading) {
clearInterval(checkInterval);
// Open modal with valid data
openBookingModal();
}
}, 100);
} else {
openBookingModal();
}
}
Key Decisions and Lessons
- Restore vs. Patch: When migration scope is large and validation fails, restore from last-known-good state is safer than attempting surgical fixes.
- Template Language Awareness: Document which files use Python templating, Jinja2, or other server-side processors. This prevents context confusion.
- Staging Validation Gates: Require syntax checking (HTML validators, JavaScript linters) before promoting from local to staging.
- Automated Diffs: Generate side-by-side diffs between production and staged changes. Large line-count deltas (e.g., 300+ lines removed) should trigger review gates.
What's Ready for Production / What Needs Testing
Ready to push: All current production files in s3://queenofsandiego.com/ are stable and contain the full working booking system. No changes pending.