BMAD-METHOD/expansion-packs/bmad-javascript-fullstack/data/development-guidelines.md

8.4 KiB

JavaScript/TypeScript Development Guidelines

Overview

This document establishes coding standards, best practices, and conventions for JavaScript/TypeScript full-stack development. These guidelines ensure consistency, quality, and maintainability.

TypeScript Standards

Configuration

Required tsconfig.json settings:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Type Definitions

  • No any types - Use unknown if type is truly unknown
  • Interface vs Type - Prefer interfaces for object shapes, types for unions/intersections
  • Explicit return types - Always specify return types for functions
  • Proper generics - Use generics for reusable type-safe code
// ✅ Good
interface UserProps {
  id: string;
  name: string;
  email: string;
}

function getUser(id: string): Promise<User | null> {
  return db.user.findUnique({ where: { id } });
}

// ❌ Bad
function getUser(id: any): any {
  return db.user.findUnique({ where: { id } });
}

Naming Conventions

Files and Folders

  • kebab-case for utility files: api-client.ts, string-utils.ts
  • PascalCase for components: UserProfile.tsx, TodoList.tsx
  • camelCase for hooks: useAuth.ts, useLocalStorage.ts

Variables and Functions

  • camelCase for variables and functions: userName, fetchData()
  • PascalCase for classes and components: UserService, Button
  • UPPER_SNAKE_CASE for constants: API_BASE_URL, MAX_RETRIES
  • Descriptive names - isLoading not loading, handleSubmit not submit
// ✅ Good
const MAX_RETRY_COUNT = 3;
const isAuthenticated = checkAuth();
function calculateTotalPrice(items: Item[]): number { }

// ❌ Bad
const max = 3;
const auth = checkAuth();
function calc(items: any): any { }

React Best Practices

Component Structure

// ✅ Good component structure
interface ButtonProps {
  children: React.ReactNode;
  onClick: () => void;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
}

export function Button({
  children,
  onClick,
  variant = 'primary',
  disabled = false
}: ButtonProps) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={`btn btn-${variant}`}
    >
      {children}
    </button>
  );
}

Hooks Rules

  • Call hooks at the top level
  • Call hooks in the same order
  • Only call hooks from React functions
// ✅ Good
function Component() {
  const [state, setState] = useState(0);
  const data = useFetch('/api/data');

  useEffect(() => {
    // effect
  }, []);

  return <div>{data}</div>;
}

// ❌ Bad - conditional hook
function Component() {
  if (condition) {
    useState(0); // ❌ Don't do this
  }
}

State Management

  • Local state - useState for component-only state
  • Server state - React Query/SWR for API data
  • Global state - Zustand/Redux only when necessary
  • Derived state - useMemo for computed values

Backend Best Practices

API Endpoints

// ✅ Good - RESTful design
GET    /api/v1/users
POST   /api/v1/users
PATCH  /api/v1/users/:id
DELETE /api/v1/users/:id

// ❌ Bad - non-RESTful
GET    /api/v1/getUsers
POST   /api/v1/createUser
POST   /api/v1/updateUser/:id

Error Handling

// ✅ Good - centralized error handling
class AppError extends Error {
  constructor(
    public statusCode: number,
    public message: string,
    public isOperational = true
  ) {
    super(message);
  }
}

app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  if (err instanceof AppError) {
    return res.status(err.statusCode).json({
      error: err.message
    });
  }

  console.error(err);
  res.status(500).json({ error: 'Internal server error' });
});

Input Validation

// ✅ Good - Zod validation
import { z } from 'zod';

const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
  password: z.string().min(8),
});

app.post('/users', async (req, res) => {
  const result = createUserSchema.safeParse(req.body);

  if (!result.success) {
    return res.status(400).json({
      errors: result.error.errors
    });
  }

  const user = await createUser(result.data);
  res.status(201).json(user);
});

Code Organization

Frontend Structure

src/
├── components/
│   ├── ui/           # Reusable UI components
│   ├── features/     # Feature-specific components
│   └── layout/       # Layout components
├── hooks/            # Custom React hooks
├── lib/              # Utilities and helpers
├── pages/            # Next.js pages or routes
├── styles/           # Global styles
├── types/            # TypeScript types
└── utils/            # Utility functions

Backend Structure

src/
├── controllers/      # Request handlers
├── services/         # Business logic
├── repositories/     # Data access layer
├── middleware/       # Express/Fastify middleware
├── routes/           # Route definitions
├── types/            # TypeScript types
├── utils/            # Utility functions
└── config/           # Configuration files

Testing Standards

Frontend Tests

// ✅ Good - React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';

describe('Button', () => {
  it('calls onClick when clicked', async () => {
    const handleClick = jest.fn();
    render(<Button onClick={handleClick}>Click me</Button>);

    await fireEvent.click(screen.getByText('Click me'));

    expect(handleClick).toHaveBeenCalledTimes(1);
  });
});

Backend Tests

// ✅ Good - Supertest API tests
import request from 'supertest';
import { app } from '../app';

describe('POST /users', () => {
  it('creates a new user', async () => {
    const response = await request(app)
      .post('/users')
      .send({
        email: 'test@example.com',
        name: 'Test User',
        password: 'password123',
      })
      .expect(201);

    expect(response.body).toMatchObject({
      email: 'test@example.com',
      name: 'Test User',
    });
  });
});

Security Guidelines

Authentication

  • Hash passwords with bcrypt (10-12 rounds)
  • Use JWT with short expiration (15-60 min)
  • Implement refresh tokens
  • Store tokens in httpOnly cookies

Authorization

  • Check permissions on every protected route
  • Implement RBAC or ABAC
  • Never trust client-side checks

Input Validation

  • Validate ALL user inputs
  • Sanitize HTML content
  • Use parameterized queries for SQL
  • Validate file uploads (type, size)

API Security

  • Enable CORS with specific origins
  • Use rate limiting
  • Implement CSRF protection
  • Set security headers (Helmet.js)

Performance Guidelines

Frontend

  • Code split routes and heavy components
  • Lazy load images below the fold
  • Use next/image for automatic optimization
  • Memoize expensive computations
  • Virtual scroll long lists

Backend

  • Add database indexes on frequently queried fields
  • Use connection pooling
  • Implement caching (Redis)
  • Paginate large datasets
  • Use background jobs for long tasks

Git Commit Conventions

# Format: <type>(<scope>): <subject>

# Types:
feat:     # New feature
fix:      # Bug fix
docs:     # Documentation only
style:    # Code style (formatting, no logic change)
refactor: # Code refactoring
test:     # Adding tests
chore:    # Maintenance (deps, build, etc)

# Examples:
feat(auth): add password reset functionality
fix(api): handle null values in user endpoint
docs(readme): update setup instructions
refactor(components): extract Button component

Code Review Checklist

Before submitting PR:

  • TypeScript compiles without errors
  • ESLint passes with no warnings
  • All tests pass
  • Test coverage meets requirements (>80%)
  • No console.logs or debugger statements
  • Meaningful commit messages
  • PR description explains changes
  • Documentation updated if needed

This living document should be updated as the team establishes new patterns and best practices.