# Risk Assessment: Story 1.1 - Database Schema Extension **Document Type**: Risk Profile **Story**: 1.1 - Database Schema Extension **Date**: 2026-01-15 **Assessment Type**: Brownfield Integration Risk Analysis **Status**: Complete --- ## Executive Summary This risk assessment analyzes the database schema extension for Story 1.1, which adds three new tables (`institutions`, `cohorts`, `cohort_enrollments`) to the existing DocuSeal codebase. The assessment identifies critical integration risks, data integrity concerns, and rollback complexities inherent in brownfield development. **Overall Risk Level**: **MEDIUM-HIGH** **Primary Concerns**: Foreign key dependencies, existing table integration, rollback complexity --- ## Risk Categories ### 1. Technical Risks | Risk ID | Probability | Impact | Severity | Description | |---------|-------------|--------|----------|-------------| | **T-01** | High | High | **CRITICAL** | **Foreign Key Constraint Failures**
Foreign keys to `templates` and `submissions` tables may fail if referenced records don't exist during migration or if existing data violates constraints. | | **T-02** | Medium | High | **HIGH** | **Migration Rollback Complexity**
Rollback may fail due to foreign key dependencies or data integrity issues, requiring manual database intervention. | | **T-03** | Low | High | **MEDIUM** | **Database Compatibility Issues**
Schema may not be compatible with all supported databases (PostgreSQL/MySQL/SQLite) due to JSONB usage or specific syntax. | | **T-04** | Medium | Medium | **MEDIUM** | **Index Creation Performance**
Creating indexes on large existing tables may cause significant downtime or locking. | | **T-05** | Low | Medium | **LOW** | **Schema Version Mismatch**
Migration timestamp conflicts with existing migrations in production. | **Mitigation Strategies:** - **T-01**: Add `ON DELETE CASCADE` or `ON DELETE SET NULL` to foreign keys; validate existing data before migration - **T-02**: Test rollback in staging environment; create backup before migration; use transaction wrapper - **T-03**: Test migration on all three database types; use Rails 7+ compatible syntax - **T-04**: Create indexes concurrently (PostgreSQL); schedule migration during low-traffic period - **T-05**: Use unique timestamp prefix; verify migration order in production --- ### 2. Integration Risks | Risk ID | Probability | Impact | Severity | Description | |---------|-------------|--------|----------|-------------| | **I-01** | **HIGH** | **HIGH** | **CRITICAL** | **Template Reference Integrity**
`cohorts.template_id` references `templates.id`. If templates are deleted or archived, foreign key constraint may prevent cohort creation or cause orphaned records. | | **I-02** | **HIGH** | **HIGH** | **CRITICAL** | **Submission Reference Integrity**
`cohort_enrollments.submission_id` references `submissions.id`. Existing DocuSeal workflows may delete submissions, breaking enrollment links. | | **I-03** | Medium | High | **HIGH** | **Account Table Confusion**
PRD specifies single `institutions` table, but DocuSeal has `accounts` table. Risk of confusion or unintended cross-references. | | **I-04** | Medium | Medium | **MEDIUM** | **Existing Query Performance Degradation**
New indexes or table locks may slow down existing DocuSeal queries (templates, submissions, submitters). | | **I-05** | Low | Medium | **LOW** | **Active Storage Conflicts**
New tables may conflict with Active Storage naming conventions or attachment behaviors. | **Mitigation Strategies:** - **I-01**: Add `restrict_with_exception` to prevent template deletion if cohorts exist; implement soft deletes on templates - **I-02**: Use `dependent: :restrict_with_exception` on CohortEnrollment submission reference; ensure submission lifecycle is managed - **I-03**: Document clearly that `institutions` is independent of `accounts`; no foreign key relationship - **I-04**: Run EXPLAIN ANALYZE on critical queries; monitor query plans after migration - **I-05**: Verify Active Storage table names don't conflict; use explicit table names if needed --- ### 3. Data Integrity Risks | Risk ID | Probability | Impact | Severity | Description | |---------|-------------|--------|----------|-------------| | **D-01** | Medium | **HIGH** | **HIGH** | **Unique Constraint Violations**
`cohort_enrollments` has unique constraints on `[cohort_id, student_email]` and `[submission_id]`. Existing data may violate these. | | **D-02** | Medium | High | **HIGH** | **NOT NULL Constraint Failures**
Required fields (`institution_id`, `template_id`, `student_email`) may receive NULL values during bulk operations. | | **D-03** | Low | High | **MEDIUM** | **JSONB Data Validation**
JSONB fields (`required_student_uploads`, `cohort_metadata`, `uploaded_documents`, `values`) may contain invalid JSON or unexpected structures. | | **D-04** | Low | Medium | **LOW** | **Timestamp Field Consistency**
`deleted_at` soft delete pattern may conflict with existing `archived_at` pattern in DocuSeal tables. | | **D-05** | Medium | Medium | **MEDIUM** | **Default Value Issues**
Default values for `status` fields may not align with business logic (e.g., 'draft' vs 'waiting'). | **Mitigation Strategies:** - **D-01**: Test unique constraints with duplicate data; add database-level validation before migration - **D-02**: Add model-level validations; use `null: false` in migration with proper defaults - **D-03**: Add JSON schema validation in models; use `validate: :json_schema` if available - **D-04**: Standardize on `deleted_at` for new tables; document pattern for future consistency - **D-05**: Review business requirements for default states; add comments in migration --- ### 4. Security Risks | Risk ID | Probability | Impact | Severity | Description | |---------|-------------|--------|----------|-------------| | **S-01** | Medium | High | **HIGH** | **Unauthorized Cross-Institution Data Access**
If multi-tenancy is accidentally enabled, students/sponsors may access data from other institutions. | | **S-02** | Low | High | **MEDIUM** | **Email Data Exposure**
`sponsor_email` and `student_email` stored in plaintext; may violate privacy policies (POPIA). | | **S-03** | Medium | Medium | **MEDIUM** | **Foreign Key Privilege Escalation**
Malicious user could potentially manipulate foreign keys to access unauthorized submissions or templates. | | **S-04** | Low | Medium | **LOW** | **Soft Delete Data Leakage**
Soft-deleted records (`deleted_at`) may still be queryable by users with direct database access. | **Mitigation Strategies:** - **S-01**: Add institution_id validation in all model scopes; enforce single institution in application logic - **S-02**: Implement email encryption at rest using Rails `encrypts` method; review POPIA compliance - **S-03**: Implement proper authorization (Cancancan) for all foreign key references; validate ownership - **S-04**: Implement default scopes to filter deleted records; use paranoia gem for soft deletes --- ### 5. Performance Risks | Risk ID | Probability | Impact | Severity | Description | |---------|-------------|--------|----------|-------------| | **P-01** | Medium | High | **HIGH** | **Query Performance Degradation**
Joining new tables with existing tables may slow down critical workflows (cohort dashboard, enrollment lists). | | **P-02** | Medium | Medium | **MEDIUM** | **Migration Execution Time**
Creating tables with multiple indexes and foreign keys may exceed 30-second threshold. | | **P-03** | Low | Medium | **LOW** | **JSONB Query Performance**
Querying JSONB fields (`cohort_metadata`, `values`) may be slower than structured columns. | | **P-04** | Low | Low | **LOW** | **Index Bloat**
Multiple indexes on small tables may cause unnecessary overhead. | **Mitigation Strategies:** - **P-01**: Use EXPLAIN ANALYZE to optimize queries; implement eager loading; add composite indexes - **P-02**: Test migration timing in staging; use `disable_ddl_transaction!` for index creation if needed - **P-03**: Use JSONB operators efficiently; consider partial indexes on frequently queried JSONB fields - **P-04**: Monitor index usage after deployment; remove unused indexes --- ### 6. Business Logic Risks | Risk ID | Probability | Impact | Severity | Description | |---------|-------------|--------|----------|-------------| | **B-01** | Medium | High | **HIGH** | **State Machine Complexity**
5-step cohort workflow (draft → active → completed) with multiple datetime fields may lead to inconsistent state transitions. | | **B-02** | Medium | Medium | **MEDIUM** | **Single Institution Constraint**
PRD requires single institution per deployment, but schema doesn't enforce this at database level. | | **B-03** | Low | Medium | **LOW** | **Program Type Validation**
`program_type` field accepts free text; may lead to inconsistent data (learnership vs learner-ship). | | **B-04** | Medium | Medium | **MEDIUM** | **Sponsor Email Uniqueness**
Multiple cohorts may share sponsor email; may cause confusion in notifications. | **Mitigation Strategies:** - **B-01**: Implement state machine gem (aasm); add validation callbacks; create state transition tests - **B-02**: Add application-level singleton pattern; database constraint with CHECK or trigger - **B-03**: Use enum or strict validation for program_type; add enum to model - **B-04**: Add business logic validation; consider separate sponsor table if needed --- ### 7. Rollback & Recovery Risks | Risk ID | Probability | Impact | Severity | Description | |---------|-------------|--------|----------|-------------| | **R-01** | **HIGH** | **HIGH** | **CRITICAL** | **Failed Rollback Due to Data Dependencies**
If enrollments reference submissions that are deleted during rollback, migration may fail. | | **R-02** | Medium | High | **HIGH** | **Data Loss During Rollback**
Rollback will drop all new tables, losing any data created during testing or partial deployment. | | **R-03** | Low | High | **MEDIUM** | **Schema.rb Desynchronization**
Failed migration may leave schema.rb out of sync with actual database state. | | **R-04** | Medium | Medium | **MEDIUM** | **Production Rollback Complexity**
Rollback in production requires coordination, downtime, and potential data recovery. | **Mitigation Strategies:** - **R-01**: Test rollback with sample data; add `dependent: :restrict_with_exception` to prevent orphaned records - **R-02**: Create database backup before migration; document data retention policy; test in staging first - **R-03**: Run `bin/rails db:schema:dump` after failed migration; manually verify schema.rb - **R-04**: Create detailed rollback playbook; schedule maintenance window; have database administrator on standby --- ## Risk Severity Matrix ### Critical Risks (Immediate Action Required) 1. **T-01**: Foreign Key Constraint Failures 2. **I-01**: Template Reference Integrity 3. **I-02**: Submission Reference Integrity 4. **R-01**: Failed Rollback Due to Data Dependencies ### High Risks (Requires Mitigation Before Deployment) 1. **T-02**: Migration Rollback Complexity 2. **D-01**: Unique Constraint Violations 3. **D-02**: NOT NULL Constraint Failures 4. **S-01**: Unauthorized Cross-Institution Data Access 5. **P-01**: Query Performance Degradation 6. **B-01**: State Machine Complexity ### Medium Risks (Monitor and Address) 1. **T-03**: Database Compatibility Issues 2. **T-04**: Index Creation Performance 3. **I-03**: Account Table Confusion 4. **I-04**: Existing Query Performance Degradation 5. **S-02**: Email Data Exposure 6. **S-03**: Foreign Key Privilege Escalation 7. **P-02**: Migration Execution Time 8. **B-02**: Single Institution Constraint 9. **B-04**: Sponsor Email Uniqueness 10. **R-02**: Data Loss During Rollback 11. **R-04**: Production Rollback Complexity ### Low Risks (Acceptable or Future Mitigation) 1. **T-05**: Schema Version Mismatch 2. **I-05**: Active Storage Conflicts 3. **D-04**: Timestamp Field Consistency 4. **D-05**: Default Value Issues 5. **S-04**: Soft Delete Data Leakage 6. **P-03**: JSONB Query Performance 7. **P-04**: Index Bloat 8. **B-03**: Program Type Validation 9. **R-03**: Schema.rb Desynchronization --- ## Integration Verification Requirements ### IV1: Existing DocuSeal Tables Remain Unchanged **Risk**: **HIGH** - Accidental modification of existing tables **Verification**: - [ ] Run `bin/rails db:schema:dump` and compare with original schema.rb - [ ] Verify no changes to `templates`, `submissions`, `submitters` tables - [ ] Check that existing indexes and foreign keys are preserved - [ ] Run existing DocuSeal test suite to ensure no regression ### IV2: New Tables Reference Existing Tables Correctly **Risk**: **CRITICAL** - Foreign key failures **Verification**: - [ ] Verify `cohorts.template_id` references valid `templates.id` - [ ] Verify `cohort_enrollments.submission_id` references valid `submissions.id` - [ ] Test with non-existent IDs to ensure foreign key constraints work - [ ] Test with deleted/archived templates/submissions to verify behavior ### IV3: Database Performance Not Degraded **Risk**: **HIGH** - Slow queries affecting user experience **Verification**: - [ ] Run EXPLAIN ANALYZE on 5 critical queries before and after migration - [ ] Measure query execution time (should be < 100ms for simple queries) - [ ] Verify indexes are being used (check EXPLAIN output) - [ ] Monitor database CPU/memory usage during migration ### IV4: Rollback Process Works **Risk**: **CRITICAL** - Failed rollback requiring manual intervention **Verification**: - [ ] Test rollback in staging environment with sample data - [ ] Verify all tables are dropped correctly - [ ] Verify no orphaned foreign key constraints remain - [ ] Verify schema.rb is restored to original state --- ## Recommended Mitigation Actions ### Pre-Migration (Required) 1. **Create Database Backup** ```bash pg_dump docuseal_production > backup_20260115.sql ``` 2. **Validate Existing Data** ```ruby # Check for potential foreign key violations Template.where.not(id: Cohort.pluck(:template_id)).count Submission.where.not(id: CohortEnrollment.pluck(:submission_id)).count ``` 3. **Test on All Database Types** - PostgreSQL (production) - SQLite (development) - MySQL (if supported) 4. **Create Staging Environment** - Mirror production schema - Test migration and rollback - Performance testing ### During Migration 1. **Use Transaction Wrapper** ```ruby ActiveRecord::Base.transaction do create_table :institutions create_table :cohorts create_table :cohort_enrollments # ... indexes and foreign keys end ``` 2. **Monitor Migration Progress** - Log execution time - Check for locks - Monitor error logs 3. **Have Rollback Ready** ```bash # Immediate rollback if issues detected bin/rails db:rollback STEP=1 ``` ### Post-Migration 1. **Verify Schema Integrity** ```bash bin/rails db:schema:dump git diff db/schema.rb ``` 2. **Run Integration Tests** ```bash bundle exec rspec spec/integration/cohort_workflow_spec.rb bundle exec rspec spec/migrations/20260114000001_create_flo_doc_tables_spec.rb ``` 3. **Monitor Production** - Check query performance - Monitor error rates - Verify data integrity --- ## Risk Acceptance Criteria ### Acceptable Risks - **Low-impact performance degradation** (< 5% slowdown on existing queries) - **Non-critical database compatibility issues** (fixable with migration updates) - **Soft delete data leakage** (mitigated by application-level scopes) ### Unacceptable Risks (Must Fix Before Merge) - **Foreign key constraint failures** (CRITICAL) - **Data loss during rollback** (CRITICAL) - **Unauthorized data access** (HIGH) - **Failed migration requiring manual intervention** (HIGH) - **Broken existing DocuSeal functionality** (HIGH) --- ## Testing Strategy ### Unit Tests (Migration) - Table creation verification - Schema validation - Index creation - Foreign key constraints - Reversibility - Data integrity ### Integration Tests - Referential integrity with existing tables - Query performance with joins - State machine transitions - Concurrent access scenarios ### Performance Tests - Migration execution time - Query performance before/after - Index usage verification - Load testing ### Security Tests - Authorization checks - Data access validation - Email encryption (if implemented) --- ## Monitoring & Alerting ### During Migration - Migration execution time > 30 seconds - Database lock wait time > 5 seconds - Error rate > 1% ### Post-Migration - Query performance degradation > 10% - Foreign key violation errors - Data integrity check failures - User-reported issues --- ## Rollback Plan ### Trigger Conditions - Migration execution time > 60 seconds - Any foreign key constraint violation - Data integrity errors - User-reported critical issues - Performance degradation > 20% ### Rollback Steps 1. **Immediate**: Stop migration if in progress 2. **Execute**: `bin/rails db:rollback STEP=1` 3. **Verify**: Check schema.rb matches original 4. **Test**: Run existing DocuSeal tests 5. **Notify**: Alert team if manual intervention needed ### Recovery Time Objective (RTO) - **Target**: < 5 minutes for rollback - **Maximum**: 30 minutes (including verification) --- ## Conclusion Story 1.1 presents **MEDIUM-HIGH** overall risk due to brownfield integration complexity. The primary concerns are: 1. **Foreign key dependencies** on existing DocuSeal tables (CRITICAL) 2. **Rollback complexity** due to data dependencies (CRITICAL) 3. **Performance impact** on existing queries (HIGH) 4. **Data integrity** during migration (HIGH) **Recommendation**: - ✅ **Proceed with caution** after implementing all mitigation strategies - ✅ **Mandatory**: Test rollback in staging environment - ✅ **Mandatory**: Run integration tests against existing DocuSeal test suite - ✅ **Mandatory**: Create database backup before production migration - ⚠️ **Consider**: Phased rollout (migrate schema first, then enable features) **Next Steps**: 1. Implement all pre-migration validation checks 2. Create comprehensive test coverage 3. Test rollback scenario 4. Schedule production migration during maintenance window 5. Monitor closely post-deployment --- **Assessment Completed By**: QA Agent **Date**: 2026-01-15 **Review Status**: Ready for Development Team Review **Approval Required**: Yes (before branch creation)