Skip to main content

Documentation Index

Fetch the complete documentation index at: https://grantmaster.dev/llms.txt

Use this file to discover all available pages before exploring further.

Engineering Patterns & Standards

While CLAUDE.md provides high-level conventions, this guide details the specific technical patterns used to scale and maintain the GrantMaster codebase.

1. The 7-Phase Testability Pattern

Used for refactoring large, legacy components (e.g., ExtensionsPage.tsx) into modular, testable units without breaking functionality.
  • Phase 1: Types & Constants: Extract Zod schemas, interfaces, and static metadata.
  • Phase 2: Logic Hooks: Move filtering, sorting, and data derivation into pure hooks.
  • Phase 3: Service Injection: Ensure services are passed as dependencies (DI) to hooks.
  • Phase 4: Presentational Components: Split JSX into small, stateless components that only receive props.
  • Phase 5: State Orchestration: Connect the hooks and components in a lean “page” component.
  • Phase 6: Deployment & Rollback: Create a .new.tsx version and use feature flags or lazy-loading to toggle.
  • Phase 7: Comprehensive Testing: Write unit tests for the extracted hooks and components using factories.

2. Dependency Injection (DI) in Hooks

To avoid tight coupling and enable easy mocking, hooks should accept their dependencies as optional arguments.
interface HookDeps {
  service?: IMyService;
}

export function useMyFeature({ service = defaultService }: HookDeps = {}) {
  // logic...
}

3. Service Layer Architecture

All data operations must go through service classes that extend BaseService or ExtensionBaseService.
  • Error Handling: Use withServiceResult to catch errors and return uniform ServiceResult objects.
  • Audit Logging: Use logSuccess and logError (inherited from BaseService) to ensure consistent observability.
  • Validation: Use Zod schemas within services to validate Firestore data on read (validateFirestoreDataRequired).

4. Event-Driven Communication

Avoid direct cross-module imports. Use the EventBus for side effects.
  • Internal: Use the global eventBus for core platform events.
  • Extensions: Always use createExtensionEventBus('id') to ensure subscriptions are cleaned up on deactivation.