Add real-time notifications to an existing task management system using Spec-Driven Development
This tutorial demonstrates how to use SDD to add features to an existing codebase. You’ll integrate real-time notifications into a task management application while respecting existing architecture and patterns.
Time to complete: 45-60 minutesWhat you’ll build: WebSocket-based real-time notifications for task updatesPrerequisites: Completed greenfield tutorial, understand SDD workflow
Before starting the SDD workflow, understand the current system:
# Ask your AI assistant to analyze the codebasePlease analyze the existing Taskify codebase and document:1. Current architecture (backend/frontend structure)2. Authentication mechanism (how JWT is used)3. Database schema (relevant tables: users, tasks, comments)4. API patterns (RESTful conventions, error handling)5. Frontend state management (Redux, Context, etc.)6. Testing approach (frameworks, coverage)Create a document summarizing these findings in .specify/memory/codebase-context.md
Backend: Can extend backend/src/services/ with notification service
Frontend: Can create new hook useNotifications() following existing pattern
Auth: Must validate JWT on WebSocket connection upgrade
Database: May need new notifications table or event log
<Note> This analysis becomes input for the specification and planning phases, ensuring new features align with existing patterns.</Note>## Step 1: Initialize Feature in Existing ProjectNavigate to your existing Taskify project:```bashcd taskify# Launch AI assistant (if not already running)claude # or your preferred assistant
Verify Spec Kit is configured:If the project doesn’t have .specify/ yet:
# Initialize Spec Kit in existing projectspecify init . --ai claude --force# This creates .specify/ without overwriting existing code
Using --force in an existing project merges Spec Kit structure without deleting your code. Review changes carefully.
Check if .specify/memory/constitution.md exists. If not, create one that reflects current architecture:
/speckit.constitution Analyze the existing Taskify codebase and create principles that reflect our current architecture and practices:1. FastAPI backend with SQLAlchemy ORM (no repository pattern)2. React frontend with Context API (no Redux)3. PostgreSQL database with foreign key constraints4. JWT authentication for all protected endpoints5. Test coverage using pytest (backend) and Jest (frontend)6. Real-time features should use WebSockets (not polling)7. Maintain backward compatibility with existing REST API
# Taskify Constitution## Principles### Article I: Existing Architecture PreservationNew features MUST integrate with existing patterns.**Rules:**- Follow existing service pattern (SQLAlchemy ORM, no repository)- Follow existing hook pattern (Context API, no Redux)- Extend existing database schema (no schema redesign)- Reuse existing authentication (JWT validation)**Rationale**: Consistency makes maintenance easier and onboarding faster.### Article II: Backward CompatibilityNew features MUST NOT break existing functionality.**Rules:**- REST API endpoints remain unchanged- Database migrations must be backward compatible- Frontend components can be extended, not rewritten- Existing tests must continue passing**Rationale**: Production system cannot tolerate breaking changes.### Article III: Real-Time PerformanceWebSocket connections MUST scale to 1,000 concurrent users.**Rules:**- Use WebSocket for real-time features (not polling)- Implement connection pooling and load balancing- Graceful degradation if WebSocket unavailable- Monitor connection count and memory usage**Rationale**: Real-time features are only valuable if they're reliable at scale.
/speckit.specify Add real-time notifications to Taskify so users are immediately notified when:1. A task is assigned to them2. A task they're watching has its status changed3. A comment is added to a task they're watchingUsers should see notifications in a dropdown in the navbar with:- Notification message (e.g., "John assigned you 'Fix login bug'")- Timestamp (relative time, e.g., "2 minutes ago")- Link to the relevant task- Unread indicatorUsers can:- Mark individual notifications as read- Mark all notifications as read- View notification history (last 30 days)Notifications should appear instantly without requiring page refresh. If the user is offline or WebSocket disconnects, they should see notifications when they reconnect.
What happens:
1
AI analyzes existing codebase
Reads .specify/memory/codebase-context.md to understand current architecture
2
Generates short name
Analyzes description → real-time-notifications
3
Checks existing features
Finds existing specs (001-create-taskify, 002-user-auth) → Next is 003
4
Creates feature branch
Branch: 003-real-time-notifications
5
Generates spec with integration notes
Spec includes “Integration Considerations” section for brownfield
### Integration Details### Backend Integration Points**Existing:**- `backend/src/services/task_service.py` - Handles task updates- `backend/src/api/tasks.py` - REST endpoints for tasks- `backend/src/middleware/auth.py` - JWT validation**Required:**- New `backend/src/services/notification_service.py` - Notification logic- New `backend/src/api/websocket.py` - WebSocket endpoint- Extend `backend/src/services/task_service.py` - Emit events on task changes- New `backend/src/models/notification.py` - Notification model### Frontend Integration Points**Existing:**- `frontend/src/hooks/useTasks.ts` - Task data fetching- `frontend/src/contexts/AuthContext.tsx` - Auth state (JWT token)- `frontend/src/components/Navbar.tsx` - Top navigation bar**Required:**- New `frontend/src/hooks/useNotifications.ts` - WebSocket connection and state- New `frontend/src/components/NotificationDropdown.tsx` - UI component- Extend `frontend/src/components/Navbar.tsx` - Add notification icon### Database Changes**New Tables:**```sqlCREATE TABLE notifications ( id UUID PRIMARY KEY, user_id UUID NOT NULL REFERENCES users(id), task_id UUID NOT NULL REFERENCES tasks(id), type VARCHAR(50) NOT NULL, -- 'task_assigned' | 'status_changed' | 'comment_added' message TEXT NOT NULL, is_read BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW());CREATE INDEX idx_notifications_user_id ON notifications(user_id);CREATE INDEX idx_notifications_created_at ON notifications(created_at);
Backward Compatibility:
No changes to existing tasks or comments tables
Migration is additive only (no ALTER TABLE on existing tables)
<Note> The specification explicitly addresses integration with existing systems—this is unique to brownfield development.</Note>## Step 4: Clarify Integration DetailsUse `/speckit.clarify` to resolve ambiguities about integration:```text/speckit.clarify Focus on integration with existing authentication and whether we need persistent notification storage or in-memory only.
## Question 1: WebSocket Authentication**Context**: The spec mentions validating JWT on WebSocket connection, but doesn't specify how to pass the token.**What we need to know**: How should JWT tokens be passed to WebSocket connections?**Suggested Answers**:| Option | Answer | Implications ||--------|--------|-------------|| A | Pass JWT as query parameter (`ws://...?token=<jwt>`) | Simple but exposes token in URL, logs may capture it || B | Pass JWT in first WebSocket message after connect | More secure, requires client to send auth message || C | Use HTTP cookie with httpOnly flag | Most secure, requires cookie-based auth || Custom | Provide your own answer | Explain your preferred approach |**Your choice**: B - Pass JWT in first message (aligns with stateless REST API pattern)
Define how to build the feature with existing tech stack:
/speckit.plan Use the existing FastAPI backend and React frontend. For real-time communication, use FastAPI's WebSocket support on the backend and the native WebSocket API on the frontend. Store notifications in PostgreSQL for persistence. Use SQLAlchemy events to trigger notifications when tasks are updated. Reuse the existing JWT authentication by validating tokens in the WebSocket connection upgrade. On the frontend, create a new useNotifications hook following the existing useTasks pattern. Integrate the notification dropdown into the existing Navbar component.
# backend/src/services/task_service.pyfrom sqlalchemy import eventfrom .notification_service import NotificationServiceclass TaskService: def __init__(self, db: Session, notification_service: NotificationService): self.db = db self.notification_service = notification_service def update_task(self, task_id: UUID, updates: TaskUpdate) -> Task: task = self.db.query(Task).filter_by(id=task_id).first() # Track changes for notifications status_changed = updates.status and updates.status != task.status assigned_changed = updates.assigned_to and updates.assigned_to != task.assigned_to # Apply updates (existing logic) for key, value in updates.dict(exclude_unset=True).items(): setattr(task, key, value) self.db.commit() # Trigger notifications (NEW) if assigned_changed: self.notification_service.create_notification( user_id=task.assigned_to, task_id=task.id, type="task_assigned", message=f"{current_user.name} assigned you '{task.title}'" ) if status_changed: # Notify task watchers (users who commented or were previously assigned) watchers = self._get_task_watchers(task.id) for watcher_id in watchers: self.notification_service.create_notification( user_id=watcher_id, task_id=task.id, type="status_changed", message=f"'{task.title}' moved to {task.status}" ) return task
[… additional implementation details in Phase 1, 2, 3 …]
</Accordion>**Key difference from greenfield:**- Plan explicitly shows integration points with existing code- Preserves existing patterns (no refactoring)- Additive changes only (no destructive modifications)## Step 6: Validate Against Existing TestsBefore implementing, ensure existing tests will continue passing:```textRun the existing test suite to establish a baseline:1. Backend tests: pytest backend/tests/2. Frontend tests: npm testDocument the current pass rate in the plan so we can verify backward compatibility after implementation.
Example baseline:
✓ Baseline Test Results: - Backend: 127 tests passing - Frontend: 89 tests passing - E2E: 15 tests passing - Total: 231 tests passingGoal: All 231 tests must continue passing after implementation.
Implement with extra focus on backward compatibility:
/speckit.implementIMPORTANT: After each phase, run the baseline test suite to verify no regressions. If any existing tests fail, fix them before proceeding.
The AI will:
1
Run baseline tests first
Establishes pre-implementation pass rate
2
Implement each phase
Creates new files and modifies existing ones carefully
3
Run tests after each phase
Verifies both new functionality AND backward compatibility
4
Fix regressions immediately
If baseline tests fail, AI iterates to fix before proceeding
Example output:
✓ Phase 0: Pre-Implementation ✓ Created database migration ✓ Ran baseline tests (231/231 passing) ✓ ✓ Checkpoint: Ready for implementation✓ Phase 1: Backend - Notification Service ✓ Created Notification model ✓ Created NotificationService ✓ Set up Redis client ✓ Wrote service tests (8 new tests) ✓ Ran tests (239/239 passing) ✓ ✓ Checkpoint: Service working, no regressions✓ Phase 2: Backend - Integration ✓ Extended TaskService (notifications on assign/status change) ✓ Extended CommentService (notifications on new comments) ✓ Updated service tests (6 modified tests) ✓ Ran tests (245/245 passing) ✓ ⚠ Checkpoint: Fixed 2 tests that broke due to new dependencies✓ Phase 3: Backend - WebSocket ✓ Created WebSocket endpoint ✓ Wrote WebSocket tests (5 new tests) ✓ Ran tests (250/250 passing) ✓ ✓ Checkpoint: WebSocket delivering notifications✓ Phase 4: Frontend - Hook ✓ Created useNotifications hook ✓ Wrote hook tests (7 new tests) ✓ Ran tests (257/257 passing) ✓ ✓ Checkpoint: Hook connecting and receiving✓ Phase 5: Frontend - UI ✓ Created NotificationDropdown component ✓ Extended Navbar component ✓ Wrote component tests (6 new tests) ✓ Ran tests (263/263 passing) ✓ ✓ Checkpoint: Notifications appearing in UI✓ Phase 6: E2E Testing ✓ Wrote E2E notification tests (4 new tests) ✓ Ran full test suite (267/267 passing) ✓ ✓ Checkpoint: Feature complete, no regressions✓ Implementation complete! - Baseline tests: 231 (all still passing) - New tests: 36 - Total: 267 tests passing - No regressions detected ✓
Common brownfield issues:
Import cycles: New services may create circular dependencies
Test fixtures: Existing fixtures may need extending
Type conflicts: TypeScript may complain about extended types
Database state: Tests may share state if not properly isolated
Create a PR emphasizing integration and backward compatibility:
Create a pull request from branch 003-real-time-notifications to main. In the description, emphasize:1. What existing code was modified vs. what was added2. Backward compatibility verification (all baseline tests pass)3. Integration points with existing services4. Migration steps for deployment5. Screenshots of notifications in action