Back to blog
EngineeringDate unavailable· min read

When the Code Ships Before the Constraint

A maintenance migration that tells the story of a database constraint that didn't make it into the original PR—and why keeping repo and production in sync matters.

Sometimes the most interesting commits are the ones that fix what you didn't notice was broken.

PR #223 shipped a new content type to our GTM scheduler: daily_cto_journal. The Python code was updated. The scheduler started using it. Everything worked. But there was a gap.

The gtm_schedule table has a check constraint on the content_type column—an enum-style constraint that validates which content types are allowed. When we added daily_cto_journal to the scheduler code, we didn't update the constraint. The mismatch didn't cause an immediate failure because the scheduler was creating rows in production before the constraint could reject them. But it was a ticking time bomb: any attempt to insert a daily_cto_journal row through the REST API or a future code path would fail with a constraint violation.

The Fix

This migration does exactly what it says: it drops the old constraint and adds a new one that includes daily_cto_journal alongside the existing seven content types (blog, social, product_sync, daily_digest, weekly_digest, weekly_notion_briefing).

The constraint was already applied manually in production to keep the scheduler running. This migration exists to bring the repository migration history in sync with what's actually deployed. That's the philosophy: production is the source of truth, but the repo should always reflect it.

Why This Matters

Maintenance migrations like this one serve two purposes. First, they keep the codebase honest. If someone clones the repo tomorrow and runs migrations from scratch, they should get the same schema that's running in production. Second, they document the gap. This migration's comment explicitly says: 'PR #223 added the content type but the constraint wasn't updated.' That's a record of what happened and why the manual fix was needed.

These kinds of gaps happen when you're moving fast. You ship the feature, you dogfood it, and only later do you realize the schema didn't quite keep up. The important part is recognizing it and closing the loop.

What's Next

This migration closes the gap between code and constraint. The daily_cto_journal content type is now fully supported at every layer. Next up: expanding the CTO journal pipeline to include richer metadata, automated section generation, and integration with the broader GTM scheduler for cross-content coordination. The constraint is fixed. The feature is live. Now we build on it.