@yaawn/workflow 0.0.0-stage → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +172 -2
  2. package/agents/instructions/output-styles/yaawn-eli5.instructions.md +45 -0
  3. package/agents/instructions/yaawn-accessibility.css.instructions.md +58 -0
  4. package/agents/instructions/yaawn-accessibility.html.instructions.md +90 -0
  5. package/agents/instructions/yaawn-accessibility.javascript.instructions.md +50 -0
  6. package/agents/instructions/yaawn-clean-code.typescript.instructions.md +74 -0
  7. package/agents/instructions/yaawn-function-coupling.typescript.instructions.md +100 -0
  8. package/agents/instructions/yaawn-html-css-js.html.instructions.md +32 -0
  9. package/agents/instructions/yaawn-open-source-first.instructions.md +38 -0
  10. package/agents/instructions/yaawn-personas.instructions.md +18 -0
  11. package/agents/instructions/yaawn-semantic-html.html.instructions.md +40 -0
  12. package/agents/instructions/yaawn-vanilla-css.css.instructions.md +50 -0
  13. package/agents/output-styles/yaawn-eli5.instructions.md +45 -0
  14. package/agents/personas/yaawn-developer.md +16 -0
  15. package/agents/personas/yaawn-product-owner.md +18 -0
  16. package/agents/personas/yaawn-reviewer.md +16 -0
  17. package/agents/skills/yaawn-clean-code/SKILL.md +61 -0
  18. package/agents/skills/yaawn-clean-code/references/classes.md +114 -0
  19. package/agents/skills/yaawn-clean-code/references/comments.md +164 -0
  20. package/agents/skills/yaawn-clean-code/references/error-handling.md +151 -0
  21. package/agents/skills/yaawn-clean-code/references/formatting.md +146 -0
  22. package/agents/skills/yaawn-clean-code/references/functions.md +261 -0
  23. package/agents/skills/yaawn-clean-code/references/meaningful-names.md +234 -0
  24. package/agents/skills/yaawn-clean-code/references/objects-and-data-structures.md +130 -0
  25. package/agents/skills/yaawn-clean-code/references/principles.md +76 -0
  26. package/agents/skills/yaawn-clean-code/references/simple-design-and-code-smells.md +130 -0
  27. package/agents/skills/yaawn-clean-code/references/unit-tests.md +114 -0
  28. package/agents/skills/yaawn-create-plan/SKILL.md +228 -0
  29. package/agents/skills/yaawn-grill-bug-report/SKILL.md +31 -0
  30. package/agents/skills/yaawn-grill-bug-report/references/bug-report-checklist.md +27 -0
  31. package/agents/skills/yaawn-grill-design/SKILL.md +68 -0
  32. package/agents/skills/yaawn-grill-design/references/design-checklist.md +36 -0
  33. package/agents/skills/yaawn-grill-other/SKILL.md +31 -0
  34. package/agents/skills/yaawn-grill-other/references/other-checklist.md +19 -0
  35. package/agents/skills/yaawn-grill-refactor/SKILL.md +31 -0
  36. package/agents/skills/yaawn-grill-refactor/references/refactor-checklist.md +26 -0
  37. package/agents/skills/yaawn-grill-spike/SKILL.md +31 -0
  38. package/agents/skills/yaawn-grill-spike/references/spike-checklist.md +25 -0
  39. package/agents/skills/yaawn-grill-story/SKILL.md +34 -0
  40. package/agents/skills/yaawn-grill-story/references/concept-challenge.md +67 -0
  41. package/agents/skills/yaawn-grill-story/references/story-checklist.md +43 -0
  42. package/agents/skills/yaawn-implement-plan/SKILL.md +196 -0
  43. package/agents/skills/yaawn-implement-plan/assets/fix-prompt.md +66 -0
  44. package/agents/skills/yaawn-implement-plan/assets/review-prompt.md +89 -0
  45. package/agents/skills/yaawn-implement-plan/assets/session-prompt.md +80 -0
  46. package/agents/skills/yaawn-implement-plan/scripts/agent.ts +218 -0
  47. package/agents/skills/yaawn-implement-plan/scripts/caffeinate.ts +56 -0
  48. package/agents/skills/yaawn-implement-plan/scripts/groups.ts +233 -0
  49. package/agents/skills/yaawn-implement-plan/scripts/overnight.ts +772 -0
  50. package/agents/skills/yaawn-implement-plan/scripts/parallel.ts +1176 -0
  51. package/agents/skills/yaawn-implement-plan/scripts/processes.ts +53 -0
  52. package/agents/skills/yaawn-implement-plan/scripts/stages.ts +65 -0
  53. package/agents/skills/yaawn-implement-plan/scripts/targets.ts +381 -0
  54. package/agents/skills/yaawn-implement-plan/scripts/worktrees.ts +431 -0
  55. package/agents/skills/yaawn-owasp-security/SKILL.md +106 -0
  56. package/agents/skills/yaawn-owasp-security/references/a01-broken-access-control.md +58 -0
  57. package/agents/skills/yaawn-owasp-security/references/a02-security-misconfiguration.md +56 -0
  58. package/agents/skills/yaawn-owasp-security/references/a03-software-supply-chain-failures.md +56 -0
  59. package/agents/skills/yaawn-owasp-security/references/a04-cryptographic-failures.md +67 -0
  60. package/agents/skills/yaawn-owasp-security/references/a05-injection.md +53 -0
  61. package/agents/skills/yaawn-owasp-security/references/a06-insecure-design.md +48 -0
  62. package/agents/skills/yaawn-owasp-security/references/a07-authentication-failures.md +68 -0
  63. package/agents/skills/yaawn-owasp-security/references/a08-software-data-integrity-failures.md +45 -0
  64. package/agents/skills/yaawn-owasp-security/references/a09-security-logging-alerting-failures.md +59 -0
  65. package/agents/skills/yaawn-owasp-security/references/a10-mishandling-exceptional-conditions.md +63 -0
  66. package/agents/skills/yaawn-prepare-plan/SKILL.md +110 -0
  67. package/agents/skills/yaawn-save-plan/SKILL.md +65 -0
  68. package/agents/skills/yaawn-save-plan/scripts/plan-body.ts +331 -0
  69. package/agents/skills/yaawn-save-plan/scripts/save-plan.ts +443 -0
  70. package/agents/skills/yaawn-split-story/SKILL.md +189 -0
  71. package/agents/skills/yaawn-split-story/references/spider.md +64 -0
  72. package/agents/skills/yaawn-split-story/scripts/create-issue.ts +162 -0
  73. package/agents/skills/yaawn-verify-executed-plan/SKILL.md +154 -0
  74. package/agents/skills/yaawn-verify-executed-plan/scripts/overnight.ts +362 -0
  75. package/agents/skills/yaawn-verify-executed-plan/scripts/pull-request.ts +131 -0
  76. package/agents/skills/yaawn-write-bug-report/SKILL.md +87 -0
  77. package/agents/skills/yaawn-write-other/SKILL.md +90 -0
  78. package/agents/skills/yaawn-write-refactor/SKILL.md +94 -0
  79. package/agents/skills/yaawn-write-spike/SKILL.md +94 -0
  80. package/agents/skills/yaawn-write-user-story/SKILL.md +187 -0
  81. package/agents/skills/yaawn-write-user-story/references/acceptance-criteria.md +186 -0
  82. package/agents/skills/yaawn-write-user-story/references/five-types-of-value.md +82 -0
  83. package/agents/skills/yaawn-write-user-story/references/user-story-fundamentals.md +75 -0
  84. package/agents/yaawn-glossary.md +146 -0
  85. package/agents/yaawn-grilling.md +75 -0
  86. package/agents/yaawn-worktrees.md +69 -0
  87. package/config/install-tasks.json +35 -0
  88. package/config/settings.json +23 -0
  89. package/config/wizard.json +128 -0
  90. package/github/ISSUE_TEMPLATE/yaawn-bug-report.md +59 -0
  91. package/github/ISSUE_TEMPLATE/yaawn-other.md +38 -0
  92. package/github/ISSUE_TEMPLATE/yaawn-refactor.md +38 -0
  93. package/github/ISSUE_TEMPLATE/yaawn-spike.md +16 -0
  94. package/github/ISSUE_TEMPLATE/yaawn-story.md +56 -0
  95. package/package.json +33 -4
  96. package/scripts/install.js +73 -0
  97. package/yaawn/gitignore +1 -0
  98. package/yaawn/yaawn-definition-of-done.md +5 -0
  99. package/yaawn/yaawn-project-glossary.md +7 -0
  100. package/yaawn/yaawn-user-roles.md +3 -0
  101. package/yaawn/yaawn-verification-commands.md +7 -0
@@ -0,0 +1,234 @@
1
+ # Meaningful Names
2
+
3
+ Rules for creating good names in code. Names are everywhere — variables, functions, arguments, classes, files, directories. Good names make code self-documenting.
4
+
5
+ > Source: Robert C. Martin, _Clean Code_ — Chapter 2: Meaningful Names
6
+
7
+ ## Rules
8
+
9
+ ### Use intention-revealing names
10
+
11
+ The name should tell you **why it exists**, **what it does**, and **how it is used**. If a name requires a comment, it does not reveal its intent.
12
+
13
+ ```typescript
14
+ // Bad
15
+ const d: number; // elapsed time in days
16
+
17
+ // Good
18
+ const elapsedTimeInDays: number;
19
+ ```
20
+
21
+ ```typescript
22
+ // Bad
23
+ const theList: User[];
24
+
25
+ // Good
26
+ const users: User[];
27
+ ```
28
+
29
+ ### Avoid disinformation
30
+
31
+ Do not leave false clues that obscure meaning. Avoid words whose established meanings differ from your intended meaning.
32
+
33
+ ```typescript
34
+ // Bad — "List" implies a specific data structure
35
+ const accountList: Account[]; // is it really a List?
36
+
37
+ // Good
38
+ const accounts: Account[];
39
+ ```
40
+
41
+ ### Avoid similar shapes
42
+
43
+ Names that look almost identical force readers to slow down and compare character-by-character.
44
+
45
+ ```typescript
46
+ // Bad
47
+ class ControllerForEfficientHandlingOfStrings {}
48
+ class ControllerForEfficientStorageOfStrings {}
49
+ ```
50
+
51
+ ### Make meaningful distinctions
52
+
53
+ If names must be different, they should mean something different.
54
+
55
+ ```typescript
56
+ // Bad — what's the difference?
57
+ interface Product {}
58
+ interface ProductInfo {}
59
+ interface ProductData {}
60
+
61
+ // Good — names convey different purposes
62
+ interface Product {}
63
+ interface ProductSummary {}
64
+ interface ProductDetails {}
65
+ ```
66
+
67
+ ### Use pronounceable names
68
+
69
+ If you can't pronounce it, you can't discuss it without sounding like an idiot.
70
+
71
+ ```typescript
72
+ // Bad
73
+ interface CstmrRcrd {}
74
+
75
+ // Good
76
+ interface CustomerRecord {}
77
+ ```
78
+
79
+ ### Use searchable names
80
+
81
+ Single-letter names and numeric constants are hard to locate across a codebase. The length of a name should correspond to the size of its scope.
82
+
83
+ ```typescript
84
+ // Bad
85
+ for (let j = 0; j < 34; j++) {
86
+ s += (t[j] * 4) / 5;
87
+ }
88
+
89
+ // Good
90
+ const realDaysPerIdealDay = 4;
91
+ const workDaysPerWeek = 5;
92
+ const numberOfTasks = 34;
93
+
94
+ for (let j = 0; j < numberOfTasks; j++) {
95
+ const realTaskDays = taskEstimates[j] * realDaysPerIdealDay;
96
+ const realTaskWeeks = realTaskDays / workDaysPerWeek;
97
+ sum += realTaskWeeks;
98
+ }
99
+ ```
100
+
101
+ ### Avoid encodings and member prefixes
102
+
103
+ Don't prefix member variables with `m_`, `_`, or Hungarian notation. Classes and functions should be small enough that prefixes are unnecessary.
104
+
105
+ ```typescript
106
+ // Bad
107
+ class Part {
108
+ private m_dsc: string; // The textual description
109
+ }
110
+
111
+ // Good
112
+ class Part {
113
+ private description: string;
114
+ }
115
+ ```
116
+
117
+ > **Note (project convention):** This codebase uses **camelCase** for all variables and constants — never snake_case or UPPER_SNAKE_CASE. See `AGENTS.md` for details.
118
+
119
+ ### Don't prefix interfaces with "I"
120
+
121
+ Let the user work with the interface without knowing it's an interface. If you must encode, encode the implementation.
122
+
123
+ ```typescript
124
+ // Bad
125
+ interface IShapeFactory {}
126
+
127
+ // Good
128
+ interface ShapeFactory {}
129
+ class ShapeFactoryImpl implements ShapeFactory {}
130
+ ```
131
+
132
+ ### Avoid mental mapping
133
+
134
+ Readers shouldn't have to mentally translate your names. Single-letter names are only acceptable for tiny loop counters (`i`, `j`, `k`).
135
+
136
+ ```typescript
137
+ // Bad
138
+ const l = getStudents();
139
+ for (const i of l) {
140
+ i.doSomething();
141
+ }
142
+
143
+ // Good
144
+ const students = getStudents();
145
+ for (const student of students) {
146
+ student.doSomething();
147
+ }
148
+ ```
149
+
150
+ ### Class names should be nouns
151
+
152
+ Classes and objects should have noun or noun phrase names. Avoid vague names like `Manager`, `Processor`, `Data`, `Info`, `Helper`, `Common`, `Utilities`.
153
+
154
+ ```typescript
155
+ // Bad — too vague, does more than one thing
156
+ class CommonUtils {}
157
+
158
+ // Good — specific purpose
159
+ class JsonUtils {}
160
+ class StringUtils {}
161
+ ```
162
+
163
+ ### Method names should be verbs
164
+
165
+ Methods should have verb or verb phrase names. Accessors, mutators, and predicates should be prefixed with `get`, `set`, and `is`/`has`.
166
+
167
+ ```typescript
168
+ // Good
169
+ postPayment();
170
+ deletePage();
171
+ save();
172
+ isActive();
173
+ hasPermission();
174
+ ```
175
+
176
+ ### Adjust name length to scope size
177
+
178
+ Short names for small scopes, longer descriptive names for larger scopes.
179
+
180
+ ```typescript
181
+ // Bad — acronym unclear outside this context
182
+ const wd = 5;
183
+
184
+ // Good
185
+ const workDaysPerWeek = 5;
186
+ ```
187
+
188
+ ### Don't use the same name for different purposes
189
+
190
+ The word `add` could mean concatenate strings, insert a record, or append to an array. Be specific.
191
+
192
+ ```typescript
193
+ // Bad
194
+ function add(value: string): void; // which kind of "add"?
195
+
196
+ // Good
197
+ function concatenate(value: string): string;
198
+ function insert(value: string): void;
199
+ function append(value: string): void;
200
+ ```
201
+
202
+ ### Add meaningful context, but don't add gratuitous context
203
+
204
+ Use context to disambiguate names, but don't add more context than is necessary.
205
+
206
+ ```typescript
207
+ // Bad — what does "state" mean?
208
+ let state: string;
209
+
210
+ // Good — context disambiguates
211
+ let addressState: string;
212
+ ```
213
+
214
+ ```typescript
215
+ // Bad — package already provides context
216
+ // in module: obnk
217
+ class ObnkConsentEntity {}
218
+
219
+ // Good
220
+ class ConsentEntity {}
221
+ ```
222
+
223
+ ### Use solution domain names
224
+
225
+ Use CS terms, algorithm names, pattern names freely — your readers are programmers.
226
+
227
+ ```typescript
228
+ // Good — recognizable to developers familiar with the Visitor pattern
229
+ class AccountVisitor {}
230
+ ```
231
+
232
+ ### Use problem domain names
233
+
234
+ When there is no CS term, use names from the problem domain so domain experts can verify the code.
@@ -0,0 +1,130 @@
1
+ # Objects and Data Structures
2
+
3
+ > Source: Robert C. Martin, _Clean Code_ — Chapter 6: Objects and Data Structures
4
+
5
+ ## Rules
6
+
7
+ ### Data abstraction
8
+
9
+ Don't expose the details of your data. Express data in abstract terms. Hiding implementation is about **abstractions**, not just putting a layer of getters/setters between the variables.
10
+
11
+ ```typescript
12
+ // Bad — exposes raw data
13
+ class Point {
14
+ x: number;
15
+ y: number;
16
+ }
17
+
18
+ // Good — abstracts the implementation
19
+ class Point {
20
+ private x: number;
21
+ private y: number;
22
+
23
+ getX(): number {
24
+ return this.x;
25
+ }
26
+ getY(): number {
27
+ return this.y;
28
+ }
29
+
30
+ setCartesian(x: number, y: number): void {
31
+ this.x = x;
32
+ this.y = y;
33
+ }
34
+ }
35
+ ```
36
+
37
+ In TypeScript, prefer **interfaces** and **readonly** properties for data that doesn't need encapsulation. Use classes with private state only when behavior is attached.
38
+
39
+ ### Data/Object anti-symmetry
40
+
41
+ This is a fundamental trade-off:
42
+
43
+ | | Adding new functions | Adding new data types |
44
+ | ---------------------------------------------- | ------------------------------------ | ------------------------------------------- |
45
+ | **Data structures** (expose data, no behavior) | Easy — add function, no data changes | Hard — all functions must change |
46
+ | **Objects** (hide data, expose behavior) | Hard — all classes must change | Easy — add new class implementing interface |
47
+
48
+ Choose based on what's more likely to change:
49
+
50
+ - **Expect new data types?** Use objects/interfaces (polymorphism)
51
+ - **Expect new functions?** Use data structures
52
+
53
+ ### Avoid hybrids
54
+
55
+ Hybrid structures that are half object and half data structure have the **worst of both worlds** — hard to add new functions AND hard to add new data types.
56
+
57
+ ```typescript
58
+ // Bad — hybrid: has both behavior and instanceof checks
59
+ abstract class Shape {
60
+ draw(): void {
61
+ if (this instanceof Square) {
62
+ /* ... */
63
+ } else if (this instanceof Rectangle) {
64
+ /* ... */
65
+ }
66
+ }
67
+ }
68
+
69
+ // Good — pure object with polymorphism
70
+ interface Shape {
71
+ draw(): void;
72
+ }
73
+
74
+ class Square implements Shape {
75
+ draw(): void {
76
+ /* square-specific drawing */
77
+ }
78
+ }
79
+
80
+ class Rectangle implements Shape {
81
+ draw(): void {
82
+ /* rectangle-specific drawing */
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### The Law of Demeter
88
+
89
+ A method should only call methods on:
90
+
91
+ 1. Its own object (`this`)
92
+ 2. Objects passed as parameters
93
+ 3. Objects it creates
94
+ 4. Its direct component objects
95
+
96
+ **Don't chain calls through collaborators** (train wrecks):
97
+
98
+ ```typescript
99
+ // Bad — reaches deep into collaborator structure
100
+ const outputDir = context.getOptions().getScratchDir().getAbsolutePath();
101
+
102
+ // Good — ask, don't reach
103
+ const outputDir = context.getOutputDirectory();
104
+ ```
105
+
106
+ ### Data Transfer Objects (DTOs)
107
+
108
+ A DTO is a class with public data and no behavior — used for transferring data between layers or processes.
109
+
110
+ Use DTOs to separate the data that crosses boundaries from your internal domain model.
111
+
112
+ ```typescript
113
+ // Bad — using the domain entity directly as input
114
+ function createUser(firstName: string, lastName: string, email: string, birthday: Date, role: Role): void {}
115
+
116
+ // Good — DTO wraps the input data
117
+ interface CreateUserDto {
118
+ firstName: string;
119
+ lastName: string;
120
+ email: string;
121
+ birthday: Date;
122
+ role: Role;
123
+ }
124
+
125
+ function createUser(dto: CreateUserDto): void {
126
+ const user = mapToUser(dto);
127
+ user.createdAt = new Date();
128
+ userRepository.save(user);
129
+ }
130
+ ```
@@ -0,0 +1,76 @@
1
+ # Principles
2
+
3
+ Core software design principles referenced throughout Clean Code.
4
+
5
+ > Source: Robert C. Martin, _Clean Code_ + [gpcoder gitbook bonus chapters](https://gpcoder.gitbook.io/clean-code/bonus)
6
+
7
+ ## SOLID
8
+
9
+ ### SRP — Single Responsibility Principle
10
+
11
+ A class should have **one, and only one, reason to change**. If you can think of more than one motive for changing a class, it has more than one responsibility.
12
+
13
+ ### OCP — Open-Closed Principle
14
+
15
+ Software entities should be **open for extension** but **closed for modification**. Add new behavior by adding new code, not changing existing code. Use interfaces, abstract classes, and polymorphism.
16
+
17
+ ### LSP — Liskov Substitution Principle
18
+
19
+ Objects of a superclass should be replaceable with objects of its subclasses **without breaking** the application. Subclasses must honor the contract of their parent.
20
+
21
+ ### ISP — Interface Segregation Principle
22
+
23
+ No client should be forced to depend on methods it does not use. Prefer **many small, specific interfaces** over one large general-purpose interface.
24
+
25
+ ### DIP — Dependency Inversion Principle
26
+
27
+ High-level modules should not depend on low-level modules. Both should depend on **abstractions**. Abstractions should not depend on details — details should depend on abstractions.
28
+
29
+ Use dependency injection to provide implementations of abstract services.
30
+
31
+ ---
32
+
33
+ ## KISS — Keep It Simple Stupid
34
+
35
+ Simplicity should be a key goal in design. Unnecessary complexity should be avoided. Ask: "Can this be written in a simpler way?"
36
+
37
+ ## DRY — Don't Repeat Yourself
38
+
39
+ Each piece of knowledge must have a **single, unambiguous, authoritative representation** in a system. When DRY is applied successfully, changing one thing doesn't require changes in unrelated places.
40
+
41
+ ## YAGNI — You Aren't Gonna Need It
42
+
43
+ Don't add functionality unless it's **actually needed now**. Never implement things when you just foresee that you might need them.
44
+
45
+ ## Boy Scout Rule
46
+
47
+ > Always leave the code better than you found it.
48
+
49
+ If you touch a file, leave it a little cleaner than you found it — better names, removed dead code, simplified logic. Over time, the codebase improves.
50
+
51
+ ## Don't Reinvent the Wheel
52
+
53
+ Check if a well-tested library or framework already solves the problem before writing a custom solution.
54
+
55
+ ## Design Patterns
56
+
57
+ Use established design patterns (Strategy, Observer, Factory, Decorator, etc.) when they simplify the design. Don't force patterns where they don't fit.
58
+
59
+ ## Coding Conventions
60
+
61
+ Follow the team's established conventions consistently. Inconsistent style makes code appear to be written by disagreeing individuals and increases cognitive load.
62
+
63
+ > **Project convention:** This codebase follows the conventions defined in `AGENTS.md`, `.editorconfig`, and `eslint.config.mjs`.
64
+
65
+ ## Fail Fast
66
+
67
+ Detect and report problems as early as possible. Validate inputs at system boundaries. A clear, immediate failure is better than a silent, delayed corruption.
68
+
69
+ ## Law of Demeter (LoD)
70
+
71
+ A method should only talk to its immediate friends — not to strangers reached through a chain of calls. See [objects-and-data-structures.md](./objects-and-data-structures.md) for details.
72
+
73
+ ## Coupling and Cohesion
74
+
75
+ - **Low coupling**: Modules should have minimal dependencies on each other. Changes in one module shouldn't require changes in others.
76
+ - **High cohesion**: A module's internal elements should be strongly related. If a class has methods that don't use the same instance variables, it may need to be split.
@@ -0,0 +1,130 @@
1
+ # Simple Design Rules and Code Smells
2
+
3
+ > Source: Robert C. Martin, _Clean Code_ — Chapters 12 (Emergent Design) and 17 (Smells and Heuristics)
4
+
5
+ ## Four Rules of Simple Design
6
+
7
+ A design is "simple" if it follows these rules in priority order:
8
+
9
+ ### Rule 1: Runs all the tests
10
+
11
+ A system that cannot be verified should never be deployed. Writing tests drives better design:
12
+
13
+ - Classes conform to SRP (easier to test)
14
+ - Tight coupling is eliminated (hard to test → fix the design)
15
+ - Tests enable safe refactoring
16
+
17
+ ### Rule 2: No duplication
18
+
19
+ Duplication is the primary enemy of a well-designed system. It represents additional work, risk, and complexity. Forms of duplication:
20
+
21
+ - Lines of code that look exactly the same
22
+ - Lines of code that are similar (same structure, different details)
23
+ - Duplication of implementation (same concept expressed differently)
24
+
25
+ Use the DRY principle, Template Method pattern, or Strategy pattern to eliminate duplication.
26
+
27
+ ### Rule 3: Ensure expressiveness
28
+
29
+ Code should clearly express the intent of its author. Spend time choosing better names, splitting large functions, and using patterns. Well-written tests also serve as documentation.
30
+
31
+ > The most important way to be expressive is to **try**. Don't just get code working and move on — make it easy for the next reader.
32
+
33
+ ### Rule 4: Minimal classes and methods
34
+
35
+ Keep function and class counts low. But this is the **lowest priority** rule — don't sacrifice tests, duplication removal, or expressiveness to minimize count.
36
+
37
+ ---
38
+
39
+ ## Code Smells
40
+
41
+ Indicators that something may be wrong in the code:
42
+
43
+ | Smell | Description |
44
+ | ----------------------- | -------------------------------------------------------------------------------------- |
45
+ | **Rigidity** | Software is difficult to change; a small change causes a cascade of subsequent changes |
46
+ | **Fragility** | Software breaks in many places due to a single change |
47
+ | **Immobility** | Cannot reuse parts of the code in other contexts |
48
+ | **Needless complexity** | Over-engineering, speculative generality |
49
+ | **Needless repetition** | Two code fragments look almost identical |
50
+ | **Opacity** | Code is hard to understand |
51
+
52
+ ### Comment smells
53
+
54
+ - Inappropriate information (author, dates — belongs in source control)
55
+ - Obsolete comments
56
+ - Redundant comments
57
+ - Poorly written comments
58
+ - Commented-out code
59
+
60
+ ### Function smells
61
+
62
+ - **Long function**: More than ~15 lines — start asking questions
63
+ - **Too many arguments**: More than 3 parameters
64
+ - **Output arguments**: Readers expect arguments to be inputs
65
+ - **Flag arguments**: Boolean that selects behavior → split the function
66
+ - **Selector arguments**: Any type used to switch behavior (enum, int, etc.)
67
+ - **Dead function**: Methods that are never called — delete them
68
+ - **Feature envy**: A method that accesses another object's data more than its own
69
+
70
+ ### Class smells
71
+
72
+ - **Large class**: Too many fields, methods, or lines
73
+ - **God class**: Concentrates too many responsibilities
74
+ - **Alternative classes with different interfaces**: Duplicate functionality, different API
75
+ - **Refused bequest**: Subclass doesn't use inherited methods → hierarchy is wrong
76
+ - **Change preventers**: Changing one class requires changes in many unrelated classes
77
+ - **Speculative generality**: Code created "just in case" for features never implemented
78
+ - **Dead code**: Variables, parameters, methods, or classes no longer used
79
+
80
+ ### General smells
81
+
82
+ - **Multiple languages in one source file**
83
+ - **Obvious behavior not implemented** (Principle of Least Surprise)
84
+ - **Incorrect behavior at boundaries** — test every boundary condition
85
+ - **Duplication** — the DRY violation, in all its forms
86
+ - **Code at wrong abstraction level** — don't mix high-level and low-level concepts
87
+ - **Base classes depending on derivatives**
88
+ - **Too much information** — keep interfaces tight and small
89
+ - **Dead code** — code that isn't executed
90
+ - **Vertical separation** — define variables and functions near where they are used
91
+ - **Inconsistency** — if you do something one way, do all similar things the same way
92
+ - **Clutter** — unused variables, uncalled functions, uninformative comments
93
+ - **Artificial coupling** — a variable/function placed in a convenient but wrong location
94
+ - **Feature envy** — a method uses another class's data more than its own
95
+ - **Inappropriate static** — prefer instance methods that can be polymorphic
96
+ - **Use explanatory variables** — break complex expressions into named intermediates
97
+ - **Function names should say what they do**
98
+ - **Understand the algorithm** — don't just make it pass tests, understand **why** it works
99
+ - **Prefer polymorphism to if/else or switch/case**
100
+ - **Follow standard conventions**
101
+ - **Replace magic numbers with named constants**
102
+ - **Be precise** — make decisions deliberately, handle exceptions
103
+ - **Structure over convention** — enforce design with types, not naming rules
104
+ - **Encapsulate conditionals** — extract boolean expressions into named functions
105
+ - **Avoid negative conditionals**
106
+ - **Functions should do one thing**
107
+ - **Hidden temporal couplings** — make call ordering explicit through function signatures
108
+ - **Encapsulate boundary conditions** — handle edge cases in one place
109
+ - **Functions should descend one level of abstraction**
110
+ - **Keep configurable data at high levels** — don't bury config in low-level functions
111
+ - **Avoid transitive navigation** — Law of Demeter: don't reach through collaborators
112
+
113
+ ### Name smells
114
+
115
+ - Choose descriptive names at the appropriate abstraction level
116
+ - Use standard nomenclature (e.g., `Decorator`, `Factory`, `Observer`)
117
+ - Use unambiguous names
118
+ - Use long names for long scopes
119
+ - Avoid encodings (Hungarian notation, prefixes)
120
+ - Names should describe side effects
121
+
122
+ ### Test smells
123
+
124
+ - **Insufficient tests** — test everything that could break
125
+ - **Don't skip trivial tests** — their documentary value exceeds their cost
126
+ - **Ignored tests are questions about ambiguity**
127
+ - **Test boundary conditions**
128
+ - **Exhaustively test near bugs** — bugs congregate
129
+ - **Patterns of failure are revealing** — complete, well-ordered tests expose patterns
130
+ - **Tests should be fast**
@@ -0,0 +1,114 @@
1
+ # Unit Tests
2
+
3
+ Test code should be maintained to the same standards of quality as production code.
4
+
5
+ > Source: Robert C. Martin, _Clean Code_ — Chapter 9: Unit Tests
6
+
7
+ ## Rules
8
+
9
+ ### Keep tests clean
10
+
11
+ Dirty tests are worse than no tests. If tests are hard to maintain, developers stop maintaining them. When tests rot, production code rots with it.
12
+
13
+ ### Focus on readability
14
+
15
+ Readability is **even more important** in tests than in production code. Say a lot with as few expressions as possible. Extract helpers for setup, execution, and verification.
16
+
17
+ ```typescript
18
+ // Bad — setup, execution, and assertion all mixed together
19
+ it('should save a user', () => {
20
+ const jsonFile = 'path/to/file/user-dto.json';
21
+ const dto = JSON.parse(readFileSync(jsonFile, 'utf-8'));
22
+ const actual = userRepository.save(dto);
23
+ const expectedFile = 'path/to/file/user.json';
24
+ const expected = JSON.parse(readFileSync(expectedFile, 'utf-8'));
25
+ expect(actual).not.toBeNull();
26
+ expect(actual.id).not.toBeNull();
27
+ expect(actual.email).toBe(expected.email);
28
+ });
29
+
30
+ // Good — clear AAA structure with helpers
31
+ it('should save user successfully', () => {
32
+ // Arrange
33
+ const dto = createUserDto();
34
+
35
+ // Act
36
+ const actual = userRepository.save(dto);
37
+
38
+ // Assert
39
+ const expected = createExpectedUser();
40
+ verifySavedUser(expected, actual);
41
+ });
42
+ ```
43
+
44
+ ### One assert per test
45
+
46
+ Each test should verify **one specific behavior**. Multiple asserts testing different behaviors in one test hide failures and make diagnosis harder.
47
+
48
+ ### Use a single concept per test
49
+
50
+ Don't combine multiple scenarios in one test function. Split boundary conditions, happy paths, and error cases into separate tests.
51
+
52
+ ```typescript
53
+ // Bad — tests both in-range and out-of-range in one test
54
+ it('should check range', () => {
55
+ expect(isInRange(9)).toBe(false);
56
+ expect(isInRange(10)).toBe(true);
57
+ expect(isInRange(100)).toBe(true);
58
+ expect(isInRange(101)).toBe(false);
59
+ });
60
+
61
+ // Good — separate tests per concept
62
+ it('should return true for values in range', () => {
63
+ expect(isInRange(10)).toBe(true);
64
+ expect(isInRange(50)).toBe(true);
65
+ expect(isInRange(100)).toBe(true);
66
+ });
67
+
68
+ it('should return false for values out of range', () => {
69
+ expect(isInRange(9)).toBe(false);
70
+ expect(isInRange(101)).toBe(false);
71
+ });
72
+ ```
73
+
74
+ ### Use the given-when-then / AAA convention
75
+
76
+ Structure every test with three phases:
77
+
78
+ 1. **Arrange** (Given): Set up the test data and preconditions
79
+ 2. **Act** (When): Execute the behavior under test
80
+ 3. **Assert** (Then): Verify the expected outcome
81
+
82
+ Use descriptive test names that communicate the scenario:
83
+
84
+ ```typescript
85
+ // Good naming patterns
86
+ it('should return empty array when no users exist', () => {});
87
+ it('should throw AuthError when password is invalid', () => {});
88
+ ```
89
+
90
+ ### Follow the F.I.R.S.T. principles
91
+
92
+ | Principle | Rule |
93
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
94
+ | **Fast** | Tests should run quickly. Slow tests won't be run frequently. If you don't run them, you won't find problems early enough. |
95
+ | **Independent** | Tests should not depend on each other. No test should set up conditions for the next test. Run in any order. |
96
+ | **Repeatable** | Tests should work in any environment — CI, local, offline. No external dependencies that make tests flaky. |
97
+ | **Self-Validating** | Tests have a boolean output: pass or fail. No manual inspection of log files to determine success. |
98
+ | **Timely** | Write tests **before** (TDD) or **alongside** the production code. Writing tests after makes production code harder to test. |
99
+
100
+ ### Test boundary conditions
101
+
102
+ Take special care to test boundaries. We often get the middle of an algorithm right but misjudge the edges.
103
+
104
+ ### Exhaustively test near bugs
105
+
106
+ Bugs tend to congregate. When you find a bug in a function, do an exhaustive test of that function. The bug was probably not alone.
107
+
108
+ ### Tests should be fast
109
+
110
+ If tests are slow, they won't be run often. Long feedback loops lead to code rot.
111
+
112
+ ### Use a coverage tool
113
+
114
+ Find modules, classes, and functions that are insufficiently tested. Don't skip trivial tests — their documentary value exceeds their cost.