Back to .md Directory

Global Coding Rules

Defines mandatory TypeScript, ESLint, import, naming, error handling, and security conventions for a Next.js project with Yarn and Husky.

May 2, 2026
0 downloads
1 views
ai
View source

What this file does

Defines mandatory TypeScript, ESLint, import, naming, error handling, and security conventions for a Next.js project with Yarn and Husky.

When to use it

  • Starting a new Next.js project and want strict TypeScript and linting rules enforced
  • Onboarding a team to a shared code style with explicit import ordering and naming
  • Adding automated git hooks and commit conventions to an existing project
  • Standardizing error handling and logging patterns across service layers

Assumes this stack

TypeScriptNext.jsESLintYarnHuskyCommitizen

trigger: always_on

Global Coding Rules

TypeScript Configuration

MUST use strict TypeScript configuration as defined in tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true
  }
}

MUST enable strict mode with no exceptions:

  • Use explicit typing for function parameters
  • No any types except in rare, documented cases
  • Use proper null checks with optional chaining
  • Enable strict property initialization checks

Code Formatting and Linting

MUST use ESLint with Next.js configuration:

{
  "scripts": {
    "lint": "eslint",
    "lint:fix": "eslint --fix"
  },
  "lint-staged": {
    "./src/**/*.{ts,js,jsx,tsx}": ["eslint --fix", "prettier --write -u"]
  }
}

MUST follow these ESLint patterns:

  • Use eslint-plugin-simple-import-sort for import ordering
  • Use eslint-plugin-unused-imports to remove unused imports
  • Follow Next.js ESLint configuration standards

Import Organization

MUST organize imports in this order:

// 1. External libraries
import React from "react";
import { NextRequest } from "next/server";

// 2. Internal absolute imports (domain, services, etc.)
import User from "@/domain/entities/User";
import Sessions from "@/services/auth/Sessions";

// 3. Relative imports
import "./Component.css";

MUST use absolute imports with @/ for all internal modules:

// Good
import db from "@/persistance/db";
import UserRepository from "@/domain/repositories/UserRepository";

// Bad
import db from "../../../persistance/db";
import UserRepository from "../../domain/repositories/UserRepository";

Package Management

MUST use Yarn as package manager (as specified in package.json):

{
  "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
}

MUST specify exact versions for critical dependencies:

{
  "dependencies": {
    "@trigger.dev/sdk": "4.0.1",
    "next": "15.5.0",
    "react": "19.1.0"
  }
}

Environment Configuration

MUST use environment variables for configuration:

# Database
DATABASE_URL=postgres://user:password@host:port/database

# Authentication
JWT_SECRET=your-very-secure-jwt-secret

# External Services
RESEND_API_KEY=re_your_resend_api_key
ELASTICSEARCH_URL=https://your-elasticsearch-cluster.com

MUST provide env.example with all required variables:

# Copy to .env.local and fill in your values
DATABASE_URL=
JWT_SECRET=
RESEND_API_KEY=
ELASTICSEARCH_URL=

Error Handling Patterns

MUST use consistent error handling across layers:

// Domain layer - Use domain-specific errors
class UserNotFoundError extends Error {
  constructor(id: string) {
    super(`User with id ${id} not found`);
    this.name = "UserNotFoundError";
  }
}

// Service layer - Transform to client errors
try {
  const user = await userRepository.findById(id);
} catch (error) {
  if (error instanceof UserNotFoundError) {
    throw new ActionClientError("USER_NOT_FOUND");
  }
  throw error;
}

// Frontend - Handle gracefully
try {
  await signIn(email, password);
} catch (error) {
  setError(error.message);
}

Async/Await Patterns

MUST use async/await over Promises for readability:

// Good
const processUser = async (id: string) => {
  const user = await userRepository.findById(id);
  const result = await processData(user);
  return result;
};

// Avoid
const processUser = (id: string) => {
  return userRepository
    .findById(id)
    .then((user) => processData(user))
    .then((result) => result);
};

Naming Conventions

MUST use consistent naming patterns:

Files and Directories:

  • PascalCase: Components, Classes, Types (UserComponent.tsx, UserRepository.ts)
  • camelCase: Functions, variables (getUserById, userService.ts)
  • kebab-case: Pages, routes (user-profile.tsx, auth-callback.ts)
  • lowercase: Configuration (middleware.ts, layout.tsx)

Code Elements:

// Interfaces and Types - PascalCase
interface UserProfile {
  id: string;
  name: string;
}

// Classes - PascalCase
class UserService {
  // Methods - camelCase
  async getUserById(id: string): Promise<User> {}
}

// Constants - SCREAMING_SNAKE_CASE
const MAX_RETRY_ATTEMPTS = 3;
const API_BASE_URL = "https://api.example.com";

// Variables and functions - camelCase
const userName = "john";
const fetchUserData = async () => {};

Performance Considerations

MUST implement performance best practices:

// Use React.memo for expensive components
const ExpensiveComponent = React.memo(({ data }) => {
  return <ComplexRendering data={data} />;
});

// Use proper dependency arrays in useEffect
useEffect(() => {
  fetchData(id);
}, [id]); // Include all dependencies

// Use Next.js Image for optimized images
import Image from "next/image";
const OptimizedImage = () => (
  <Image src="/image.jpg" alt="Description" width={500} height={300} />
);

// Implement proper loading states
const [isLoading, setIsLoading] = useState(false);

Security Practices

MUST follow security guidelines:

// Validate all inputs
const validateEmail = (email: string): boolean => {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};

// Use secure cookie settings
const cookieOptions = {
  httpOnly: true,
  secure: process.env.NODE_ENV === "production",
  sameSite: "strict" as const,
  maxAge: 7 * 24 * 60 * 60,
};

// Sanitize user inputs
const sanitizedInput = input.trim().toLowerCase();

// Use environment variables for secrets
const JWT_SECRET = process.env.JWT_SECRET!;

Git Commit Conventions

MUST use Conventional Commits with Commitizen:

{
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  }
}

Commit message format:

type(scope): description

feat(auth): add JWT token refresh functionality
fix(database): resolve connection pool timeout issue
docs(readme): update installation instructions
style(components): format button component code
refactor(services): extract common validation logic
test(auth): add unit tests for password hashing

Git Hooks with Husky

MUST use Husky for git hooks:

{
  "scripts": {
    "prepare": "husky"
  },
  "lint-staged": {
    "./src/**/*.{ts,js,jsx,tsx}": ["eslint --fix", "prettier --write -u"]
  }
}

Documentation Standards

MUST document complex functions and classes:

/**
 * Authenticates a user with email and password
 * @param email - User's email address
 * @param password - Plain text password
 * @returns Promise resolving to authenticated user
 * @throws ActionClientError when credentials are invalid
 */
const authenticateUser = async (
  email: string,
  password: string
): Promise<User> => {
  // Implementation
};

Testing Patterns

MUST follow testing conventions (when tests are added):

// Unit tests - test individual functions
describe("PasswordManager", () => {
  it("should hash passwords securely", async () => {
    const password = "test123";
    const hash = await PasswordManager.hash(password);
    expect(hash).not.toBe(password);
  });
});

// Integration tests - test service interactions
describe("AuthService", () => {
  it("should create user and send welcome email", async () => {
    const userData = { email: "test@example.com" };
    const user = await AuthService.createUser(userData);
    expect(user.id).toBeDefined();
    // Verify email was sent
  });
});

Bundle Analysis and Optimization

MUST use Next.js built-in optimization:

{
  "scripts": {
    "build": "next build --turbopack",
    "dev": "next dev --turbopack"
  }
}

MUST implement code splitting:

// Dynamic imports for large components
const HeavyComponent = dynamic(() => import("./HeavyComponent"), {
  loading: () => <Loading />,
});

// Route-based code splitting (automatic with App Router)
// pages/dashboard/page.tsx automatically creates a chunk

Logging Standards

MUST use structured logging:

const logger = {
  info: (message: string, meta?: Record<string, any>) => {
    console.log(
      JSON.stringify({
        level: "info",
        message,
        meta,
        timestamp: new Date().toISOString(),
      })
    );
  },
  error: (message: string, error?: Error) => {
    console.error(
      JSON.stringify({
        level: "error",
        message,
        error: error?.message,
        stack: error?.stack,
        timestamp: new Date().toISOString(),
      })
    );
  },
};

Code Review Guidelines

MUST ensure code reviews check for:

  • TypeScript strict compliance
  • Proper error handling
  • Security considerations (input validation, secrets)
  • Performance implications
  • Test coverage for new features
  • Documentation for complex logic
  • Consistent naming conventions
  • Proper import organization

What's inside

16 sections covering TypeScript config, ESLint, imports, package management, env vars, error handling, async/await, naming, performance, security, git conventions, Husky, documentation, testing, bundle analysis, and logging.

Change this for your project

  • Replace @/ import prefix if your project uses a different alias
  • Replace yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610 with your Yarn version
  • Replace @trigger.dev/sdk, next, react version pins with your dependency versions
  • Replace DATABASE_URL, JWT_SECRET, RESEND_API_KEY, ELASTICSEARCH_URL with your own env variable names

Where it goes

Save as AGENTS.md in your repository root. Read by Codex, Cursor and other agents that follow the AGENTS.md convention.

Worth borrowing

  • Domain-specific error classes that map to client-facing error codes
  • Structured logging with JSON output and timestamps
  • Explicit import ordering enforced by ESLint plugins

Related Documents