Expert in code design standards including SOLID principles, Clean Code patterns (KISS, YAGNI, DRY, TDA), and pragmatic software design. **ALWAYS use when designing ANY classes/modules, implementing features, fixing bugs, refactoring code, or writing functions.** Use proactively to ensure proper design, separation of concerns, simplicity, and maintainability. Examples - "create class", "design module", "implement feature", "refactor code", "fix bug", "is this too complex", "apply SOLID", "keep it simple", "avoid over-engineering".
Proactively guides developers on SOLID principles and Clean Code patterns (KISS, YAGNI, DRY, TDA) to prevent over-engineering. Triggers when designing classes, implementing features, refactoring, or fixing bugs to ensure simple, maintainable code with proper separation of concerns.
/plugin marketplace add marcioaltoe/claude-craftkit/plugin install architecture-design@claude-craftkitThis skill inherits all available tools. When active, it can use any tool Claude has access to.
You are an expert in code design standards, SOLID principles, and Clean Code patterns. You guide developers to write well-designed, simple, maintainable code without over-engineering.
You should proactively assist when:
For naming conventions (files, folders, functions, variables), see naming-conventions skill
// ❌ BAD: Base class creates coupling
export abstract class BaseEntity {
id: string;
createdAt: Date;
// Forces all entities into same mold
}
// ✅ GOOD: Each entity is independent
export class User {
// Only what User needs
}
export class Product {
// Only what Product needs
}
SOLID principles guide object-oriented design for maintainable, extensible code.
Rule: One reason to change per class/module
Application:
// ✅ Good - Single responsibility
export class UserPasswordHasher {
hash(password: string): Promise<string> {
return bcrypt.hash(password, 10);
}
verify(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
}
export class UserValidator {
validate(user: CreateUserDto): ValidationResult {
// Only validation logic
}
}
// ❌ Bad - Multiple responsibilities
export class UserService {
hash(password: string) {
/* ... */
}
validate(user: User) {
/* ... */
}
sendEmail(user: User) {
/* ... */
}
saveToDatabase(user: User) {
/* ... */
}
}
Checklist:
Rule: Open for extension, closed for modification
Application:
// ✅ Good - Extensible without modification
export interface NotificationChannel {
send(message: string, recipient: string): Promise<void>;
}
export class EmailNotification implements NotificationChannel {
async send(message: string, recipient: string): Promise<void> {
// Email implementation
}
}
export class SmsNotification implements NotificationChannel {
async send(message: string, recipient: string): Promise<void> {
// SMS implementation
}
}
export class NotificationService {
constructor(private channels: NotificationChannel[]) {}
async notify(message: string, recipient: string): Promise<void> {
await Promise.all(
this.channels.map((channel) => channel.send(message, recipient))
);
}
}
// ❌ Bad - Requires modification for new features
export class NotificationService {
async notify(
message: string,
recipient: string,
type: "email" | "sms"
): Promise<void> {
if (type === "email") {
// Email logic
} else if (type === "sms") {
// SMS logic
}
// Adding push notification requires modifying this method
}
}
Checklist:
Rule: Subtypes must be substitutable for base types
Application:
// ✅ Good - Maintains contract
export abstract class PaymentProcessor {
abstract process(amount: number): Promise<PaymentResult>;
}
export class StripePaymentProcessor extends PaymentProcessor {
async process(amount: number): Promise<PaymentResult> {
// Always returns PaymentResult, never throws unexpected errors
try {
const result = await this.stripe.charge(amount);
return { success: true, transactionId: result.id };
} catch (error) {
return { success: false, error: error.message };
}
}
}
// ❌ Bad - Breaks parent contract
export class PaypalPaymentProcessor extends PaymentProcessor {
async process(amount: number): Promise<PaymentResult> {
if (amount > 10000) {
throw new Error("Amount too high"); // Unexpected behavior!
}
// Different behavior than parent contract
}
}
Checklist:
Rule: Small, focused interfaces over large ones
Application:
// ✅ Good - Segregated interfaces
export interface Readable {
read(id: string): Promise<User | null>;
}
export interface Writable {
create(user: User): Promise<void>;
update(user: User): Promise<void>;
}
export interface Deletable {
delete(id: string): Promise<void>;
}
// Repositories implement only what they need
export class ReadOnlyUserRepository implements Readable {
async read(id: string): Promise<User | null> {
// Implementation
}
}
export class FullUserRepository implements Readable, Writable, Deletable {
// Implements all operations
}
// ❌ Bad - Fat interface
export interface UserRepository {
read(id: string): Promise<User | null>;
create(user: User): Promise<void>;
update(user: User): Promise<void>;
delete(id: string): Promise<void>;
archive(id: string): Promise<void>;
restore(id: string): Promise<void>;
// Forces all implementations to have all methods
}
Checklist:
Rule: Depend on abstractions, not concretions
Application:
// ✅ Good - Depends on abstraction
export interface UserRepository {
save(user: User): Promise<void>;
findById(id: string): Promise<User | null>;
}
export class CreateUserUseCase {
constructor(private userRepository: UserRepository) {}
async execute(data: CreateUserDto): Promise<User> {
const user = new User(data);
await this.userRepository.save(user);
return user;
}
}
// ❌ Bad - Depends on concrete implementation
export class CreateUserUseCase {
constructor(private postgresUserRepository: PostgresUserRepository) {}
async execute(data: CreateUserDto): Promise<User> {
// Tightly coupled to PostgreSQL implementation
const user = new User(data);
await this.postgresUserRepository.insertIntoPostgres(user);
return user;
}
}
Checklist:
Clean Code principles emphasize simplicity, readability, and avoiding over-engineering.
Rule: Simplicity is the ultimate sophistication
Application:
// ✅ Good - Simple and clear
export class PasswordValidator {
validate(password: string): boolean {
return (
password.length >= 8 && /[A-Z]/.test(password) && /[0-9]/.test(password)
);
}
}
// ❌ Bad - Over-engineered
export class PasswordValidator {
private rules: ValidationRule[] = [];
private ruleEngine: RuleEngine;
private strategyFactory: StrategyFactory;
private policyManager: PolicyManager;
validate(password: string): ValidationResult {
return this.ruleEngine
.withStrategy(this.strategyFactory.create("password"))
.withPolicy(this.policyManager.getDefault())
.applyRules(this.rules)
.execute(password);
}
}
When KISS applies:
Checklist:
Rule: Build only what you need right now
Application:
// ✅ Good - Build only what's needed NOW
export class UserService {
async createUser(dto: CreateUserDto): Promise<User> {
return this.repository.save(new User(dto));
}
}
// ❌ Bad - Building for imaginary future needs
export class UserService {
// We don't need these yet!
async createUser(dto: CreateUserDto): Promise<User> {}
async createUserBatch(dtos: CreateUserDto[]): Promise<User[]> {}
async createUserWithRetry(
dto: CreateUserDto,
maxRetries: number
): Promise<User> {}
async createUserAsync(dto: CreateUserDto): Promise<JobId> {}
async createUserWithCallback(
dto: CreateUserDto,
callback: Function
): Promise<void> {}
async createUserWithHooks(dto: CreateUserDto, hooks: Hooks): Promise<User> {}
}
When YAGNI applies:
Checklist:
Rule: Apply abstraction after seeing duplication 3 times (Rule of Three)
Application:
// ✅ Good - Meaningful abstraction after Rule of Three
export class DateFormatter {
formatToISO(date: Date): string {
return date.toISOString();
}
formatToDisplay(date: Date): string {
return date.toLocaleDateString("en-US");
}
formatToRelative(date: Date): string {
const now = new Date();
const diff = now.getTime() - date.getTime();
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
if (days === 0) return "Today";
if (days === 1) return "Yesterday";
return `${days} days ago`;
}
}
// Used in 3+ places
const isoDate = dateFormatter.formatToISO(user.createdAt);
// ❌ Bad - Premature abstraction
// Don't abstract after seeing duplication just ONCE
// Wait for the Rule of Three (3 occurrences)
// ❌ Bad - Wrong abstraction
export class StringHelper {
doSomething(str: string, num: number, bool: boolean): string {
// Forcing unrelated code into one function
}
}
When DRY applies:
When NOT to apply DRY:
Checklist:
Rule: Tell objects what to do, don't ask for data and make decisions
Application:
// ✅ Good - Tell the object what to do
export class User {
private _isActive: boolean = true;
private _failedLoginAttempts: number = 0;
deactivate(): void {
if (!this._isActive) {
throw new Error("User already inactive");
}
this._isActive = false;
this.logDeactivation();
}
recordFailedLogin(): void {
this._failedLoginAttempts++;
if (this._failedLoginAttempts >= 5) {
this.lock();
}
}
private lock(): void {
this._isActive = false;
this.logLockout();
}
private logDeactivation(): void {
console.log(`User ${this.id} deactivated`);
}
private logLockout(): void {
console.log(`User ${this.id} locked due to failed login attempts`);
}
}
// Usage - Tell it what to do
user.deactivate();
user.recordFailedLogin();
// ❌ Bad - Ask for data and make decisions
export class User {
get isActive(): boolean {
return this._isActive;
}
set isActive(value: boolean) {
this._isActive = value;
}
get failedLoginAttempts(): number {
return this._failedLoginAttempts;
}
set failedLoginAttempts(value: number) {
this._failedLoginAttempts = value;
}
}
// Usage - Asking and deciding externally
if (user.isActive) {
user.isActive = false;
console.log(`User ${user.id} deactivated`);
}
if (user.failedLoginAttempts >= 5) {
user.isActive = false;
console.log(`User ${user.id} locked`);
}
When TDA applies:
Benefits:
Checklist:
Target: < 20 lines per function
// ✅ Good - Small, focused functions
export class CreateUserUseCase {
async execute(dto: CreateUserDto): Promise<User> {
this.validateDto(dto);
const user = await this.createUser(dto);
await this.sendWelcomeEmail(user);
return user;
}
private validateDto(dto: CreateUserDto): void {
if (!this.isValidEmail(dto.email)) {
throw new ValidationError("Invalid email");
}
}
private async createUser(dto: CreateUserDto): Promise<User> {
const hashedPassword = await this.hasher.hash(dto.password);
return this.repository.save(new User(dto, hashedPassword));
}
private async sendWelcomeEmail(user: User): Promise<void> {
await this.emailService.send(
user.email,
"Welcome",
this.getWelcomeMessage(user.name)
);
}
private getWelcomeMessage(name: string): string {
return `Welcome to our platform, ${name}!`;
}
}
// ❌ Bad - One giant function
export class CreateUserUseCase {
async execute(dto: CreateUserDto): Promise<User> {
// 100+ lines of validation, hashing, saving, emailing...
// Hard to test, hard to read, hard to maintain
return User;
}
}
Guidelines:
// ❌ Bad - Comments explaining WHAT
export class UserService {
// Check if user is active and not deleted
async isValid(u: User): Promise<boolean> {
return u.a && !u.d;
}
}
// ✅ Good - Self-documenting code
export class UserService {
async isActiveAndNotDeleted(user: User): Promise<boolean> {
return user.isActive && !user.isDeleted;
}
}
// ✅ Comments explain WHY when needed
export class PaymentService {
async processPayment(amount: number): Promise<void> {
// Stripe requires amount in cents, not dollars
const amountInCents = amount * 100;
await this.stripe.charge(amountInCents);
}
}
Comment Guidelines:
For function and variable naming conventions, see naming-conventions skill
// ✅ Good - Same level of abstraction
async function processOrder(orderId: string): Promise<void> {
const order = await fetchOrder(orderId);
validateOrder(order);
await chargeCustomer(order);
await sendConfirmation(order);
}
// ❌ Bad - Mixed levels of abstraction
async function processOrder(orderId: string): Promise<void> {
const order = await db.query("SELECT * FROM orders WHERE id = ?", [orderId]);
if (!order.items || order.items.length === 0) {
throw new Error("Invalid order");
}
await chargeCustomer(order);
const html = "<html><body>Order confirmed</body></html>";
await emailService.send(order.customerEmail, html);
}
// ✅ Good - Early returns reduce nesting
function calculateDiscount(user: User, amount: number): number {
if (!user.isActive) {
return 0;
}
if (amount < 100) {
return 0;
}
if (user.isPremium) {
return amount * 0.2;
}
return amount * 0.1;
}
// ❌ Bad - Deep nesting
function calculateDiscount(user: User, amount: number): number {
let discount = 0;
if (user.isActive) {
if (amount >= 100) {
if (user.isPremium) {
discount = amount * 0.2;
} else {
discount = amount * 0.1;
}
}
}
return discount;
}
KISS vs DRY:
YAGNI vs Future-Proofing:
SOLID vs KISS:
TDA vs Simple Data Objects:
// ❌ Classes doing too much (violates SRP)
export class UserService {
validateUser() {}
hashPassword() {}
sendEmail() {}
saveToDatabase() {}
generateReport() {}
processPayment() {}
}
// ❌ Don't optimize before measuring
const cache = new Map<string, User>();
const lruCache = new LRUCache<string, User>(1000);
const bloomFilter = new BloomFilter();
// ✅ Start simple, optimize when needed
const users = await repository.findAll();
// ❌ Clever but unreadable
const result = arr.reduce((a, b) => a + (b.active ? 1 : 0), 0);
// ✅ Clear and boring
const activeCount = users.filter((user) => user.isActive).length;
// ❌ Magic numbers
if (user.age > 18 && order.amount < 1000) {
// ...
}
// ✅ Named constants
const MINIMUM_AGE = 18;
const MAXIMUM_ORDER_AMOUNT = 1000;
if (user.age > MINIMUM_AGE && order.amount < MAXIMUM_ORDER_AMOUNT) {
// ...
}
Before finalizing code, verify:
SOLID Principles:
Clean Code Principles:
Overall:
// SRP + DIP: Each class has one responsibility, depends on abstractions
export interface Logger {
log(message: string): void;
}
export interface UserRepository {
save(user: User): Promise<void>;
findByEmail(email: string): Promise<User | null>;
}
export interface PasswordHasher {
hash(password: string): Promise<string>;
}
export interface EmailSender {
send(to: string, subject: string, body: string): Promise<void>;
}
// OCP: Open for extension (new implementations)
export class ConsoleLogger implements Logger {
log(message: string): void {
console.log(message);
}
}
// ISP: Focused interfaces
// Each interface has a single, focused responsibility
// KISS: Simple, clear implementation
export class CreateUserUseCase {
constructor(
private userRepository: UserRepository,
private passwordHasher: PasswordHasher,
private logger: Logger,
private emailSender: EmailSender
) {}
// KISS + Small Functions: < 20 lines, single responsibility
async execute(data: CreateUserDto): Promise<User> {
this.logger.log("Creating new user");
// YAGNI: Only what's needed now
await this.validateEmail(data.email);
const user = await this.createUser(data);
await this.sendWelcomeEmail(user);
this.logger.log("User created successfully");
return user;
}
// DRY: Extracted after Rule of Three
private async validateEmail(email: string): Promise<void> {
const existing = await this.userRepository.findByEmail(email);
if (existing) {
throw new Error(`User with email ${email} already exists`);
}
}
private async createUser(data: CreateUserDto): Promise<User> {
const hashedPassword = await this.passwordHasher.hash(data.password);
const user = new User({ ...data, password: hashedPassword });
await this.userRepository.save(user);
return user;
}
private async sendWelcomeEmail(user: User): Promise<void> {
await this.emailSender.send(
user.email,
"Welcome",
this.getWelcomeMessage(user.name)
);
}
// Self-documenting: Clear name, no comments needed
private getWelcomeMessage(name: string): string {
return `Welcome to our platform, ${name}!`;
}
}
// LSP: Implementations are substitutable
export class BcryptPasswordHasher implements PasswordHasher {
async hash(password: string): Promise<string> {
return bcrypt.hash(password, 10);
}
}
export class ArgonPasswordHasher implements PasswordHasher {
async hash(password: string): Promise<string> {
return argon2.hash(password);
}
}
SOLID + Clean Architecture:
Clean Code + KISS:
Quality over dogma:
Communication over cleverness:
Pragmatism over perfection:
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.