javascript-excellence

JavaScript Excellence

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "javascript-excellence" with this command: npx skills add tejovanthn/rasikalife/tejovanthn-rasikalife-javascript-excellence

JavaScript Excellence

This skill embodies DHH's coding philosophy adapted for JavaScript and TypeScript - emphasizing clarity, simplicity, and expressiveness.

Core Principles

  • Clarity over Cleverness: Code should be obvious, not clever

  • Simplicity over Complexity: The simplest solution is usually the best

  • Expressiveness: Code should read like prose

  • DRY (Don't Repeat Yourself): Eliminate duplication ruthlessly

  • Convention over Configuration: Sensible defaults, customize when needed

  • No Premature Abstraction: Wait until patterns emerge

Language Best Practices

Use Modern JavaScript Features

✅ Do use modern syntax:

// Destructuring const { name, email } = user;

// Spread operators const newUser = { ...user, verified: true };

// Optional chaining const street = user?.address?.street;

// Nullish coalescing const name = user.name ?? "Anonymous";

// Template literals const greeting = Hello, ${name}!;

❌ Don't write old-style code:

// Don't var name = user.name ? user.name : "Anonymous"; var newUser = Object.assign({}, user, { verified: true });

Prefer Const and Let

✅ Do use const by default:

const users = await getUsers(); const total = users.length;

❌ Don't use var:

var users = await getUsers(); // No!

Use Meaningful Names

✅ Do use descriptive names:

async function sendWelcomeEmail(user: User) { const emailContent = buildWelcomeEmail(user); await emailService.send(emailContent); }

❌ Don't use cryptic abbreviations:

async function sndWlcmEml(u: User) { // What? const ec = bldWlcmEml(u); await es.snd(ec); }

TypeScript Best Practices

Use Type Inference

✅ Do let TypeScript infer:

const users = await db.user.findMany(); // Type is inferred const count = users.length; // Type is inferred

function double(n: number) { return n * 2; // Return type inferred as number }

❌ Don't over-specify:

const users: User[] = await db.user.findMany(); // Redundant const count: number = users.length; // Redundant

function double(n: number): number { // Return type unnecessary return n * 2; }

Use Discriminated Unions for State

✅ Do model states explicitly:

type LoadingState<T> = | { status: "idle" } | { status: "loading" } | { status: "success"; data: T } | { status: "error"; error: Error };

function render(state: LoadingState<User>) { switch (state.status) { case "idle": return <div>Not started</div>; case "loading": return <div>Loading...</div>; case "success": return <div>{state.data.name}</div>; // data is available! case "error": return <div>Error: {state.error.message}</div>; } }

❌ Don't use boolean flags:

type LoadingState<T> = { isLoading: boolean; isError: boolean; data?: T; error?: Error; }; // Can have invalid states like isLoading=true and isError=true

Keep Types Simple

✅ Do use simple types:

type User = { id: string; name: string; email: string; };

type CreateUserInput = Pick<User, "name" | "email">;

❌ Don't over-engineer types:

type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T;

type RecursiveRequired<T> = { [P in keyof T]-?: RecursiveRequired<T[P]>; }; // Unless you really need this complexity!

Function Patterns

Keep Functions Small and Focused

✅ Do write focused functions:

async function createUser(data: CreateUserInput) { const hashedPassword = await hashPassword(data.password); const user = await db.user.create({ data: { ...data, password: hashedPassword } }); await sendWelcomeEmail(user); return user; }

async function hashPassword(password: string) { return bcrypt.hash(password, 10); }

async function sendWelcomeEmail(user: User) { // Email logic here }

❌ Don't write giant functions:

async function createUser(data: CreateUserInput) { // 200 lines of mixed concerns // password hashing // validation // database access // email sending // logging // etc. }

Use Early Returns

✅ Do use guard clauses:

function processPayment(amount: number, user: User) { if (amount <= 0) { throw new Error("Invalid amount"); }

if (!user.paymentMethod) { throw new Error("No payment method"); }

if (user.balance < amount) { throw new Error("Insufficient balance"); }

// Happy path at the end return chargePayment(user, amount); }

❌ Don't nest deeply:

function processPayment(amount: number, user: User) { if (amount > 0) { if (user.paymentMethod) { if (user.balance >= amount) { return chargePayment(user, amount); } else { throw new Error("Insufficient balance"); } } else { throw new Error("No payment method"); } } else { throw new Error("Invalid amount"); } }

Use Async/Await Consistently

✅ Do use async/await:

async function loadUserData(userId: string) { const user = await db.user.findUnique({ where: { id: userId } }); const posts = await db.post.findMany({ where: { userId } }); return { user, posts }; }

❌ Don't mix promises and async/await:

async function loadUserData(userId: string) { const user = await db.user.findUnique({ where: { id: userId } }); return db.post.findMany({ where: { userId } }).then(posts => { return { user, posts }; }); }

Object and Array Patterns

Use Destructuring

✅ Do destructure:

function createPost({ title, content, authorId }: CreatePostInput) { return db.post.create({ data: { title, content, authorId } }); }

const { name, email } = user; const [first, second, ...rest] = items;

Use Array Methods

✅ Do use functional array methods:

const activeUsers = users.filter(u => u.active); const userNames = users.map(u => u.name); const totalScore = scores.reduce((sum, score) => sum + score, 0); const hasAdmin = users.some(u => u.role === "admin");

❌ Don't use for loops unnecessarily:

const activeUsers = []; for (let i = 0; i < users.length; i++) { if (users[i].active) { activeUsers.push(users[i]); } }

Use Object Methods

✅ Do use Object methods:

const keys = Object.keys(user); const values = Object.values(user); const entries = Object.entries(user);

const merged = Object.assign({}, defaults, overrides); // Or better: const merged = { ...defaults, ...overrides };

Error Handling

Use Try-Catch for Async

✅ Do handle errors:

async function getUser(id: string) { try { return await db.user.findUnique({ where: { id } }); } catch (error) { logger.error("Failed to fetch user", { error, id }); throw new Error("User not found"); } }

Create Custom Errors

✅ Do use custom error classes:

class NotFoundError extends Error { constructor(message: string) { super(message); this.name = "NotFoundError"; } }

class ValidationError extends Error { constructor(message: string, public fields: string[]) { super(message); this.name = "ValidationError"; } }

// Usage if (!user) { throw new NotFoundError("User not found"); }

Code Organization

Group Related Code

✅ Do organize by feature:

src/ features/ auth/ login.ts signup.ts session.ts posts/ create.ts list.ts edit.ts

❌ Don't organize by type:

src/ controllers/ authController.ts postController.ts services/ authService.ts postService.ts models/ user.ts post.ts

Extract Reusable Logic

✅ Do extract when patterns emerge:

// After seeing this pattern 3+ times function requireAuth<T>( handler: (userId: string, input: T) => Promise<Response> ) { return async (input: T) => { const userId = await getUserId(); if (!userId) { throw redirect("/login"); } return handler(userId, input); }; }

// Use it export const loader = requireAuth(async (userId, { params }) => { // userId is guaranteed to exist });

❌ Don't abstract too early:

// Don't create "BaseController" after one use class BaseController { // Generic abstraction nobody asked for }

Comments and Documentation

Write Self-Documenting Code

✅ Do write clear code:

function calculateTotalPrice(items: CartItem[]) { const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0); const tax = subtotal * TAX_RATE; return subtotal + tax; }

❌ Don't comment obvious code:

// Calculate total price function calc(items: CartItem[]) { // Sum all items const s = items.reduce((sum, item) => sum + item.price * item.quantity, 0); // Calculate tax const t = s * TAX_RATE; // Return total return s + t; }

Comment the "Why", Not the "What"

✅ Do explain reasoning:

// We disable scroll lock on mobile because iOS Safari has issues // with position:fixed when the keyboard is open. See issue #123 if (isMobile) { disableScrollLock(); }

❌ Don't state the obvious:

// Check if mobile if (isMobile) { // Disable scroll lock disableScrollLock(); }

Testing Patterns

Write Readable Tests

✅ Do write clear tests:

test("creates user with hashed password", async () => { const input = { email: "test@example.com", password: "secret123" };

const user = await createUser(input);

expect(user.email).toBe(input.email); expect(user.password).not.toBe(input.password); expect(await verifyPassword(input.password, user.password)).toBe(true); });

Test Behavior, Not Implementation

✅ Do test outcomes:

test("returns 404 when post not found", async () => { const response = await app.request("/posts/invalid-id"); expect(response.status).toBe(404); });

❌ Don't test internals:

test("calls database with correct query", async () => { const spy = vi.spyOn(db, "query"); await getPost("123"); expect(spy).toHaveBeenCalledWith({ id: "123" }); });

Anti-Patterns to Avoid

  1. Callback Hell

❌ Don't nest callbacks:

getUser(userId, (error, user) => { if (error) return handleError(error); getPosts(user.id, (error, posts) => { if (error) return handleError(error); // ...more nesting }); });

✅ Do use async/await:

const user = await getUser(userId); const posts = await getPosts(user.id);

  1. Mutation Everywhere

❌ Don't mutate unnecessarily:

function addItem(cart: Cart, item: Item) { cart.items.push(item); // Mutates input return cart; }

✅ Do prefer immutability:

function addItem(cart: Cart, item: Item) { return { ...cart, items: [...cart.items, item] }; }

  1. God Objects

❌ Don't create giant classes:

class UserManager { createUser() {} updateUser() {} deleteUser() {} authenticateUser() {} sendEmail() {} generateReport() {} // 50 more methods... }

✅ Do use focused modules:

// user.ts export function createUser() {} export function updateUser() {}

// auth.ts export function authenticate() {}

// email.ts export function sendEmail() {}

Remember

DHH's philosophy in JavaScript:

  • Write code that reads naturally

  • Prefer simplicity over cleverness

  • Eliminate duplication

  • Keep functions small and focused

  • Use the language's features effectively

  • Don't abstract too early

  • Make invalid states unrepresentable

  • Test behavior, not implementation

Your code should be a joy to read and maintain.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

frontend-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

email-templates

No summary provided by upstream source.

Repository SourceNeeds Review
General

marketing-copy

No summary provided by upstream source.

Repository SourceNeeds Review
General

conform

No summary provided by upstream source.

Repository SourceNeeds Review