All skills
asyrafhussin avatar

/clean-code-principles

@680deef

SOLID principles, design patterns, DRY, KISS, and clean code fundamentals. Use when reviewing architecture, checking code quality, refactoring, or discussing design decisions. Triggers on "review architecture", "check code quality", "SOLID principles", "design patterns", or "clean code".

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/clean-code-principles

This session only. Nothing lands on disk.

rulescore-dry-single-source.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

DRY - Single Source of Truth

Every piece of knowledge or configuration should exist in exactly one place. When data or logic needs to be referenced from multiple locations, use a single authoritative source.

Bad Example

// Anti-pattern: Same values defined in multiple places

// In constants file
const API_BASE_URL = 'https://api.example.com/v1';

// In another file - duplicate!
const baseUrl = 'https://api.example.com/v1';

// In config.ts - another duplicate!
export const config = {
  apiUrl: 'https://api.example.com/v1'
};

// In API client - yet another!
class ApiClient {
  private baseUrl = 'https://api.example.com/v1'; // Duplicated!
}

// Status codes defined in multiple places
class OrderService {
  async getOrder(id: string): Promise<Order> {
    const order = await this.repository.findById(id);
    if (order.status === 'pending') { // Magic string
      // ...
    }
    if (order.status === 'completed') { // Magic string
      // ...
    }
  }
}

class OrderController {
  async listPendingOrders(): Promise<Order[]> {
    return this.repository.findByStatus('pending'); // Same magic string
  }
}

// In frontend code
const isPending = order.status === 'pending'; // And again

// Database seeds with duplicated data
const seedRoles = [
  { id: 1, name: 'admin', permissions: ['read', 'write', 'delete', 'admin'] },
  { id: 2, name: 'editor', permissions: ['read', 'write'] },
  { id: 3, name: 'viewer', permissions: ['read'] }
];

// In authorization middleware - duplicated permission logic
function checkPermission(user: User, action: string): boolean {
  if (user.role === 'admin') {
    return true; // Admin can do anything - duplicated knowledge
  }
  if (user.role === 'editor' && ['read', 'write'].includes(action)) {
    return true; // Editor permissions - duplicated
  }
  if (user.role === 'viewer' && action === 'read') {
    return true; // Viewer permissions - duplicated
  }
  return false;
}

Good Example

// Correct approach: Single source of truth for all shared knowledge

// Configuration - one authoritative source
// config/index.ts
export const Config = {
  api: {
    baseUrl: process.env.API_BASE_URL || 'https://api.example.com/v1',
    timeout: Number(process.env.API_TIMEOUT) || 30000,
    retries: Number(process.env.API_RETRIES) || 3
  },
  database: {
    url: process.env.DATABASE_URL!,
    poolSize: Number(process.env.DB_POOL_SIZE) || 10
  }
} as const;

// All code references the single config
class ApiClient {
  constructor(private baseUrl: string = Config.api.baseUrl) {}
}

// Enums for finite sets of values - single source of truth
// domain/order/status.ts
export const OrderStatus = {
  PENDING: 'pending',
  PROCESSING: 'processing',
  SHIPPED: 'shipped',
  DELIVERED: 'delivered',
  CANCELLED: 'cancelled'
} as const;

export type OrderStatus = typeof OrderStatus[keyof typeof OrderStatus];

// All code uses the enum
class OrderService {
  async getOrder(id: string): Promise<Order> {
    const order = await this.repository.findById(id);
    if (order.status === OrderStatus.PENDING) {
      // Single source of truth
    }
  }
}

class OrderController {
  async listPendingOrders(): Promise<Order[]> {
    return this.repository.findByStatus(OrderStatus.PENDING); // Same source
  }
}

// Roles and permissions - single authoritative definition
// domain/auth/roles.ts
export const Permission = {
  READ: 'read',
  WRITE: 'write',
  DELETE: 'delete',
  ADMIN: 'admin'
} as const;

export type Permission = typeof Permission[keyof typeof Permission];

export const RoleDefinitions = {
  admin: {
    name: 'Administrator',
    permissions: [Permission.READ, Permission.WRITE, Permission.DELETE, Permission.ADMIN]
  },
  editor: {
    name: 'Editor',
    permissions: [Permission.READ, Permission.WRITE]
  },
  viewer: {
    name: 'Viewer',
    permissions: [Permission.READ]
  }
} as const;

export type RoleName = keyof typeof RoleDefinitions;

// Permission checking uses the definitions
export function hasPermission(role: RoleName, permission: Permission): boolean {
  const roleDef = RoleDefinitions[role];
  return roleDef.permissions.includes(permission);
}

// Database seeds generated from the single source
export function generateRoleSeeds(): RoleSeed[] {
  return Object.entries(RoleDefinitions).map(([key, def], index) => ({
    id: index + 1,
    name: key,
    displayName: def.name,
    permissions: [...def.permissions]
  }));
}

// Validation rules - single source
// domain/user/validation.ts
export const UserValidationRules = {
  email: {
    maxLength: 255,
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  },
  password: {
    minLength: 8,
    maxLength: 128,
    requireUppercase: true,
    requireLowercase: true,
    requireNumber: true,
    requireSpecial: false
  },
  username: {
    minLength: 3,
    maxLength: 30,
    pattern: /^[a-zA-Z0-9_]+$/
  }
} as const;

// Validators use the rules
export function validateEmail(email: string): ValidationResult {
  const rules = UserValidationRules.email;

  if (email.length > rules.maxLength) {
    return { valid: false, error: `Email must be ${rules.maxLength} characters or less` };
  }
  if (!rules.pattern.test(email)) {
    return { valid: false, error: 'Invalid email format' };
  }
  return { valid: true };
}

// Database schema uses the same rules
// migrations/001_create_users.ts
export const createUsersTable = `
  CREATE TABLE users (
    id UUID PRIMARY KEY,
    email VARCHAR(${UserValidationRules.email.maxLength}) NOT NULL UNIQUE,
    username VARCHAR(${UserValidationRules.username.maxLength}) NOT NULL UNIQUE,
    -- ...
  )
`;

// Frontend validation uses the same rules (shared module)
// shared/validation.ts (used by both frontend and backend)
export function getPasswordRequirements(): string[] {
  const rules = UserValidationRules.password;
  const requirements: string[] = [];

  requirements.push(`At least ${rules.minLength} characters`);
  if (rules.requireUppercase) requirements.push('At least one uppercase letter');
  if (rules.requireLowercase) requirements.push('At least one lowercase letter');
  if (rules.requireNumber) requirements.push('At least one number');
  if (rules.requireSpecial) requirements.push('At least one special character');

  return requirements;
}

// Error messages - single source
// errors/messages.ts
export const ErrorMessages = {
  user: {
    notFound: 'User not found',
    alreadyExists: 'A user with this email already exists',
    invalidCredentials: 'Invalid email or password',
    accountLocked: 'Account is locked. Please contact support.'
  },
  order: {
    notFound: 'Order not found',
    alreadyCancelled: 'Order has already been cancelled',
    cannotCancel: 'Order cannot be cancelled in its current state'
  },
  auth: {
    tokenExpired: 'Your session has expired. Please log in again.',
    unauthorized: 'You do not have permission to perform this action'
  }
} as const;

// All code uses the same error messages
class UserService {
  async findById(id: string): Promise<User> {
    const user = await this.repository.findById(id);
    if (!user) {
      throw new NotFoundError(ErrorMessages.user.notFound);
    }
    return user;
  }
}

Why

  1. Consistency: The same value is always the same everywhere. No "this worked yesterday" bugs.

  2. Easy Updates: Change a validation rule, config value, or status code in one place.

  3. Prevents Drift: Without a single source, values diverge over time as different developers make changes.

  4. Documentation: The source file serves as documentation for what values are valid.

  5. Type Safety: TypeScript can enforce that only valid values are used.

  6. Searchability: Easy to find all usages by searching for the constant name.

  7. Refactoring: Rename a status? Change it in one place and let the compiler find all usages.

Source: SKILL.md on GitHub

1 warning16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill contains language-agnostic guidelines, examples, and reference material for clean code and SOLID principles. It consists purely of static documentation and contains no executable code or security risks.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    5/29 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 680deef. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
metadata
{
  "author": "AsyrafHussin",
  "version": "1.0.2"
}

README badge

README badge for asyrafhussin/agent-skills/clean-code-principles