Dimma Front-End Design Patterns Framework

Apply SOLID, DRY, KISS, and YAGNY principles specifically adapted for front-end code so you write maintainable, AI-friendly components, hooks, and modules that are easy to change, test, and extend.

// TL;DR

The Dimma Front-End Design Patterns Framework is a structured approach that adapts SOLID, DRY, KISS, and YAGNI principles specifically for front-end development — components, hooks, functions, and TypeScript types. Use it whenever you write, review, or refactor front-end code and need to decide how to split responsibilities, reduce duplication, manage complexity, or structure architecture. It also provides precise pattern language for prompting AI to generate cleaner code and giving actionable code-review feedback. The framework distinguishes between fat clients and thin clients to calibrate how aggressively each principle should be applied.

// When should I use the Dimma Front-End Design Patterns Framework?

Use this skill whenever you are writing, reviewing, or refactoring front-end code — components, hooks, types, or architecture — and need to decide how to structure logic, split responsibilities, reduce duplication, or manage complexity. Also use it when prompting AI to generate front-end code or when giving code-review feedback.

// What inputs do I need before applying the Dimma Framework?

  • code_or_scenariorequired
    The specific component, hook, function, type, or architectural decision you want to evaluate or improve.
  • project_typerequired
    Is this a fat client (business logic on the client) or a thin client (mostly displays data)? This affects which principles are most relevant.
  • project_scale
    Small/solo project, growing team codebase, or large codebase with shared types and interfaces? Scale determines how aggressively to apply patterns like Interface Segregation and DRY.
  • ai_context
    Are you prompting AI to generate this code, reviewing AI-generated code, or writing it yourself? Changes how you apply pattern language as instructions.

// What are the core principles of the Dimma Front-End Design Patterns Framework?

Single Responsibility Principle (SRP)

One component, hook, or function should have only one reason to change. Split code by reason to change, not by type. High cohesion within a model, low coupling between models.

Open/Closed Principle (OCP)

Add new behavior by creating new code, not by modifying code that already works. Make base code closed for modification but open for extension via callbacks, composition, or new functions.

Liskov Substitution Principle (LSP)

If a component, class, or interface extends something, the extended version must be usable anywhere the original is used without breaking anything. Extending means adding capabilities on top, not replacing or changing the original contract.

Interface Segregation Principle (ISP)

Don't force a class, component, or type to depend on things it doesn't use. Split broad interfaces into focused ones so each model only implements what it actually needs.

Dependency Inversion Principle (DIP)

Higher-level modules (business logic) should not depend on lower-level modules (API calls, storage, UI). Business logic should depend on abstractions, and lower-level code can depend on those same abstractions. Fat clients make this very relevant.

DRY — Don't Repeat Yourself

Every piece of knowledge should have only one representation in the codebase — a single source of truth. DRY is about duplicated knowledge, not merely duplicated code. Tests should NOT be DRY; they must remain readable and self-contained.

KISS — Keep It Simple

Don't add complexity unless the problem requires it. Match the complexity of the solution to the complexity of the actual problem. Simpler is the default; add complexity only when you have a real reason.

YAGNY — You Aren't Going to Need It

Don't build features or abstractions until you actually need them. Code written for something you might need later has a cost: you write it, test it, and maintain it. If nobody uses it, it adds zero value. YAGNY is about not building something until you have the problem; KISS is about choosing a simpler solution for a problem you do have.

// How do you apply the Dimma Framework step by step?

  1. 1

    Identify the entity type you are working with

    Classify: is this a component, hook, function, TypeScript type/interface, class, or an architectural decision? The patterns apply at all levels but manifest differently. Also note: design patterns operate at the component/hook/logic level; architectural patterns (MVC, Feature Slice Design, Layered) operate at the system level — do not conflate them.

  2. 2

    Apply the Single Responsibility smell test

    Ask: how many reasons could this entity change? If more than one, list them. Common god-component/god-function signals: a hook that fetches data AND validates forms AND formats output. If multiple independent reasons exist, split into separate hooks/components/functions — one per reason. Check cohesion (do things that change together live together?) and coupling (can you remove one module without breaking another?).

  3. 3

    Check for Open/Closed violations on any entity that handles multiple variants

    Look for growing if/else or switch chains where each branch represents a fundamentally different scenario (not just a small variation). If adding a new variant requires editing the existing function, extract a base hook/function with shared logic, then pass variant-specific logic as a callback parameter. Ask: is this a completely different scenario requiring its own logic, or just a small variation? If just a variation, a simple if/else is fine — do not over-extract.

  4. 4

    Check LSP if any entity extends a base component, class, or interface

    Verify the extended version supports the same props, methods, and contract as the base. Symptom of violation: a subclass throws errors on methods it inherits, or a child component renames or removes props the parent defines. If a class cannot support all methods of the interface it extends, split the interface (which also fixes via ISP).

  5. 5

    Audit interfaces and shared types for Interface Segregation

    Look for: classes with placeholder/throw methods they don't really use, TypeScript types using Partial to paper over fields that never exist for a given entity, or one broad interface forced onto multiple unrelated consumers. Split interfaces so each class/component only implements what it actually needs. Most relevant in larger codebases with shared types; rarely needed in small projects.

  6. 6

    Trace dependency direction to enforce Dependency Inversion

    Map which layer each piece of code lives in: business logic (high-level, changes rarely) vs. API calls, storage, UI (low-level, changes often). If a business logic hook or function directly imports a fetch/API function, it is violating DIP. Fix by making business logic a pure function that receives data as parameters, and create a separate connector hook that fetches data and passes it in. For shared services, use Dependency Injection (pass via constructor/props) or Inversion of Control containers (React Context, Angular Injectable, Vue provide/inject). Only apply full abstraction layers in fat clients — thin clients don't need them.

  7. 7

    Apply the DRY single-source-of-truth test to any duplicated logic

    Ask: is this duplicated code the same knowledge (same rule, same domain logic) or just coincidentally similar code serving different purposes? If same knowledge: extract to a shared helper/hook. If coincidentally similar: leave it duplicated — duplication is fine when models need to stay independent (this is the wrong abstraction antipattern trap). Never apply DRY to tests — tests must be readable as standalone units; helpers in tests should only remove boilerplate, not hide test purpose.

  8. 8

    Apply KISS to every complexity decision

    At the code level: use useState before useReducer. At the library level: use a simple charting library before D3. At the architecture level: use Layered Architecture before Feature Slice Design. Ask: does the actual problem require this complexity right now? If not, use the simpler option. KISS does not mean always pick the simplest tool — it means match solution complexity to problem complexity.

  9. 9

    Apply YAGNY to any feature, abstraction, or configuration not yet required

    Ask: do we have an actual current use case for this? If no: do not build it. Applies to: generic components with many props for hypothetical cases, HTTP clients with interceptors/retry when a simple fetch works, multiple interface implementations when only one exists, configuration options for flexibility nobody asked for. Exception: if the roadmap explicitly plans for a use case next quarter, it is acceptable to prepare — do not hardcode strings if multi-language is coming. YAGNY is not an excuse to skip error handling, tests, or reasonable project structure.

  10. 10

    Translate pattern violations into precise, pattern-named feedback

    For code reviews or AI prompts, use the pattern names explicitly. Instead of 'this doesn't look right', say 'this violates the Single Responsibility Principle — split into two hooks'. Instead of 'write clean code', say 'keep this hook focused on one responsibility' with an example. Specific language produces faster alignment between humans and AI.

// What are real-world examples of the Dimma Framework in action?

A single hook handles form field state, email/password validation, and API submission all in one place. The team wants to add new validation rules but keeps breaking the submission logic.

Apply SRP: identify two distinct reasons to change — API contract changes vs. validation rule changes. Split into two hooks: one owns fetching/saving data (changes when API changes), one owns form values and validation (changes when form requirements change). Each hook has one responsibility and they don't interfere. This also improves testability — each hook needs a simpler test setup.

A shared shipping cost function has grown to five if/else branches — standard, express, international, same-day, and freight — and every new shipping type requires editing the same function.

Apply OCP: extract shared logic (e.g., weight calculation) into a base hook. Each shipping type gets its own cost function. Pass the cost function as a callback to the base hook. Adding a new shipping type now means writing a new cost function only — the base hook is never modified, eliminating regression risk.

Two different feature modules both contain nearly identical email validation regex and logic, copied independently. A bug fix in one copy was not applied to the other, causing inconsistent validation behavior.

Apply DRY: this is duplicated knowledge (the same email validation rule), not merely similar-looking code. Extract isValidEmail into a shared helper with a single source of truth. Fix the bug once; it is fixed everywhere. Write one set of tests for the helper. Note: if the two modules needed different validation rules for different business reasons, leaving them separate would be correct — that would be coincidental similarity, not shared knowledge.

A developer is building a date formatting helper and adds locale, timezone, custom format options, and a fallback string before any consumer has requested these features.

Apply YAGNY: no current use case exists for the extra parameters. Build only the simple formatDate(date) helper now. If locale support is later added to the roadmap, extend it then — based on real requirements. Code written for hypothetical needs requires writing, testing, and maintaining at zero current value.

An order service class creates its own logger instance internally using new ConsoleLogger(), making unit testing require a real logger and making it impossible to switch to a different logging service without editing the order service.

Apply DIP with Dependency Injection: define a Logger interface with log, error, and warn methods. Pass the logger into the order service from outside (constructor injection or React Context). OrderService now depends on the Logger abstraction, not ConsoleLogger directly. Switching to SentryLogger requires zero changes to OrderService as long as SentryLogger implements the same interface. Business logic is now testable with a mock logger.

// What are the common mistakes when applying front-end design patterns?

  • God component / god function antipattern: one hook or component that fetches data, validates forms, manages state, and formats output — it has too many reasons to change and is fragile to modify.
  • Wrong abstraction antipattern: merging code that looks similar into one shared function when the similarity is coincidental, not shared knowledge. The shared function then accumulates conditions for each consumer, becoming fragile and hard to maintain.
  • Applying DRY blindly across domain boundaries: two modules may have similar-looking code but need to stay independent. Extracting to a shared function creates tight coupling — duplication across domain boundaries is often correct.
  • Over-splitting with SRP: splitting a hook or component that does not actually have different reasons to change. Every new module has a cost — more files, more navigation, harder to trace data flow. Split only when there is a real different reason to change.
  • Making tests too DRY: hiding test setup behind multiple helper layers so the test's purpose is no longer readable. Tests are verified by reading, not by other tests — they must be self-explanatory.
  • Treating KISS and YAGNY as the same principle: KISS is about choosing a simpler solution for a problem you have; YAGNY is about not building something until you actually have the problem. They operate differently.
  • Using YAGNY as an excuse to skip error handling, tests, or reasonable project structure — those are good practices, not premature features.
  • Applying Dependency Inversion in thin clients (apps that mostly display data with no domain rules) — full abstraction layers and inversion of control containers add complexity without benefit in simple apps.
  • Over-applying Open/Closed by extracting every condition into a separate function even when it is just a small variation, not a fundamentally different scenario.
  • Confusing design patterns (code organization: components, hooks, functions) with architectural patterns (how parts of the app connect: MVC, Feature Slice Design, Layered Architecture) — they operate at different levels and should not be conflated.
  • Writing vague AI prompts like 'write clean code' or 'don't write bad code' instead of pattern-specific instructions like 'keep this hook focused on one responsibility' — vague instructions produce random results.

// What key terms do I need to know for the Dimma Framework?

Single Responsibility Principle (SRP)
One component, class, or function should have only one reason to change. Violations produce god components or god functions.
God Component / God Function
The antipattern where one entity handles too many unrelated responsibilities at once — fetching, validation, state management, and formatting all in one hook or component.
Open/Closed Principle (OCP)
Code should be open for extension (new behavior via new code) but closed for modification (existing working code is not changed). New variants are added as new functions/callbacks, not as new branches inside existing code.
Liskov Substitution Principle (LSP)
If type B extends type A, type B must be usable anywhere type A is expected without breaking anything. Extensions add capabilities; they do not replace or break the original contract.
Interface Segregation Principle (ISP)
Don't force a class or component to depend on things it doesn't use. Split broad interfaces into smaller, focused ones so each consumer only implements what it actually needs.
Dependency Inversion Principle (DIP)
Higher-level modules (business logic) should not depend on lower-level modules (API, storage, UI). Both should depend on abstractions (interfaces). Implemented via Dependency Injection or Inversion of Control containers.
Dependency Injection (DI)
One way to implement DIP: instead of a module creating its own dependencies, the dependencies are provided from the outside — via constructor parameters, props, or context.
Inversion of Control (IoC) Container
A central registry where services are registered and consumed without manual passing of dependencies. Examples: Angular's Injectable system, React Context API, Vue's provide/inject.
Fat Client
A front-end application with significant business logic on the client side. DIP and clean architecture principles are very relevant here.
Thin Client
A front-end application that mostly displays data with no domain rules on the client. Full abstraction layers and IoC containers are not needed here.
DRY — Don't Repeat Yourself
Every piece of knowledge should have only one representation in the codebase (single source of truth). About duplicated knowledge, not merely duplicated code.
Single Source of Truth
The DRY outcome: one shared function, type, or constant owns a piece of knowledge so that fixing or changing it propagates everywhere automatically.
Wrong Abstraction
The DRY antipattern: merging coincidentally similar code into one shared function when the code actually serves different purposes. The shared function then accumulates conditional branches for each consumer, becoming fragile.
KISS — Keep It Simple
Don't add complexity unless the problem requires it. Match solution complexity to problem complexity. Simpler is the default; add complexity only with a real reason.
YAGNY — You Aren't Going to Need It
Don't build features or abstractions until you actually need them. Code for hypothetical needs has a cost (write, test, maintain) with zero current value.
Cohesion
Whether things that belong together actually live together. High cohesion means all logic for one feature/responsibility is in one place. Low cohesion means jumping between multiple files to understand one feature.
Coupling
Whether separated modules are truly independent. Low coupling means removing module B doesn't break module A. High/tight coupling means module A can't function without importing from module B.
Vertical Slice Architecture
Organizing code by feature rather than by layer. Everything related to a feature (page, hook, utils, API) lives in one folder. A structural example of SRP and high cohesion at the project level.
High Cohesion / Low Coupling
The target state: things that change together live together (high cohesion within a model), and models don't depend on each other (low coupling between models).

// FREQUENTLY ASKED QUESTIONS

What is the Dimma Front-End Design Patterns Framework?

The Dimma Front-End Design Patterns Framework is a structured system that adapts SOLID, DRY, KISS, and YAGNI principles specifically for front-end code — components, hooks, functions, and TypeScript types. It provides a 10-step workflow to evaluate and improve any front-end entity, distinguishes between fat and thin clients to calibrate principle intensity, and gives you precise pattern vocabulary for code reviews and AI prompts.

What are the SOLID principles in front-end development?

In front-end development, SOLID means: Single Responsibility — one hook or component, one reason to change. Open/Closed — add new behavior via new code or callbacks, not by editing working code. Liskov Substitution — extended components must work anywhere the original is used. Interface Segregation — split broad TypeScript interfaces so consumers only implement what they need. Dependency Inversion — business logic depends on abstractions, not directly on API calls or storage.

How do I apply SOLID principles to React components and hooks?

Start by checking how many reasons a component or hook could change. If a hook fetches data, validates forms, and formats output, it violates SRP — split it into separate hooks by reason to change. For OCP, replace growing if/else chains with callbacks passed to a base hook. For DIP, make business logic hooks pure functions that receive data as parameters, with a separate connector hook handling API calls.

How do I use the Dimma Framework to prompt AI for better front-end code?

Use explicit pattern names in your prompts instead of vague instructions. Say 'keep this hook focused on one responsibility — it should only manage form validation, not API calls' instead of 'write clean code.' Reference specific principles like 'apply Open/Closed — pass the cost calculation as a callback so adding new shipping types doesn't modify the base hook.' Specific pattern language produces dramatically more consistent AI-generated code.

How does the Dimma Framework compare to general clean code guidelines?

General clean code guidelines are language-agnostic and often vague — 'write readable code,' 'use good names.' The Dimma Framework applies each SOLID, DRY, KISS, and YAGNI principle specifically to front-end entities: components, hooks, TypeScript types, and architecture. It also calibrates advice based on client type (fat vs. thin) and project scale, and provides a concrete 10-step workflow instead of abstract recommendations.

When should I use the Dimma Front-End Design Patterns Framework?

Use it whenever you write, review, or refactor front-end code and need to decide how to structure logic, split responsibilities, reduce duplication, or manage complexity. It applies to components, hooks, TypeScript types, and architectural decisions. Also use it when prompting AI to generate front-end code or when giving code-review feedback — the pattern vocabulary makes feedback precise and actionable.

What is the difference between KISS and YAGNI in front-end development?

KISS means choosing a simpler solution for a problem you already have — use useState before useReducer, use a simple chart library before D3. YAGNI means not building features or abstractions until you actually have the problem — don't add locale support to a date helper before anyone requests it. KISS operates on solution complexity; YAGNI operates on problem existence. They are complementary but distinct.

What results can I expect from applying the Dimma Framework to my codebase?

You can expect components and hooks that are easier to test because each has one responsibility. Fewer regressions when adding features because new behavior comes from new code, not editing existing code. Clearer code reviews because feedback uses precise pattern names. Better AI-generated code because prompts are specific. Reduced duplication of domain knowledge while avoiding the wrong abstraction trap. Overall, a codebase that is easier to change, extend, and maintain.

What is the wrong abstraction antipattern in front-end code?

The wrong abstraction happens when you merge coincidentally similar code into one shared function, thinking you're applying DRY. The code looks similar but serves different purposes across different domain boundaries. The shared function then accumulates conditional branches for each consumer, becoming fragile and harder to maintain than the original duplication. DRY applies to duplicated knowledge — the same business rule — not merely similar-looking code.

How do I know if my React hook violates the Single Responsibility Principle?

Ask how many independent reasons the hook could change. If it fetches data (changes when API contract changes), validates forms (changes when validation rules change), and formats output (changes when display requirements change), it has three reasons to change — a clear SRP violation. Split it into separate hooks, one per reason. A symptom is that changing one behavior frequently breaks unrelated behavior within the same hook.

// GET THIS SKILL — FREE

Use this skill in your AI

Every skill on SkillForge is free. Drop your email and copy this skill straight into Claude, ChatGPT, or any LLM.

We'll email you when new skills drop. Unsubscribe anytime.