--- name: wednesday-dev description: Technical development guidelines for Wednesday Solutions projects. Enforces import ordering, complexity limits, naming conventions, TypeScript best practices, and code quality standards for React/Next.js applications. license: MIT metadata: author: wednesday-solutions version: "1.0" compatibility: Next.js 14+, React 18+, TypeScript 5+ permissions: allow: - Bash(npm run lint) - Bash(npm run format:check) - Bash(npm run test) - Bash(npm run build) --- # Wednesday Technical Development Guidelines ## Trigger Load this skill when writing, reviewing, or refactoring **code logic**: - Writing a new function, hook, API route, or service - Reviewing TypeScript / JavaScript for quality issues - Refactoring for complexity, naming, or structure - "Does this follow our coding standards?" **Do NOT use this skill for:** choosing UI components or visual styling (use `wednesday-design`). This skill covers code structure, TypeScript, imports, and logic — not visual output. --- This skill enforces code quality standards for Wednesday Solutions projects. Follow these guidelines to maintain consistency, readability, and maintainability across the codebase. ## 1. Import Order Imports must follow a strict ordering pattern. The project uses `@trivago/prettier-plugin-sort-imports` to enforce this automatically. ### Required Order ```typescript // 1. React (always first) import React, { useState, useEffect } from 'react' // 2. Next.js imports import { useRouter } from 'next/navigation' import Image from 'next/image' // 3. State management (Recoil, Redux) import { useSelector } from 'react-redux' // 4. UI libraries (MUI, Radix) import { Button } from '@mui/material' // 5. Path alias imports (@/) import { useAuth } from '@/lib/hooks/useAuth' import { API_ROUTES } from '@/lib/constants' // 6. External packages import { z } from 'zod' import { motion } from 'framer-motion' // 7. Internal components import { Header } from 'components/Header' // 8. Relative imports (sibling/parent) import { utils } from './utils' import { types } from '../types' ``` ### Rules - Each import group must be separated by a blank line - Imports within each group should be alphabetically sorted - Use type imports for TypeScript types: `import type { User } from '@/types'` - Avoid wildcard imports (`import * as`) unless necessary ## 2. Code Complexity ### Cyclomatic Complexity **Maximum allowed: 8** (stricter than industry standard of 10) Functions exceeding this limit must be refactored. See [references/COMPLEXITY.md](references/COMPLEXITY.md) for detailed strategies. ### Quick Remediation | Complexity | Action Required | |------------|-----------------| | 1-4 | Good - easy to test | | 5-7 | Acceptable - consider simplifying | | 8 | At limit - refactor if adding logic | | 9+ | Must refactor before merging | ### How to Reduce Complexity 1. **Extract helper functions** - Break large functions into smaller, focused ones 2. **Use early returns** - Replace nested conditions with guard clauses 3. **Replace conditionals with polymorphism** - Use strategy pattern for complex branching 4. **Simplify boolean expressions** - Extract complex conditions into named variables 5. **Use lookup tables** - Replace switch statements with object maps ```typescript // BAD: High complexity function getDiscount(user: User, items: Item[]) { let discount = 0 if (user.isPremium) { if (items.length > 10) { discount = 20 } else if (items.length > 5) { discount = 10 } else { discount = 5 } } else { if (items.length > 10) { discount = 10 } else if (items.length > 5) { discount = 5 } } return discount } // GOOD: Low complexity with lookup table const DISCOUNT_TABLE = { premium: { large: 20, medium: 10, small: 5 }, regular: { large: 10, medium: 5, small: 0 }, } as const function getCartSize(count: number): 'large' | 'medium' | 'small' { if (count > 10) return 'large' if (count > 5) return 'medium' return 'small' } function getDiscount(user: User, items: Item[]) { const tier = user.isPremium ? 'premium' : 'regular' const size = getCartSize(items.length) return DISCOUNT_TABLE[tier][size] } ``` ### File Size Limits - **Max file lines**: 250 - **Max function lines**: 350 - **Max line length**: 120 characters ## 3. Naming Conventions ### Components & Classes Use **PascalCase** for React components, classes, types, and interfaces. ```typescript // Components function UserProfile() { } function PaymentCard() { } // Types & Interfaces interface UserProfileProps { } type PaymentMethod = 'card' | 'bank' // Classes class AuthService { } ``` ### Functions & Variables Use **camelCase** for functions, variables, hooks, and object properties. ```typescript // Functions function fetchUserData() { } function calculateTotal() { } // Variables const isLoading = true const userProfile = { } // Hooks function useAuth() { } function useLocalStorage() { } ``` ### Constants & Enums Use **UPPER_SNAKE_CASE** for constants and enum values. ```typescript // Constants const API_BASE_URL = 'https://api.example.com' const MAX_RETRY_ATTEMPTS = 3 // Enums enum UserRole { ADMIN = 'ADMIN', USER = 'USER', GUEST = 'GUEST', } ``` ### Boolean Variables Prefix with `is`, `has`, `should`, `can`, or `will` for clarity. ```typescript // Good const isAuthenticated = true const hasPermission = false const shouldRefetch = true const canEdit = user.role === 'ADMIN' // Bad const authenticated = true const permission = false const refetch = true ``` ### Files & Folders | Type | Convention | Example | |------|------------|---------| | Component files | PascalCase | `UserProfile.tsx` | | Hook files | camelCase with `use` prefix | `useAuth.ts` | | Utility files | camelCase | `formatDate.ts` | | Constant files | camelCase | `apiRoutes.ts` | | Type files | camelCase | `user.types.ts` | | Test files | Match source + `.test` | `UserProfile.test.tsx` | | Folders | camelCase | `components/`, `hooks/` | ### Descriptive Naming Names should be self-documenting: ```typescript // Bad - unclear intent const d = new Date() const arr = users.filter(u => u.a) function proc(x: number) { } // Good - clear intent const currentDate = new Date() const activeUsers = users.filter(user => user.isActive) function processPayment(amount: number) { } ``` ## 4. TypeScript Best Practices ### Strict Type Safety - Enable strict mode in `tsconfig.json` - Avoid `any` type - use `unknown` and narrow types - Use explicit return types for public functions - Leverage discriminated unions for state ```typescript // Use discriminated unions type AsyncState = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: Error } // Narrow types properly function processValue(value: unknown) { if (typeof value === 'string') { return value.toUpperCase() } if (typeof value === 'number') { return value.toFixed(2) } throw new Error('Unsupported type') } ``` ### Type Imports Separate type imports from value imports: ```typescript import { useState } from 'react' import type { FC, ReactNode } from 'react' import { fetchUser } from '@/lib/api' import type { User, UserRole } from '@/types' ``` ## 5. React Patterns ### Component Structure Follow this order within components: 1. Type definitions (props interface) 2. Component function declaration 3. Hooks (useState, useEffect, custom hooks) 4. Derived state / computations 5. Event handlers 6. Effects 7. Return statement (JSX) ```typescript interface UserCardProps { userId: string onSelect?: (user: User) => void } export function UserCard({ userId, onSelect }: UserCardProps) { // 1. Hooks const [isExpanded, setIsExpanded] = useState(false) const { data: user, isLoading } = useUser(userId) // 2. Derived state const displayName = user ? `${user.firstName} ${user.lastName}` : 'Unknown' // 3. Event handlers const handleClick = () => { setIsExpanded(!isExpanded) onSelect?.(user) } // 4. Early returns for loading/error states if (isLoading) return if (!user) return null // 5. Main render return ( {displayName} {isExpanded && {user.bio}} ) } ``` ### Use Client Directive Mark client components explicitly: ```typescript 'use client' import { useState } from 'react' // ... client-side component ``` ## 6. Forbidden Patterns ### No Console Statements Console statements are forbidden in production code. Use proper logging utilities. ```typescript // Forbidden console.log('debug:', data) console.error('Error:', err) // Use structured logging or remove before commit ``` ### No Magic Numbers/Strings Extract to named constants: ```typescript // Bad if (retryCount > 3) { } if (status === 'active') { } // Good const MAX_RETRIES = 3 const STATUS_ACTIVE = 'active' if (retryCount > MAX_RETRIES) { } if (status === STATUS_ACTIVE) { } ``` ### No Unused Code - Remove unused imports (enforced by `eslint-plugin-unused-imports`) - Delete commented-out code - Remove unused variables (prefix with `_` only if intentionally unused) ## 7. Testing Requirements ### Unit Tests - Use Jest with React Testing Library - Test file naming: `ComponentName.test.tsx` - Focus on user behavior, not implementation details ```typescript import { render, screen, fireEvent } from '@testing-library/react' import { UserCard } from './UserCard' describe('UserCard', () => { it('displays user name correctly', () => { render() expect(screen.getByText('John Doe')).toBeInTheDocument() }) it('calls onSelect when clicked', () => { const onSelect = jest.fn() render() fireEvent.click(screen.getByRole('button')) expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: '123' })) }) }) ``` ### E2E Tests - Use Playwright for end-to-end tests - Test file naming: `feature.spec.ts` - Test critical user flows ## 8. Security Considerations - Sanitize HTML with DOMPurify before rendering - Validate all user inputs with Zod schemas - Never expose sensitive data in client-side code - Use environment variables for secrets - Implement CSRF protection for forms ## 9. Performance Guidelines - Use `next/dynamic` for code splitting - Implement proper loading states - Memoize expensive computations with `useMemo` - Avoid unnecessary re-renders with `React.memo` - Use image optimization with `next/image` --- ## 10. Commenting Guidelines (brownfield-aware) These conventions are read by the brownfield intelligence pipeline to build module purpose maps, detect tech debt, and generate the reverse PRD. Well-placed comments make the map report significantly more useful — they surface intent that can't be inferred from code structure alone. ### Substantive comments (captured as developer intent) Write comments that explain **why**, not what. Comments of 8+ words that aren't noise are collected as "developer intent" and used to infer module purpose. ```typescript // BAD — too short or states the obvious (filtered out) // increment counter i++ // BAD — noise (filtered out) // eslint-disable-next-line @typescript-eslint/no-explicit-any // @param userId - the user id // GOOD — explains why, ≥8 words (captured) // We batch here because the payment provider rate-limits to 10 req/s per merchant // Using optimistic updates to avoid the 200ms API round-trip on every keystroke // This module owns all JWT lifecycle — issue, refresh, revoke. Nothing else should touch tokens. ``` ### Tech debt tags Use these exact tags — the brownfield scanner recognises them and maps severity: | Tag | Severity | Use for | |-----|----------|---------| | `FIXME:` | 🔴 high | Broken or incorrect behaviour that needs fixing | | `BUG:` | 🔴 high | Known bug, not yet fixed | | `XXX:` | 🔴 high | Dangerous or wrong — must not ship | | `HACK:` | 🟡 medium | Works but is a deliberate shortcut | | `KLUDGE:` | 🟡 medium | Messy workaround, needs rethinking | | `SMELL:` | 🟡 medium | Design concern, not yet a bug | | `TODO:` | 🟡 medium | Planned work, not urgent | | `TEMP:` | 🟡 medium | Intentionally temporary, must be removed | | `OPTIMIZE:` | 🔵 low | Performance opportunity | | `PERF:` | 🔵 low | Performance opportunity | | `REVIEW:` | 🔵 low | Needs a second pair of eyes | | `IDEA:` | ⚪ info | Suggestion, not planned work | | `NOTE:` | ⚪ info | Important context for future readers | | `DEPRECATED:` | ⚪ info | Kept for backwards compat, do not use | ```typescript // FIXME: token refresh fails silently when the network drops mid-request // TODO: extract this into a shared retry utility once the payments module is stable // HACK: Stripe webhook retries mean this handler can run twice — idempotency check is manual // DEPRECATED: use useAuthV2() instead — this will be removed in v3 ``` ### Module-level header comments Add a short header at the top of barrel files (`index.ts`) or the primary file of a directory. The brownfield pipeline uses this to infer the module's purpose without LLM calls. ```typescript // src/auth/index.ts // // Auth module — owns all identity concerns: JWT issue/refresh/revoke, session management, // and permission checks. No other module should read or write tokens directly. // Business feature: yes (user-facing login, OAuth, SSO flows). ``` ### Graph annotation comments When a file uses dynamic patterns that the static analyser cannot resolve, add explicit annotations so the dependency graph stays accurate: ```typescript // @wednesday-skills:connects-to login → src/auth/session.ts // (dynamic require resolved manually) const mod = require(`./handlers/${eventName}`) // @wednesday-skills:global db → src/lib/db/client.ts // (injected via app context, not imported directly) app.locals.db = createDbClient() ``` ### What NOT to write (filtered as noise) The following are intentionally ignored by the scanner — don't rely on them for documentation: ```typescript // @param userId - the user id ← JSDoc parameters // @returns Promise ← JSDoc return types // eslint-disable-next-line ← linter directives // prettier-ignore ← formatter directives // Copyright 2024 Wednesday Solutions ← copyright headers // Auto-generated by codegen ← generated file markers // do not edit ← generated file markers ``` For detailed examples and edge cases, see [references/COMPLEXITY.md](references/COMPLEXITY.md).