Back to Posthog

Phased migration plan for product isolation

.agents/skills/isolating-product-facade-contracts/references/phased-migration-plan.md

1.43.14.7 KB
Original Source

Phased migration plan for product isolation

This reference provides a practical execution sequence for converting an existing product into the facade+contracts pattern used by Visual review.

Phase 0 — Baseline and scoping

Prerequisite: the product must already live under products/<name>/. This workflow does not cover moving code out of posthog/, ee/, or other shared directories — that is a separate step. If the product still lives in legacy/common code, stop here and do the move first.

  • Read architecture docs:
  • Run hogli product:lint <name> — the isolation progress section shows exactly which layers exist, what's structurally wrong with each, and what to do next. Use this as the starting point before reading code.
  • Locate the product's current code under products/<name>/backend/ and identify what's already there vs. what's still missing (models, logic, presentation, facade).
  • Inventory existing tests and gaps in backend/tests/.

Suggested commands:

bash
rg -n "from products\.<target>\.backend\.(models|logic|presentation|tasks|storage)" .
rg -n "products\.<target>\.backend\.facade" .

Phase 1 — Create contract skeleton

Start with read paths before write paths.

  1. Add backend/facade/contracts.py with frozen dataclasses for existing read responses.
  2. Add backend/facade/__init__.py exports to make imports straightforward.
  3. Add minimal domain enums either in contracts.py or facade/enums.py if they grow.

Reference patterns:

Phase 2 — Add thin facade API

  1. Add backend/facade/api.py with thin methods wrapping logic.
  2. Implement _to_contract mapper functions close to facade methods.
  3. Keep method signatures capability-based (list_*, get_*, create_*, etc.).

Reference patterns:

Phase 3 — Migrate low-risk consumers first

Start with call sites that are easiest to verify:

  1. Read-only DRF endpoints.
  2. Internal helper modules that only read.
  3. Any explicit cross-product imports into models or logic.

For DRF endpoints:

  • Ensure schema annotations are present (@validated_request or @extend_schema).
  • Keep serializers as JSON adapters between API payloads and contract dataclasses.

Docs:

Phase 4 — Migrate writes and background flows

  1. Migrate create/update/delete endpoints to call facade methods.
  2. Migrate Celery or async entrypoints to call facade methods, not internals.
  3. Keep transaction boundaries in facade where needed.

Reference patterns:

Phase 5 — Tighten boundaries and clean up

  1. Remove direct callers of internal modules where facade replacements exist.
  2. Update tach.toml dependency/interface declarations to match desired import rules.
  3. Remove obsolete adapters and dead helper functions.

Then verify:

  • Product tests pass.
  • API schema generation remains healthy after serializer/view changes.
  • No cross-product internal imports remain.

Use this template to avoid long, risky PRs:

  • PR A: Add contracts + read-only facade + unit tests for facade mapping.
  • PR B: Move read-only presentation endpoints to facade.
  • PR C: Move write endpoints to facade.
  • PR D: Move tasks/background integrations.
  • PR E: Boundary enforcement and cleanup (tach.toml, dead code removal).

When possible, keep each PR deployable and backward compatible.

Security and isolation checklist

  • All querysets scoped by team.
  • No product-specific fields added to Team.
  • Contracts avoid Django/DRF imports.
  • No cross-product ORM imports.
  • View endpoints declare schemas for request/response.

Additional references: