ProficientNowTechRFCs
Product RFCs/DB Ops/RFC DBOPS 0002

5. Migration Strategy

RFC-DBOPS-0002                                                    Section 5
Category: Standards Track                            Migration Strategy

5.1 Coexistence Model

The migration from Prisma Client to Kysely follows a dual-path coexistence model. Both query engines operate simultaneously within backend-main during the migration period. Each endpoint is routed to either the Prisma path or the Kysely path at the service layer. The routing is determined by migration status, not by runtime configuration or feature flags.

5.1.1 Coexistence Architecture

During coexistence, the @pnats/schema-manager package continues to provide PrismaService and database configuration. A new Kysely service (or provider) is introduced alongside PrismaService in the NestJS dependency injection container. Tenant module services that have been migrated inject the Kysely provider; services that have not been migrated continue to inject PrismaService.

AspectPrisma Path (during coexistence)Kysely Path (during coexistence)
Query generationTenantPrismaClient via PrismaServiceKysely instance via Kysely provider
Connection poolPer-tenant Prisma Engine internal poolShared pg.Pool
Schema scopingImplicit via client configurationExplicit via withSchema(tenantId)
Type safetyPrisma-generated typesKysely database interface types
Rollback mechanismOriginal Prisma service code remains in placeRevert injection to PrismaService

5.1.2 Connection Budget

During coexistence, total database connections are the sum of the pg.Pool maximum and the aggregate Prisma Engine connections for non-migrated endpoints. The migration strategy MUST monitor connection utilization and adjust pool sizing if the combined connection count approaches PgBouncer or PostgreSQL limits.


5.2 Phase 1 — High-Read Endpoints

Phase 1 targets endpoints characterized by high read frequency and measurable Prisma overhead. These endpoints provide the clearest latency improvement signal and the lowest risk surface for initial migration.

5.2.1 Candidate Selection Criteria

CriterionRationale
High request frequencyMaximizes aggregate latency savings
Read-only or read-dominantAvoids write-path complexity in initial migration
Relation hydration via includeBenefits from explicit join control
Existing benchmark dataValidates improvement against measured baseline
Stable API contractReduces risk of concurrent API and query-layer changes

5.2.2 Initial Candidate Endpoints

The company list endpoint (GET /api/v1/company) is the first migration candidate based on the existing benchmark evidence. Additional Phase 1 candidates SHOULD be selected from modules with similar characteristics:

ModuleEndpoint PatternRationale
CompanyList with relation hydrationBenchmark evidence: 46.9% latency reduction
PositionsList with submission and contact joinsHigh-frequency list view with nested relations
CandidatesList with submission and status joinsRead-heavy with complex relation graph
SubmissionsList with candidate and position joinsFrequently accessed with deep nesting

5.3 Phase 2 — Complex Query Endpoints

Phase 2 extends migration to endpoints with complex query composition: dynamic filters, search, sorting formulas, and multi-predicate logic.

5.3.1 Complexity Factors

FactorDescription
Dynamic filter compositionFilters constructed from filter and filterLogic request parameters
Full-text searchPostgreSQL tsvector search queries currently using Prisma's search parameter
Sort formulasCustom sort expressions including sortFormula and multi-column ordering
Table-scoped viewsQueries parameterized by tableId for user-configured table layouts
Conditional relation loadingRelation subsets that vary based on request context

5.3.2 Migration Approach

Complex query endpoints MUST be migrated by reconstructing the full query composition logic in Kysely's query API. Each endpoint's filter builder, search integration, and sort logic MUST be reimplemented to produce SQL equivalent to the Prisma path for all supported parameter combinations.


5.4 Phase 3 — Write Operations

Phase 3 migrates create, update, and delete operations from Prisma Client to Kysely.

5.4.1 Write-Path Considerations

ConsiderationDescription
Transaction semanticsMulti-step writes that use Prisma's $transaction MUST be replicated using Kysely's transaction
Nested writesPrisma's nested create and connectOrCreate MUST be decomposed into explicit insert sequences
Optimistic concurrencyAny version-based concurrency checks MUST be preserved in the Kysely implementation
Side effectsPost-write hooks or event emissions tied to Prisma middleware MUST be replicated
Return valuesPrisma's automatic return of created/updated records MUST be matched via RETURNING clauses

5.4.2 Transaction Boundaries

Kysely's transaction method provides explicit transaction control. Write operations that currently use Prisma's interactive transactions MUST be migrated to Kysely transactions with equivalent isolation semantics. The transaction scope MUST remain within the same tenant schema, scoped via withSchema().


5.5 Validation Gates

Each endpoint MUST pass through validation gates before its Prisma path is deactivated. Validation gates enforce Invariant 3 (Feature Parity) and Invariant 4 (Zero Data Regression).

5.5.1 Gate Definitions

GateRequirement
Unit test parityAll existing unit tests MUST pass against the Kysely implementation without modification
Integration test parityAll existing integration tests MUST pass against the Kysely implementation without modification
Response equivalenceA dual-execution mode MUST compare Prisma and Kysely responses for a defined sample of production inputs
Performance validationP50 and P95 response times MUST not regress compared to the Prisma baseline for the same endpoint
Schema coverageAll supported query parameters (filters, sorts, search, pagination) MUST produce correct results

5.5.2 Dual-Execution Validation

During validation, an endpoint MAY operate in dual-execution mode where both the Prisma and Kysely paths execute for the same request. The Prisma result serves the response while the Kysely result is compared asynchronously. Discrepancies are logged for investigation.

Dual-execution mode MUST be time-bounded and MUST NOT be used in production for endpoints with write side effects.


5.6 Rollback Mechanics

Rollback capability (Invariant 5) is maintained through code-level retention of the Prisma implementation.

5.6.1 Rollback Approach

AspectDescription
Code retentionPrisma service methods MUST NOT be deleted until the Kysely path passes all validation gates
Injection switchingRollback is achieved by reverting the NestJS provider injection from the Kysely provider to PrismaService
Data compatibilityNo schema changes are introduced by the query-layer migration; rollback does not require data migration
Connection pool cleanupIf Prisma clients are deactivated, rollback reactivates the PrismaManager client cache for affected tenants

5.6.2 Rollback Scope

Rollback operates at the endpoint level. Reverting one endpoint to its Prisma path does not affect other endpoints already validated on the Kysely path. This granularity allows surgical rollback without a full system-wide revert.