Technical Specification Template
A detailed template for specifying complex features with requirements, design, milestones, and acceptance criteria.
Table of Contents
Technical Specification Template
Technical specifications bridge the gap between high-level requirements and implementation. They provide enough detail for engineers to build features correctly while remaining flexible enough to adapt during development.
Why Use Technical Specifications?
Benefits:
- Aligns stakeholders on what will be built
- Reduces ambiguity during implementation
- Enables parallel work across teams
- Creates documentation for future reference
- Facilitates accurate estimation
When to write a tech spec:
- Features taking more than 1-2 weeks
- Cross-team projects
- Complex integrations
- User-facing features with specific requirements
- Performance-critical systems
The Template
# Technical Specification: [Feature Name]
**Version:** [1.0]
**Author:** [Name]
**Status:** [Draft | Review | Approved | In Development | Complete]
**Created:** [YYYY-MM-DD]
**Last Updated:** [YYYY-MM-DD]
## Overview
### Summary
[1-2 paragraph summary of the feature]
### Background
[Context and motivation for this feature]
### Goals
- [Primary goal 1]
- [Primary goal 2]
### Non-Goals
- [What this feature explicitly won't do]
## Requirements
### Functional Requirements
| ID | Requirement | Priority | Notes |
|----|-------------|----------|-------|
| FR-1 | [Requirement description] | [Must/Should/Could] | [Additional context] |
| FR-2 | [Requirement description] | [Must/Should/Could] | [Additional context] |
### Non-Functional Requirements
| ID | Category | Requirement | Target |
|----|----------|-------------|--------|
| NFR-1 | Performance | [Requirement] | [Metric] |
| NFR-2 | Security | [Requirement] | [Standard] |
| NFR-3 | Scalability | [Requirement] | [Target] |
### User Stories
**As a [user type], I want to [action] so that [benefit].**
#### Story 1: [Title]
**Acceptance Criteria:**
- [ ] Given [context], when [action], then [result]
- [ ] Given [context], when [action], then [result]
## Technical Design
### Architecture
[Architecture diagram or description]┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Client │────►│ Server │────►│ Database │ └─────────────┘ └─────────────┘ └─────────────┘
### Data Model
[Database schema changes]
```sql
CREATE TABLE [table_name] (
id UUID PRIMARY KEY,
[column] [type] [constraints],
created_at TIMESTAMP DEFAULT NOW()
);
API Design
[API endpoints and contracts]
// POST /api/[endpoint]
interface CreateRequest {
field1: string;
field2: number;
}
interface CreateResponse {
id: string;
created_at: string;
}Component Design
[Frontend/backend components]
interface ComponentProps {
// Props definition
}
// Component behavior descriptionState Management
[How state will be managed]
Error Handling
| Error Case | Response | User Message |
|---|---|---|
| [Case 1] | [HTTP code/action] | [User-friendly message] |
Security Considerations
- Authentication requirements
- Authorization rules
- Data encryption needs
- Input validation
- Audit logging
Testing Strategy
Unit Tests
- [Component/function to test]
- [Expected coverage]
Integration Tests
- [Integration points to test]
E2E Tests
- [User flows to test]
Performance Tests
- [Load test scenarios]
- [Performance benchmarks]
Migration Plan
Database Migrations
-- Migration: [description]
-- Up
[SQL statements]
-- Down
[Rollback statements]Data Migration
[Any data transformation needed]
Feature Flags
[Feature flag configuration]
Rollout Plan
Phase 1: [Name]
- Timeline: [Dates]
- Scope: [What's included]
- Success criteria: [Metrics]
Phase 2: [Name]
- Timeline: [Dates]
- Scope: [What's included]
- Success criteria: [Metrics]
Rollback Plan
[How to rollback if issues occur]
Dependencies
External Dependencies
| Dependency | Type | Status | Owner |
|---|---|---|---|
| [Service/Team] | [Blocking/Non-blocking] | [Status] | [Owner] |
Internal Dependencies
- [Internal dependency 1]
- [Internal dependency 2]
Timeline
| Milestone | Date | Description |
|---|---|---|
| Design Complete | [Date] | Tech spec approved |
| Development Start | [Date] | Sprint begins |
| Feature Complete | [Date] | All code merged |
| QA Complete | [Date] | Testing finished |
| Release | [Date] | Production deployment |
Open Questions
- [Question needing resolution]
- [Question needing resolution]
Appendix
Glossary
| Term | Definition |
|---|---|
| [Term] | [Definition] |
References
- [Link to related documents]
- [Link to design mockups]
## Complete Example
```markdown
# Technical Specification: User Notification Preferences
**Version:** 1.2
**Author:** Alex Rivera
**Status:** Approved
**Created:** 2025-10-01
**Last Updated:** 2025-10-12
## Overview
### Summary
Implement a notification preferences system allowing users to control which notifications they receive and through which channels (email, push, in-app). Users can set preferences at global and per-category levels.
### Background
Currently, users receive all notifications without ability to customize. User research (Q3 2025) found:
- 67% of users want to reduce notification volume
- 45% have disabled all notifications due to lack of granularity
- Top requested feature in last 3 months
### Goals
- Allow users to enable/disable notifications by category
- Support multiple channels (email, push, in-app)
- Provide sensible defaults for new users
- Enable quiet hours / do not disturb
- Reduce notification-related support tickets by 40%
### Non-Goals
- Notification scheduling (send later) - future feature
- Team/organization-level defaults - separate project
- SMS notifications - not supported in this phase
- Notification templates customization
## Requirements
### Functional Requirements
| ID | Requirement | Priority | Notes |
|----|-------------|----------|-------|
| FR-1 | Users can view all notification categories | Must | Categories from config |
| FR-2 | Users can enable/disable each category | Must | Per channel |
| FR-3 | Users can set global notification on/off | Must | Master switch |
| FR-4 | Users can configure quiet hours | Should | Time zone aware |
| FR-5 | Users can set channel preferences | Must | Email, push, in-app |
| FR-6 | System respects preferences when sending | Must | All notification paths |
| FR-7 | Users can reset to defaults | Could | One-click reset |
| FR-8 | Admins can see preference analytics | Could | Dashboard view |
### Non-Functional Requirements
| ID | Category | Requirement | Target |
|----|----------|-------------|--------|
| NFR-1 | Performance | Preference check latency | < 10ms p99 |
| NFR-2 | Performance | Settings page load time | < 500ms |
| NFR-3 | Availability | Preference service uptime | 99.9% |
| NFR-4 | Scalability | Support concurrent users | 10,000/min |
| NFR-5 | Security | Preferences are user-private | No cross-user access |
### User Stories
#### Story 1: Manage Email Notifications
**As a user, I want to control which emails I receive so that my inbox isn't overwhelmed.**
**Acceptance Criteria:**
- [ ] Given I'm on settings page, when I toggle "Marketing emails" off, then I stop receiving marketing emails
- [ ] Given I disabled an email category, when I re-enable it, then I receive those emails again
- [ ] Given I change preferences, when I save, then I see confirmation message
#### Story 2: Configure Quiet Hours
**As a user, I want to set quiet hours so that I'm not disturbed during off-hours.**
**Acceptance Criteria:**
- [ ] Given I'm on settings, when I enable quiet hours, then I can set start/end times
- [ ] Given quiet hours are active, when a notification would be sent, then push/in-app are suppressed (email still sends)
- [ ] Given my time zone changes, when quiet hours are evaluated, then they use my current time zone
#### Story 3: Disable All Notifications
**As a user, I want a master switch to disable all notifications quickly.**
**Acceptance Criteria:**
- [ ] Given I'm on settings, when I toggle "All notifications" off, then all notification types are disabled
- [ ] Given all notifications are off, when I toggle back on, then my previous per-category settings are restored
- [ ] Given all notifications are off, when system tries to notify, then no notifications are sent
## Technical Design
### Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Web Client │────►│ API Gateway │────►│ Preference Svc │ └─────────────────┘ └─────────────────┘ └────────┬────────┘ │ ┌─────────────────┐ ┌───────▼────────┐ │ Notification │◄─────────────────────────────│ PostgreSQL │ │ Service │ (checks preferences) │ + Redis │ └────────┬────────┘ └────────────────┘ │ ┌────┴────┬──────────┐ ▼ ▼ ▼ ┌─────┐ ┌──────┐ ┌────────┐ │Email│ │ Push │ │ In-App │ └─────┘ └──────┘ └────────┘
### Data Model
```sql
-- User notification preferences
CREATE TABLE notification_preferences (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
-- Global settings
global_enabled BOOLEAN DEFAULT true,
quiet_hours_enabled BOOLEAN DEFAULT false,
quiet_hours_start TIME, -- e.g., '22:00'
quiet_hours_end TIME, -- e.g., '08:00'
timezone VARCHAR(50) DEFAULT 'UTC',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(user_id)
);
-- Category-level preferences
CREATE TABLE notification_category_preferences (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
category VARCHAR(50) NOT NULL, -- e.g., 'comments', 'mentions', 'marketing'
-- Channel settings
email_enabled BOOLEAN DEFAULT true,
push_enabled BOOLEAN DEFAULT true,
in_app_enabled BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(user_id, category)
);
-- Indexes
CREATE INDEX idx_notif_pref_user ON notification_preferences(user_id);
CREATE INDEX idx_notif_cat_pref_user ON notification_category_preferences(user_id);
-- Audit log
CREATE TABLE notification_preference_audit (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
changed_field VARCHAR(100),
old_value TEXT,
new_value TEXT,
changed_at TIMESTAMP DEFAULT NOW()
);
API Design
// GET /api/v1/users/me/notification-preferences
interface GetPreferencesResponse {
global: {
enabled: boolean;
quietHours: {
enabled: boolean;
start: string | null; // "22:00"
end: string | null; // "08:00"
timezone: string;
};
};
categories: {
[category: string]: {
email: boolean;
push: boolean;
inApp: boolean;
};
};
availableCategories: {
id: string;
name: string;
description: string;
defaultEnabled: boolean;
}[];
}
// PUT /api/v1/users/me/notification-preferences
interface UpdatePreferencesRequest {
global?: {
enabled?: boolean;
quietHours?: {
enabled?: boolean;
start?: string;
end?: string;
timezone?: string;
};
};
categories?: {
[category: string]: {
email?: boolean;
push?: boolean;
inApp?: boolean;
};
};
}
interface UpdatePreferencesResponse {
success: boolean;
updated: GetPreferencesResponse;
}
// POST /api/v1/users/me/notification-preferences/reset
interface ResetPreferencesResponse {
success: boolean;
preferences: GetPreferencesResponse;
}Component Design
// NotificationPreferences.tsx
interface NotificationPreferencesProps {
userId: string;
}
interface CategoryToggleProps {
category: NotificationCategory;
preferences: CategoryPreference;
onChange: (category: string, channel: Channel, enabled: boolean) => void;
disabled: boolean; // When global is off
}
// State structure
interface PreferencesState {
loading: boolean;
saving: boolean;
error: string | null;
preferences: Preferences | null;
pendingChanges: Partial<Preferences>;
}State Management
Using React Query for server state:
// Queries
const { data: preferences } = useQuery(
['notification-preferences'],
fetchPreferences
);
// Mutations with optimistic updates
const mutation = useMutation(updatePreferences, {
onMutate: async (newPrefs) => {
await queryClient.cancelQueries(['notification-preferences']);
const previous = queryClient.getQueryData(['notification-preferences']);
queryClient.setQueryData(['notification-preferences'], newPrefs);
return { previous };
},
onError: (err, newPrefs, context) => {
queryClient.setQueryData(['notification-preferences'], context.previous);
},
});Error Handling
| Error Case | Response | User Message |
|---|---|---|
| Invalid time format | 400 Bad Request | "Please enter a valid time (HH:MM)" |
| Invalid timezone | 400 Bad Request | "Please select a valid timezone" |
| Preference not found | 404 Not Found | "Preferences not found. Using defaults." |
| Database error | 500 Internal Error | "Unable to save preferences. Please try again." |
| Rate limited | 429 Too Many Requests | "Too many updates. Please wait a moment." |
Security Considerations
- Authentication: All endpoints require valid JWT
- Authorization: Users can only access their own preferences (user_id from JWT)
- Input validation: Zod schemas for all request bodies
- Rate limiting: 10 preference updates per minute per user
- Audit logging: All preference changes logged with timestamp
Testing Strategy
Unit Tests
Preference Service:
shouldReturnDefaultsForNewUsershouldMergePartialUpdatesshouldValidateQuietHoursFormatshouldHandleTimezoneConversionshouldRespectGlobalDisable
Coverage target: 90%
Integration Tests
- API endpoint tests with test database
- Preference check integration with notification service
- Cache invalidation on update
E2E Tests
describe('Notification Preferences', () => {
it('should disable email notifications', async () => {
await page.goto('/settings/notifications');
await page.click('[data-testid="comments-email-toggle"]');
await page.click('[data-testid="save-button"]');
await expect(page.locator('.toast-success')).toBeVisible();
});
it('should enable quiet hours', async () => {
await page.goto('/settings/notifications');
await page.click('[data-testid="quiet-hours-toggle"]');
await page.fill('[data-testid="quiet-start"]', '22:00');
await page.fill('[data-testid="quiet-end"]', '08:00');
await page.click('[data-testid="save-button"]');
await expect(page.locator('.toast-success')).toBeVisible();
});
});Performance Tests
- Load test: 1000 preference reads/second
- Stress test: 100 concurrent preference updates
- Cache hit ratio target: >95%
Migration Plan
Database Migrations
-- Migration: 20251015_create_notification_preferences
-- Up
CREATE TABLE notification_preferences (...);
CREATE TABLE notification_category_preferences (...);
CREATE TABLE notification_preference_audit (...);
-- Create default preferences for existing users
INSERT INTO notification_preferences (user_id, global_enabled)
SELECT id, true FROM users
WHERE id NOT IN (SELECT user_id FROM notification_preferences);
-- Down
DROP TABLE notification_preference_audit;
DROP TABLE notification_category_preferences;
DROP TABLE notification_preferences;Feature Flags
{
"notification_preferences_enabled": {
"type": "boolean",
"default": false,
"description": "Enable new notification preferences UI"
},
"quiet_hours_enabled": {
"type": "boolean",
"default": false,
"description": "Enable quiet hours feature"
}
}Rollout Plan
Phase 1: Internal Testing
- Timeline: Oct 20-24
- Scope: Feature flag on for internal team only
- Success criteria: No critical bugs, <100ms API response
Phase 2: Beta Users
- Timeline: Oct 25-31
- Scope: 5% of users (beta flag)
- Success criteria: <5 bug reports, positive feedback
Phase 3: General Availability
- Timeline: Nov 1-7
- Scope: 100% of users
- Success criteria: Support tickets decrease, no P0/P1 incidents
Rollback Plan
- Disable feature flag immediately
- Users see previous settings UI
- Notification service ignores new preferences table
- Data preserved for re-rollout
Dependencies
External Dependencies
| Dependency | Type | Status | Owner |
|---|---|---|---|
| Design mockups | Blocking | Complete | Design team |
| Push notification service | Non-blocking | Available | Platform team |
| Email service API | Non-blocking | Available | Platform team |
Internal Dependencies
- User service (for user lookup)
- Notification service (for preference checks)
- Analytics service (for tracking)
Timeline
| Milestone | Date | Description |
|---|---|---|
| Design Complete | Oct 12 | Tech spec approved |
| Development Start | Oct 14 | Sprint begins |
| Backend Complete | Oct 21 | API and database |
| Frontend Complete | Oct 25 | UI components |
| QA Complete | Oct 30 | Testing finished |
| Beta Release | Oct 31 | 5% of users |
| GA Release | Nov 7 | 100% of users |
Open Questions
-
Should quiet hours affect email?Resolved: No, emails still send -
Default preferences for existing users?Resolved: All enabled - Should we add notification frequency limits (daily digest)?
Appendix
Glossary
| Term | Definition |
|---|---|
| Quiet Hours | Time period when push/in-app notifications are suppressed |
| Category | Grouping of notification types (e.g., comments, mentions) |
| Channel | Delivery method (email, push, in-app) |
References
- Design Mockups - Figma
- User Research Report
- Notification Service Documentation
- ADR-045: Notification Architecture
## Best Practices
### 1. Right Level of Detail
**Too vague:** "Build a notification system"
**Too detailed:** Line-by-line code specification
**Just right:** Design decisions, interfaces, data models
### 2. Keep It Updated
- Update status as development progresses
- Add open questions as they arise
- Document decisions made during implementation
### 3. Collaborate Early
- Share draft with key stakeholders
- Get feedback before finalizing
- Include reviewers in the process
### 4. Visual Communication
Use diagrams for:
- Architecture overviews
- Data flows
- State machines
- User flows
### 5. Link to Related Documents
- Design mockups
- RFCs
- ADRs
- API documentation
---
*A good tech spec is a communication tool. It should be detailed enough to implement from, but readable enough for stakeholders to understand.*