agentleFS
robconeryrobconery/kolea/.claude/skills/design-principles/SKILL.md

design-principles

Skill7 starsChanged 3 months agoMITView on GitHub

General object-oriented design principles for TypeScript projects that are NOT part of SOLID or the Gang of Four patterns — coupling and cohesion (incl. connascence), DRY, YAGNI, KISS, separation of concerns, encapsulation / information hiding, Tell Don't Ask, Law of Demeter, composition over inheritance, program-to-an-interface, Command–Query Separation, Principle of Least Astonishment, and fail-fast. Use when designing or refactoring modules, reviewing code for coupling/duplication/leaky-abstraction smells, deciding whether an abstraction earns its keep, or answering "is this good design / which principle applies" questions that SOLID and GoF do not cover.

24 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use test-driven-development.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • OpenAISkill

    test-driven-development

    Use when implementing any feature or bugfix, before writing implementation code

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

1 rule. 0 are common, 1 is distinctive, and 0 are very specific to this project.

  • 0

    Common

    Most of the 21 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Connascence is the practical coupling metric: Prefer connascence of *name* (shared identifier) over connascence of *type*, and both over …

    line 96

“Common” is measured: kept by at least 6 in 10 of the 21 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Design Principles in TypeScript (beyond SOLID & GoF)

The design heuristics that predate and underpin SOLID: how to measure and reduce coupling, maximize cohesion, avoid premature/false abstraction, and keep objects in charge of their own data — adapted to idiomatic TypeScript.

🎯 Why: Design for Change

The goal of writing software is to be able to change it safely. Coupling is the cost of the next change; cohesion is its locality. Tell-Don't-Ask, Demeter, encapsulation, composition-over-inheritance — all of these exist to shrink the blast radius of an edit. DRY and YAGNI cut both ways: too little abstraction and a change ripples; too much and the seam is in the wrong place. Pick the abstraction that makes the next diff small.

Scope — how this differs from the other two skills

  • solid-principles owns SRP, OCP, LSP, ISP, DIP. This skill does not re-explain those. Where a principle here is adjacent (Separation of Concerns ≈ SRP at the architecture level; Program-to-an-Interface ≈ DIP/ISP), the reference file states the distinction and defers to solid-principles.
  • gof-patterns owns the 23 named patterns. This skill explains the forces (coupling, cohesion, Tell-Don't-Ask) that motivate those patterns but never re-documents a pattern. "Composition over inheritance" here is the general principle; the patterns that apply it (Strategy, Decorator) live in gof-patterns.

How to use this skill

  1. Match the symptom in the decision guide to a principle.
  2. Open references/principles.md for that principle's full before/after TypeScript example and its "when this is over-engineering" note.
  3. Apply the smallest change that removes real pain. These are heuristics that trade off against each other (DRY vs. coupling, YAGNI vs. extensibility) — resolve the conflict in favor of the code that is cheapest to change now.

Reference file

  • references/principles.md — all principles with intent, a smell, a bad example, a refactor, TypeScript-specific notes, and when not to apply.

The principles

PrincipleOne-line intent
Coupling (loose vs. tight)Minimize what one module must know about another; measure it with connascence.
CohesionGroup things that change together and are used together.
DRYEvery piece of knowledge has one authoritative representation.
YAGNIDon't build it until a real requirement demands it.
KISSPrefer the simplest design that works; complexity must be earned.
Separation of ConcernsDistinct concerns live in distinct, swappable layers/modules.
Encapsulation / Information HidingHide representation; expose intent through behavior.
Tell, Don't AskSend objects commands; don't pull their state out to decide for them.
Law of DemeterTalk only to immediate collaborators; don't navigate object graphs.
Composition over InheritanceAssemble behavior from parts; reserve inheritance for true substitutability.
Program to an InterfaceDepend on a role/contract, not a concrete class.
Command–Query SeparationA method either changes state or returns a value — not both.
Principle of Least AstonishmentBehavior should match the name and the caller's reasonable expectation.
Fail FastReject invalid state at the boundary, loudly, before it propagates.

Decision guide — symptom → principle

You notice…Apply
Changing module A forces edits in B, C, DReduce Coupling (find the connascence)
A file groups unrelated helpers used by different callersImprove Cohesion
The same business rule is encoded in three placesDRY
Two code paths look identical today but model different conceptsStop applying DRY — see false-DRY note
An abstraction/config exists "for the future" with one callerYAGNI
A clever generic/metaprogramming layer nobody can explainKISS
HTTP, business rules, and SQL interleaved in one functionSeparation of Concerns
Callers read an object's fields then mutate them backTell, Don't Ask / Encapsulation
a.getB().getC().doThing() train wrecksLaw of Demeter
A subclass inherits methods it must disableComposition over Inheritance
Business logic news a concrete client / imports an SDK directlyProgram to an Interface (then see solid-principles DIP)
A getter mutates, or a "save" returns the thing it savedCommand–Query Separation
A function named getUser also creates one as a side effectPrinciple of Least Astonishment
Invalid data is detected three layers deep, far from its sourceFail Fast

TypeScript-specific guidance

  • Connascence is the practical coupling metric. Prefer connascence of name (shared identifier) over connascence of type, and both over connascence of position (argument order) — replace positional params with an options object. Keep stronger forms (meaning, algorithm, timing) local to one module. See the Coupling section.
  • readonly, #private, and accessor methods enforce encapsulation. Use #field (true runtime privacy) over the private keyword when callers must not reach in; expose readonly views instead of mutable arrays/objects.
  • A narrow function type is an interface. "Program to an interface" rarely needs a class implements; type Notify = (e: Event) => void decouples just as well with less ceremony.
  • as const + discriminated unions make illegal states unrepresentable — the strongest form of Fail Fast (compile-time instead of runtime).
  • Tell-Don't-Ask conflicts with rich domain models exposed to React. UI often legitimately needs to ask (render state). Apply Tell-Don't-Ask to domain/service code; let view models be data bags.
  • DRY is about knowledge, not characters. Two functions with identical bodies that encode different decisions should stay separate; coupling them to save lines creates a change-amplifier. Rule of Three before extracting.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.