Skip to main content

Code Standards: Don't Let Your Code Become a Mess

Code is written for humans to read, and only incidentally for machines to execute. — Harold Abelson

Why Do Code Standards Matter?

You might think: "As long as the code works, who cares about formatting?"

Wrong. There are two reasons:

1. You'll Be Collaborating with Others

Imagine you and a friend are cooking together. You measure in centimeters, they measure in inches. You add salt by the spoonful, they add "a pinch." No wonder the dish turns out terrible.

Code standards are the agreed-upon "measurement system" — ensuring everyone's code looks like it was written by one person.

2. AI Needs Clean Code Too

AI tools (Cursor, Copilot) reference your existing code style when generating code. If your code is messy, the AI-generated code will be messy too.

Consistent code style = AI understands your intent better = generates more accurate code

Naming Conventions

Naming is the foundation of code readability. Three most common naming styles:

camelCase

First letter lowercase, first letter of each subsequent word capitalized:

const userName = "John";
const isLoggedIn = true;
function getUserInfo() { }
function handleSubmitForm() { }

Used for: JavaScript/TypeScript variables and functions

PascalCase

First letter of every word capitalized:

const UserProfile = () => { };
class ShoppingCart { }
interface ButtonProps { }
type UserType = { };

Used for: React components, class names, interfaces, types

snake_case

Words separated by underscores:

user_name = "John"
is_logged_in = True
def get_user_info(): pass

Used for: Python, database field names, environment variables

Naming Quick Reference

Language/ScenarioStyleExample
JS/TS variablescamelCaseuserName
JS/TS functionscamelCasegetUserInfo()
React componentsPascalCaseUserProfile
ConstantsUPPER_SNAKEAPI_BASE_URL
CSS class nameskebab-caseuser-card
Database fieldssnake_casecreated_at
Environment variablesUPPER_SNAKEDATABASE_URL

Good Names vs Bad Names

// ❌ Bad naming
const d = new Date();
const x = users.filter(u => u.a > 18);
function calc(a, b) { return a * b * 0.1; }

// ✅ Good naming
const currentDate = new Date();
const adultUsers = users.filter(user => user.age > 18);
function calculateTax(price, quantity) { return price * quantity * 0.1; }

Naming principles:

  • Names should express intent — don't be afraid of long names
  • Booleans should start with is, has, or can
  • Function names should start with verbs: get, set, handle, create, delete

ESLint + Prettier Setup

ESLint checks code quality, Prettier enforces code formatting. Together, they keep your code consistent automatically.

Installation

npm install -D eslint prettier eslint-config-prettier eslint-plugin-prettier

ESLint Configuration (Next.js)

Create .eslintrc.json:

{
"extends": [
"next/core-web-vitals",
"prettier"
],
"rules": {
"no-unused-vars": "warn",
"no-console": "warn",
"prefer-const": "error",
"no-var": "error"
}
}

Prettier Configuration

Create .prettierrc:

{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80,
"bracketSpacing": true,
"arrowParens": "always"
}

Auto-Format on Save

Add the following to VS Code's settings.json:

{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}

Now every time you press Ctrl+S, your code is auto-formatted and lint issues are automatically fixed.

.editorconfig

.editorconfig ensures different editors use the same formatting settings:

root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

Place this file in your project root and commit it to Git. Team members using different editors will stay consistent.

Next.js Project Folder Structure

A good folder structure makes the project easy to understand at a glance:

my-app/
├── src/
│ ├── app/ # Next.js App Router pages
│ │ ├── layout.tsx # Global layout
│ │ ├── page.tsx # Homepage
│ │ ├── login/
│ │ │ └── page.tsx # Login page
│ │ └── api/ # API routes
│ │ └── users/
│ │ └── route.ts
│ ├── components/ # Reusable components
│ │ ├── ui/ # Base UI components
│ │ │ ├── Button.tsx
│ │ │ └── Input.tsx
│ │ └── layout/ # Layout components
│ │ ├── Header.tsx
│ │ └── Footer.tsx
│ ├── lib/ # Utility functions and config
│ │ ├── db.ts # Database connection
│ │ ├── auth.ts # Authentication logic
│ │ └── utils.ts # General utility functions
│ ├── hooks/ # Custom Hooks
│ │ └── useUser.ts
│ ├── types/ # TypeScript type definitions
│ │ └── index.ts
│ └── styles/ # Style files
│ └── globals.css
├── public/ # Static assets
│ └── images/
├── .env.local # Environment variables
├── .gitignore
├── next.config.js
├── package.json
├── tsconfig.json
└── README.md

File Naming Conventions

  • Component files: PascalCase — Button.tsx, UserProfile.tsx
  • Utility files: camelCase — useUser.ts, formatDate.ts
  • Page files: Always page.tsx (Next.js convention)
  • Style files: camelCase or kebab-case

The Art of Commenting

When to Write Comments

// ✅ Explain "why" not "what"
// Need to delay 300ms because Stripe's SDK takes time to load
await new Promise(resolve => setTimeout(resolve, 300));

// ✅ Explain complex business logic
// Discount rules: ¥30 off for orders over ¥200, ¥100 off for orders over ¥500, VIP gets an extra 10% off
function calculateDiscount(amount, isVip) { }

// ✅ Mark to-do items
// TODO: Add SMS verification later

// ✅ Warning messages
// WARNING: Do not modify this value — it affects all existing user passwords
const SALT_ROUNDS = 10;

When NOT to Write Comments

// ❌ Comment states the obvious
// Get the user name
const userName = getUser().name;

// ❌ Comment is longer than the code
// This function iterates through the array, checks each item's type property,
// and if the type equals 'active', adds it to the result array
const activeItems = items.filter(item => item.type === 'active');

// ❌ Commented-out code (just delete it — Git remembers everything)
// oldFunction();
// const temp = doSomething();

Principle: Good code speaks for itself. If you need lots of comments, the code isn't clear enough.

Using .cursorrules to Standardize AI-Generated Code

The .cursorrules file goes in your project root and tells Cursor AI your code style preferences:

# Project Standards

## Tech Stack
- Next.js 14+ (App Router)
- TypeScript (strict mode)
- Tailwind CSS
- Supabase

## Code Style
- Use functional components, not class components
- Use const, not let or var
- Components use PascalCase, variables use camelCase
- Prefer Server Components; use Client Components only when interactivity is needed
- Each file exports only one component

## Naming Conventions
- Page files: page.tsx
- Layout files: layout.tsx
- Component files: PascalCase.tsx
- Hook files: useXxx.ts
- Utility files: camelCase.ts

## Error Handling
- Use try/catch for async operations
- Show user-friendly error messages — don't expose technical details
- Use toast notifications for operation results

## Comments
- Complex logic must have comments
- Use JSDoc at the top of components for descriptions
- Don't write obvious comments

With this file, AI-generated code will be much more aligned with your project's style.

Code Review Checklist

Even if you're working solo, use this checklist to review your own code:

Basic Checks

  • Does the code run correctly?
  • Any leftover console.log statements?
  • Any unused variables or imports?
  • Are the names clear and meaningful?

Code Quality

  • Any duplicated code? Can it be extracted into a function/component?
  • Are functions too long? (Consider splitting if over 50 lines)
  • Is error handling comprehensive?
  • Any hardcoded values? (Should use constants or environment variables)

Security Checks

  • Any exposed sensitive information? (API keys, passwords)
  • Is user input validated?
  • Any SQL injection or XSS risks?

Performance Checks

  • Any unnecessary re-renders? (React)
  • Are large lists paginated or virtualized?
  • Are images optimized? (Compressed, lazy-loaded)

Commit Checks

  • Is the commit message following conventions?
  • Are there unnecessary files being committed? (.env, node_modules)
  • Does the code pass ESLint checks?

Commit Message Conventions

Good commit messages make Git history clear at a glance:

# Format: type: short description

# ✅ Good commit messages
feat: add user login functionality
fix: fix homepage loading slowly
docs: update README installation instructions
style: unify code formatting
refactor: refactor authentication module
chore: upgrade Next.js to 14.1

# ❌ Bad commit messages
update
fix bug
modified
asdfgh
TypePurpose
featNew feature
fixBug fix
docsDocumentation
styleFormatting (doesn't affect functionality)
refactorRefactoring
choreHousekeeping
testTesting

Summary

  • Name things clearly — code is written for humans
  • Set up ESLint + Prettier — auto-format on save
  • Keep folder structure consistent — easy to find, easy to maintain
  • Comments should explain "why" — not "what"
  • Use .cursorrules to guide AI — generate more consistent code
  • Use the checklist before committing — build good habits

Code standards aren't a constraint — they're a tool that helps you write better code. Once standards become habit, you won't need to think about them anymore.