Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill should be used when adding features to existing Firebase projects. Triggers on "add function", "create endpoint", "new tool", "add api", "new collection", "implement", "build feature". Guides TDD workflow with test-first development, security rules, and emulator verification.
.claude/skills/aiskillstore-firebase-development-add-feature/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 0% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 103% | 0% |
| case-21 | ✗→✓ | ▲ Improved | 17% | 0% |
| case-23 | ✗→✓ | ▲ Improved | 70% | 0% |
| case-06 | ✓→✗ | ▼ Worse | 88% | 0% |
This sub-skill guides adding new features to existing Firebase projects using TDD. It handles Cloud Functions, Firestore collections, and API endpoints.
Key principles:
{success, message, data?} response patternDo not use for:
firebase-development:project-setupfirebase-development:debugfirebase-development:validateCreate checklist with these 12 steps:
Determine what's being added:
Examine the project to understand patterns:
bashls -la functions/src/ grep -r "onRequest" functions/src/ grep "express" functions/package.json
Determine: Architecture style, auth method, security model.
Reference: docs/examples/express-function-architecture.md
Create test file before implementation:
typescript// ABOUTME: Unit tests for [feature name] functionality // ABOUTME: Tests [what the feature does] with various scenarios import { describe, it, expect } from 'vitest'; import { handleYourFeature } from '../../tools/yourFeature'; describe('handleYourFeature', () => { it('should return success when given valid input', async () => { const result = await handleYourFeature('user-123', { name: 'test' }); expect(result.success).toBe(true); }); it('should return error for invalid input', async () => { const result = await handleYourFeature('user-123', { name: '' }); expect(result.success).toBe(false); }); });
Run test to confirm it fails: npm run test
Create implementation file:
typescript// ABOUTME: Implements [feature name] for [purpose] // ABOUTME: Returns {success, message, data?} response export async function handleYourFeature( userId: string, params: { name: string } ): Promise<{ success: boolean; message: string; data?: any }> { if (!userId) { return { success: false, message: 'Authentication required' }; } if (!params.name) { return { success: false, message: 'Invalid input: name required' }; } // Implementation here return { success: true, message: 'Success', data: { /* ... */ } }; }
Reference: docs/examples/express-function-architecture.md
Update firestore.rules for new collections:
Server-write-only (preferred):
javascriptmatch /yourCollection/{docId} { allow read: if request.auth != null; allow write: if false; // Only Cloud Functions }
Client-write (if needed):
javascriptmatch /yourCollection/{docId} { allow create: if request.auth != null && request.resource.data.userId == request.auth.uid; allow update: if request.auth != null && resource.data.userId == request.auth.uid && request.resource.data.diff(resource.data).affectedKeys() .hasOnly(['name', 'updatedAt']); }
Reference: docs/examples/firestore-rules-patterns.md
Add to firestore.indexes.json for complex queries:
json{ "collectionGroup": "yourCollection", "fields": [ {"fieldPath": "userId", "order": "ASCENDING"}, {"fieldPath": "createdAt", "order": "DESCENDING"} ] }
Skip if no complex queries (single-field indexes are automatic).
Based on project pattern:
API Keys:
typescriptapp.post('/endpoint', apiKeyGuard, async (req, res) => { const userId = req.userId!; // ... });
Firebase Auth:
typescriptif (!req.auth) { res.status(401).json({ success: false, message: 'Auth required' }); return; } const userId = req.auth.uid;
Reference: docs/examples/api-key-authentication.md
All handlers use consistent pattern:
typescriptinterface HandlerResponse { success: boolean; message: string; data?: any; }
Include validation at every layer (defense in depth).
Add to functions/src/index.ts:
Express: Add route or switch case Domain-grouped: export * from './yourDomain'; Individual: Import and export in index.js
Verify: npm run build
Run tests: npm run test
All tests should pass. If not, fix implementation (not tests).
Create functions/src/__tests__/emulator/yourFeature.test.ts:
Test complete workflow with emulators:
Run: npm run test:emulator (with emulators running)
bashfirebase emulators:start open http://127.0.0.1:4000
Verify:
All handlers MUST return:
typescript// Success { success: true, message: "Created", data: { id: "abc" } } // Error { success: false, message: "Invalid input" }
Before marking complete:
docs/examples/express-function-architecture.mddocs/examples/api-key-authentication.mddocs/examples/firestore-rules-patterns.mddocs/examples/emulator-workflow.md| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-11 | pass→pass | 5,720 | 5,270 | -8% | 1 | 1 | 0% | 1,027 | 2,636 | +157% | 0 | 0 | — |
case-06 | pass→fail | 14,124 | 13,132 | -7% | 1 | 1 | 0% | 1,535 | 2,890 | +88% | 0 | 0 | — |
case-01 | fail→fail | 20,737 | 3,771 | -82% | 1 | 1 | 0% | 4,209 | 2,076 | -51% | 0 | 0 | — |
case-02 | fail→fail | 14,423 | 3,555 | -75% | 1 | 1 | 0% | 2,999 | 2,088 | -30% | 0 | 0 | — |
case-03 | fail→fail | 20,377 | 6,611 | -68% | 1 | 1 | 0% | 4,065 | 2,103 | -48% | 0 | 0 | — |
case-04 | pass→pass | 13,628 | 4,675 | -66% | 1 | 1 | 0% | 2,511 | 2,461 | -2% | 0 | 0 | — |
case-05 | pass→pass | 11,175 | 3,097 | -72% | 1 | 1 | 0% | 1,739 | 2,174 | +25% | 0 | 0 | — |
case-07 | fail→fail | 9,779 | 6,212 | -36% | 1 | 1 | 0% | 1,987 | 1,977 | -1% | 0 | 0 | — |
case-08 | fail→pass | 33,201 | 9,311 | -72% | 1 | 1 | 0% | 3,552 | 3,542 | -0% | 0 | 0 | — |
case-09 | pass→pass | 15,822 | 27,251 | +72% | 1 | 1 | 0% | 2,877 | 5,102 | +77% | 0 | 0 | — |
case-10 | pass→pass | 6,941 | 4,854 | -30% | 1 | 1 | 0% | 1,159 | 2,385 | +106% | 0 | 0 | — |
case-12 | pass→fail | 5,337 | 6,077 | +14% | 1 | 1 | 0% | 1,025 | 2,098 | +105% | 0 | 0 | — |
case-13 | pass→pass | 11,926 | 6,311 | -47% | 1 | 1 | 0% | 2,361 | 2,769 | +17% | 0 | 0 | — |
case-14 | pass→pass | 14,995 | 9,355 | -38% | 1 | 1 | 0% | 3,026 | 3,444 | +14% | 0 | 0 | — |
case-15 | pass→pass | 11,669 | 11,801 | +1% | 1 | 1 | 0% | 2,232 | 4,060 | +82% | 0 | 0 | — |
case-16 | fail→fail | 11,533 | 13,661 | +18% | 1 | 1 | 0% | 2,168 | 4,420 | +104% | 0 | 0 | — |
case-17 | fail→pass | 8,103 | 5,827 | -28% | 1 | 1 | 0% | 1,256 | 2,552 | +103% | 0 | 0 | — |
case-18 | pass→pass | 10,722 | 12,394 | +16% | 1 | 1 | 0% | 2,054 | 4,097 | +99% | 0 | 0 | — |
case-19 | pass→pass | 4,958 | 3,045 | -39% | 1 | 1 | 0% | 718 | 2,169 | +202% | 0 | 0 | — |
case-20 | pass→pass | 10,931 | 4,667 | -57% | 1 | 1 | 0% | 1,860 | 2,319 | +25% | 0 | 0 | — |
case-21 | fail→pass | 10,179 | 2,821 | -72% | 1 | 1 | 0% | 1,859 | 2,182 | +17% | 0 | 0 | — |
case-22 | pass→pass | 8,275 | 2,552 | -69% | 1 | 1 | 0% | 1,436 | 2,037 | +42% | 0 | 0 | — |
case-23 | fail→pass | 8,913 | 3,083 | -65% | 1 | 1 | 0% | 1,260 | 2,144 | +70% | 0 | 0 | — |
case-24 | pass→pass | 9,513 | 3,300 | -65% | 1 | 1 | 0% | 1,528 | 2,106 | +38% | 0 | 0 | — |
case-25 | pass→pass | 13,417 | 8,143 | -39% | 1 | 1 | 0% | 2,231 | 2,954 | +32% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted, and 22 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +8 percentage points is the difference between those two pass rates over the 22 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.