Schema Evolution

Schema changes in Causet fall into two categories: safe changes you can deploy with no ceremony, and breaking changes that require a migration strategy.


Safe changes (additive, deploy freely)

These changes can be deployed without a rebuild or data migration:

Events

  • Add an optional payload field
  • Add a new event type
  • Add a new action
  • Add a new projection

Projections

  • Add a new column (fields: + derive:)
  • Add a new index
  • Add a new event type to source_events
  • Add a new aggregate
  • Add a new mutation op for an existing event

Queries

  • Add a new query
  • Add a new optional input field
  • Add a new WHERE clause (restricts results further)
  • Increase limit

Note: Adding a column to a projection requires a redeploy to apply the DDL. The column will be NULL for existing rows until events are replayed.


Breaking changes (require migration strategy)

These changes require care:

ChangeRiskStrategy
Remove event payload fieldBreaks projections, rules that reference itDeprecation period: deploy new IR without references first
Rename event payload fieldBreaks all referencesVersion the event (V2 pattern)
Change payload field typeType mismatch in projectionsVersion the event
Change projection primary keyExisting rows become orphanedRebuild (truncate + replay)
Remove a projection columnQueries referencing it breakRemove query references first, then column
Change query input typeClient breakageVersion the query
Rename event typeBreaks all source_events referencesVersion the event

V2 event pattern

When an event’s payload shape needs to change:

  1. Introduce a new event type with V2 naming:

    events:
      TICKET_PURCHASED:      # old — keep for backward compatibility
        ...
      TICKET_PURCHASED_V2:   # new — enhanced payload
        state: ticket
        payload:
          ticket_id:   string
          user_id:     string
          show_id:     string
          seat_number: string   # new field
          price_cents: int      # new field
  2. Emit both from actions during transition:

    side_effects:
      rules:
        - name: emit_purchase
          then:
            - op: emit
              event_type: TICKET_PURCHASED_V2
              payload:
                ticket_id:   intent.ticket_id
                user_id:     intent.user_id
                show_id:     intent.show_id
                seat_number: intent.seat_number
                price_cents: intent.price_cents
  3. Update projections to source from the new event type.

  4. Rebuild affected projections to backfill historical V2 events.

  5. After full migration: deprecate and remove TICKET_PURCHASED.


Column removal

Projection columns are not automatically removed on deploy (IR-driven DDL is additive only).

To remove a column:

  1. Remove all query references to the column.
  2. Remove the column from the projection fields: and derive:.
  3. Deploy new IR.
  4. Manually run DDL: ALTER TABLE ... DROP COLUMN ...

Warning: Remove query references before dropping the column. Dropping a column with active queries causes query failures.


Primary key changes

Changing a projection’s primary key is a destructive operation.

Procedure:

  1. Create a new projection with the correct primary key and a different name.
  2. Replay events to build the new projection.
  3. Update queries to reference the new projection.
  4. Deploy query changes.
  5. Remove the old projection from DSL.
  6. Drop the old table manually.

IR versioning

Every compile produces a new IR version (hash). Active version per fork is managed by causet-saas-cloud.

  • Rolling back IR: deploy the previous release in the control plane.
  • Rollback safety: projection DDL is additive — rolling back IR does not drop columns.
  • Stale IR: projection worker and query service may cache the IR (Redis TTL). After deploying, wait for cache refresh or restart services.

Compiler validation

The compiler validates cross-references at compile time:

  • Events referenced in source_events must exist in the events: section
  • Fields referenced in derive: must exist in the event payload schema
  • Fields referenced in query where: must exist in the projection fields:

If you rename an event or remove a payload field, the compiler will catch all references and report errors before the release is deployed.

Run the compiler in CI on every PR to catch schema evolution issues early.