Skip to content

JavaScript – Naming Conventions

Consistent naming makes JavaScript code predictable and self-documenting. Because JavaScript is a dynamically typed language, names carry even more weight than in statically typed languages — a good name tells you not just the purpose of a value but often its intended type and role.

These conventions follow the Google JavaScript Style Guide and are enforced via ESLint in all Cygnus Dynamics JavaScript projects.

Applies to plain JavaScript files

This section covers .js source files. For React component naming see React – Component Rules. For Node.js module conventions see Node.js – Project Structure.

Quick Reference

Identifier Convention Example
Variables camelCase orderTotal, userEmailAddress
Functions camelCase (verb) calculateDiscount(), fetchUser()
Constants (module-level) UPPER_SNAKE_CASE MAX_RETRY_COUNT, API_BASE_URL
Constants (function-scoped) camelCase const defaultTimeout = 3000
Classes PascalCase (noun) OrderService, PaymentProcessor
Booleans camelCase — reads as question isActive, hasPermission
File names kebab-case or camelCase order-service.js, orderService.js
Private fields (class) #camelCase #orderId, #internalState
Enum / frozen constant objects PascalCase OrderStatus.PENDING
Symbol camelCase description Symbol('orderId')
Generator functions camelCase — noun or verb phrase function* orderSequence()

Variables

All variables must be camelCase, starting with a lowercase letter. Names must be descriptive and purposeful:

// ✅ Descriptive camelCase
const orderTotal = calculateTotal(cartItems);
const activeUserCount = users.filter(u => u.isActive).length;
const defaultCurrencyCode = 'USD';

// ❌ Vague, abbreviated, or wrong case
const t = calculateTotal(cartItems);     // single letter
const act_usr = users.length;            // snake_case not allowed
const OrderTotal = calculateTotal();     // PascalCase is for classes

One Declaration Per Statement

// ✅ One per line
const userId = request.params.id;
const userEmail = request.body.email;

// ❌ Multiple declarations on one line
let userId = request.params.id, userEmail = request.body.email;

const Over let, Never var

// ✅ const by default
const apiUrl = process.env.API_URL;
const user = await fetchUser(userId);

// ✅ let only when reassignment is required
let retryCount = 0;
while (retryCount < MAX_RETRIES) {
  retryCount++;
}

// ❌ var is banned — function-scoped, hoisted, unreliable in blocks
var userName = 'Alice';

Constants

Module-level constants use UPPER_SNAKE_CASE. Function-scoped constants use camelCase:

// ✅ Module-level constants — UPPER_SNAKE_CASE
const MAX_RETRY_COUNT = 3;
const DEFAULT_PAGE_SIZE = 20;
const API_BASE_URL = 'https://api.cygnusdynamics.com/v2';
const SESSION_TIMEOUT_MS = 1_800_000; // 30 minutes — numeric separators for readability

// ✅ Function-scoped constants — camelCase
function processOrder(order) {
  const taxRate = 0.20;           // local — camelCase
  const discountThreshold = 100;
  return order.total * (1 + taxRate);
}

Enums and Frozen Constant Objects

For groups of related constants, use Object.freeze to create a sealed enum-like object. The object name is PascalCase; the values are UPPER_SNAKE_CASE:

// ✅ Frozen enum object — immutable, iterable, autocompletable
export const OrderStatus = Object.freeze({
  PENDING:   'pending',
  CONFIRMED: 'confirmed',
  SHIPPED:   'shipped',
  DELIVERED: 'delivered',
  CANCELLED: 'cancelled',
});

export const UserRole = Object.freeze({
  ADMIN:    'admin',
  ENGINEER: 'engineer',
  VIEWER:   'viewer',
});

// Usage — readable and type-safe at the call site
if (order.status === OrderStatus.CONFIRMED) {
  await scheduleShipment(order);
}

// ❌ Separate loose constants — no grouping, no discoverability
const ORDER_STATUS_PENDING   = 'pending';
const ORDER_STATUS_CONFIRMED = 'confirmed';

// ❌ Plain object without freeze — values can be accidentally mutated
const OrderStatus = {
  PENDING: 'pending',
};
OrderStatus.PENDING = 'oops'; // silently changes the enum — Object.freeze prevents this

Functions

Function names are camelCase and must begin with a verb:

// ✅ Verb-first, descriptive camelCase
function fetchOrderById(orderId) { }
function calculateShippingCost(order, destination) { }
function validateEmailAddress(email) { }
function sendConfirmationEmail(user, order) { }

// ❌ Noun names, vague names, wrong case
function order(id) { }
function FetchOrder(id) { }
function doStuff() { }

Boolean-returning Functions

// ✅ Reads as a natural question
function isOrderEligibleForDiscount(order) { }
function hasActiveSubscription(user) { }
function canProcessRefund(order, user) { }
function shouldRetryRequest(error, attemptCount) { }

Arrow Functions

// ✅ Arrow functions for callbacks and inline expressions
const activeOrders = orders.filter(order => order.status === OrderStatus.ACTIVE);
const orderIds = orders.map(order => order.id);

// ✅ Named function declarations for top-level, exported, standalone functions
function calculateOrderTotal(items) {
  return items.reduce((total, item) => total + item.price * item.quantity, 0);
}

// ❌ Anonymous arrow for top-level named functions — hard to trace in stack traces
const calculateOrderTotal = (items) => {
  return items.reduce(...);
};

Classes

Class names are PascalCase nouns:

// ✅
class OrderService { }
class PaymentProcessor { }
class UserSessionManager { }

// ❌
class orderService { }
class ProcessOrders { }   // verb phrase — use a noun

Private Class Fields

Use the native # private field syntax (ES2022+):

// ✅ Native private fields with #
class OrderService {
  #orderRepository;
  #paymentService;

  constructor(orderRepository, paymentService) {
    this.#orderRepository = orderRepository;
    this.#paymentService = paymentService;
  }

  async #validateOrder(order) { }   // private method
  async createOrder(request) { }    // public method
}

// ❌ Underscore convention — not actually private
class OrderService {
  constructor() {
    this._orderRepository = null;  // still publicly accessible
  }
}

Generator Functions

Generator functions follow the same camelCase verb convention. Name them descriptively to communicate what sequence they produce:

// ✅ Generator function — camelCase, communicates what it generates
function* generateOrderIds(startId, count) {
  for (let i = 0; i < count; i++) {
    yield startId + i;
  }
}

// ✅ Async generator for streaming data
async function* streamOrderBatch(userId, batchSize) {
  let cursor = null;
  do {
    const batch = await orderRepo.findBatch(userId, { cursor, limit: batchSize });
    for (const order of batch.items) {
      yield order;
    }
    cursor = batch.nextCursor;
  } while (cursor);
}

// Usage
for await (const order of streamOrderBatch(userId, 50)) {
  await processOrder(order);
}

Symbol Naming

Symbols are used for unique keys that prevent naming collisions. Describe the symbol's purpose in the Symbol() description string:

// ✅ Symbol for unique property keys — description reflects purpose
const orderIdSymbol      = Symbol('orderId');
const internalStateSymbol = Symbol('internalState');

// ✅ Well-known symbols — use the standard name
class OrderCollection {
  [Symbol.iterator]() {
    let index = 0;
    const items = this.#items;
    return {
      next() {
        return index < items.length
          ? { value: items[index++], done: false }
          : { done: true };
      },
    };
  }
}

// ✅ Symbol.for() for shared symbols across modules
const REQUEST_ID = Symbol.for('cygnus.requestId');  // namespaced description

File Names

JavaScript file names must be all lowercase, using kebab-case (preferred) or camelCase consistently within a project:

✅ Correct
order-service.js
payment-processor.js
user-profile-utils.js
index.js

✅ Also acceptable (pick one, be consistent)
orderService.js
paymentProcessor.js

❌ Incorrect
OrderService.js        // uppercase — fails on case-sensitive filesystems
order_service.js       // snake_case
my component.js        // space in name

Booleans

Boolean variables must read as a clear true/false question in an if statement:

// ✅ Reads naturally
const isAuthenticated = checkAuth(token);
const hasUnreadMessages = unreadCount > 0;
const isExpressShipping = cart.shippingOption === 'express';

if (isAuthenticated && hasUnreadMessages) {
  showNotificationBadge();
}

// ❌ Ambiguous
const authenticated = checkAuth(token);
const unreadMessages = unreadCount > 0;  // looks like a count

Special Naming Patterns

Event Handlers

// ✅ handle or on prefix
function handleFormSubmit(event) { }
function handlePaymentSuccess(result) { }
const onUserLogout = () => { clearSession(); };

Async Functions

Do not append Async — the async keyword makes it explicit:

// ✅
async function fetchUserProfile(userId) { }
async function processPayment(order, card) { }

// ❌ Redundant suffix
async function fetchUserProfileAsync(userId) { }

Callback Parameters

// ✅ Descriptive callback parameter names
users.forEach(user => sendWelcomeEmail(user));
orders.filter(order => order.total > MINIMUM_ORDER_VALUE);

// ❌ Generic, meaningless names
users.forEach(x => sendWelcomeEmail(x));

Things to Avoid

Abbreviations — write userCount not usrCnt. Accepted abbreviations: id, url, api, http, html, json, dto, num.

Hungarian notationstrName, intCount, boolActive are banned.

Single-letter names — only acceptable for loop indices (i, j, k) and mathematical parameters (x, y, n).

$ prefix — do not use $ as a variable prefix. It conflicts with jQuery conventions.