Added AI skills
This commit is contained in:
9
.agents/skills/typescript-clean-code/LICENSE
Normal file
9
.agents/skills/typescript-clean-code/LICENSE
Normal file
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright 2025 BMAD Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
134
.agents/skills/typescript-clean-code/SKILL.md
Normal file
134
.agents/skills/typescript-clean-code/SKILL.md
Normal file
@@ -0,0 +1,134 @@
|
||||
---
|
||||
name: typescript-clean-code
|
||||
description: |
|
||||
Clean Code principles, professional practices, and workflows for TypeScript developers. Based on Robert C. Martin's "Clean Code" and "The Clean Coder" books.
|
||||
|
||||
IMPORTANT: When this skill is active, always load and consult the reference files (rules.md, examples.md) before giving advice or writing code. Reference content takes precedence over general knowledge.
|
||||
|
||||
Use this skill when:
|
||||
- Writing TypeScript/JavaScript code
|
||||
- Reviewing code or pull requests
|
||||
- Refactoring existing code
|
||||
- Following test-driven development (TDD)
|
||||
- Fixing bugs with proper test coverage
|
||||
- Planning test strategy for features
|
||||
- Estimating tasks accurately
|
||||
- Handling deadlines and commitments professionally
|
||||
- Working effectively with teams
|
||||
---
|
||||
|
||||
# Clean Code
|
||||
|
||||
Principles, practices, and workflows for TypeScript developers.
|
||||
|
||||
## Critical: Reference-First Approach
|
||||
|
||||
**Always load and consult the reference files before applying any principle or making any recommendation.** The references in this skill contain curated, authoritative knowledge from Robert C. Martin's books, adapted for TypeScript. When this skill is active:
|
||||
|
||||
1. **Read references before responding** - For any code quality or professional practice topic, load the relevant `rules.md` and `examples.md` files from `references/` before giving advice or writing code. Do not rely on general knowledge alone.
|
||||
2. **Reference content overrides internal knowledge** - If your general knowledge conflicts with what the reference files state, follow the reference files. They contain the specific rules, thresholds, and patterns this skill enforces.
|
||||
3. **Cite specific rules** - When making recommendations, reference the specific rule (e.g., "per `references/functions/rules.md` Rule 1: Keep Functions Small, 2-5 lines ideal") so the user can trace the guidance back to its source.
|
||||
4. **Use examples from reference files** - Prefer the bad/good code examples in `references/[topic]/examples.md` over generating your own. These examples are curated for TypeScript and demonstrate the exact patterns intended.
|
||||
5. **Follow workflows step-by-step** - When executing a task (review, refactoring, TDD, etc.), load the corresponding workflow file and follow each step, loading the reference files each step points to.
|
||||
|
||||
**Do not skip loading references.** Even if you "know" Clean Code principles, the reference files contain specific TypeScript adaptations, thresholds, checklists, and smell catalogs that your general knowledge may not match exactly.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **For a task**: Check `guidelines.md` → find the right workflow → load it → follow each step (loading referenced files)
|
||||
2. **For reference**: Load the specific `rules.md` and `examples.md` files relevant to your work → apply them
|
||||
3. **Follow the workflow**: Step-by-step process for consistent results — always load the files each step references
|
||||
|
||||
## Workflows
|
||||
|
||||
Step-by-step processes for common tasks:
|
||||
|
||||
| Workflow | When to Use |
|
||||
|----------|-------------|
|
||||
| `workflows/code-review/workflow.md` | Reviewing code for quality |
|
||||
| `workflows/pr-review/workflow.md` | Reviewing pull requests |
|
||||
| `workflows/tdd.md` | Test-driven development cycle |
|
||||
| `workflows/refactoring/workflow.md` | Safe refactoring with tests |
|
||||
| `workflows/new-feature.md` | Building new functionality |
|
||||
| `workflows/bug-fix.md` | Fixing bugs properly |
|
||||
| `workflows/test-strategy.md` | Planning test coverage |
|
||||
| `workflows/estimation.md` | Estimating tasks (PERT) |
|
||||
| `workflows/deadline-negotiation.md` | Handling unrealistic deadlines |
|
||||
|
||||
### Step-File Architecture (Code Review, PR Review, Refactoring)
|
||||
|
||||
The code review, PR review, and refactoring workflows use a **step-file architecture** for context-safe execution:
|
||||
|
||||
- Each workflow has a `workflow.md` entry point that describes steps and loads `steps/step-01-init.md`
|
||||
- Each step is a separate file in `steps/`, loaded sequentially
|
||||
- Progress is tracked via `stepsCompleted` array in the output document's YAML frontmatter
|
||||
- If context is compacted mid-workflow, `step-01-init.md` detects the existing output and `step-01b-continue.md` resumes from the last completed step
|
||||
- Each step loads specific reference files before analysis and cites rules in findings
|
||||
- The refactoring workflow includes a loop (steps 4-7) for iterative change-test-commit cycles
|
||||
|
||||
## Reference Categories
|
||||
|
||||
### Part 1: Code Quality (Clean Code book)
|
||||
|
||||
| Category | Files | Purpose |
|
||||
|----------|-------|---------|
|
||||
| naming | 3 | Variable, function, class naming |
|
||||
| functions | 4 | Function design and review |
|
||||
| classes | 3 | Class/module design |
|
||||
| comments | 3 | Comment best practices |
|
||||
| error-handling | 3 | Exception handling |
|
||||
| unit-tests | 3 | Clean test principles |
|
||||
| formatting | 3 | Code layout |
|
||||
| smells | 3 | Code smell catalog (50+) |
|
||||
|
||||
### Part 2: Professional Practices (Clean Coder book)
|
||||
|
||||
| Category | Files | Purpose |
|
||||
|----------|-------|---------|
|
||||
| professionalism | 3 | Professional ethics |
|
||||
| saying-no | 3 | Declining requests |
|
||||
| commitment | 3 | Making promises |
|
||||
| coding-practices | 3 | Daily habits, flow, debugging |
|
||||
| tdd | 3 | TDD workflow and benefits |
|
||||
| practicing | 3 | Deliberate practice |
|
||||
| acceptance-testing | 3 | Requirements as tests |
|
||||
| testing-strategies | 3 | Test pyramid |
|
||||
| time-management | 3 | Meetings, focus |
|
||||
| estimation | 3 | PERT estimation |
|
||||
| pressure | 3 | Working under pressure |
|
||||
| collaboration | 3 | Working with teams |
|
||||
|
||||
## Key Principles (Summary Only — Always Load Full References)
|
||||
|
||||
These are abbreviated reminders. **Always load the corresponding reference files for the full rules, thresholds, and examples before applying.**
|
||||
|
||||
### Code Quality
|
||||
1. **Readability** → `references/formatting/rules.md`, `references/naming/rules.md`
|
||||
2. **Single Responsibility** → `references/classes/rules.md`, `references/functions/rules.md`
|
||||
3. **Small Units** → `references/functions/rules.md` (Rule 1: 2-5 lines ideal)
|
||||
4. **Meaningful Names** → `references/naming/rules.md`
|
||||
5. **DRY** → `references/smells/rules.md` (G5)
|
||||
6. **Clean Tests** → `references/unit-tests/rules.md`
|
||||
|
||||
### Professional Practices
|
||||
1. **Take Responsibility** → `references/professionalism/rules.md`
|
||||
2. **Say No** → `references/saying-no/rules.md`
|
||||
3. **Commit Clearly** → `references/commitment/rules.md`
|
||||
4. **Estimates != Commitments** → `references/estimation/rules.md`
|
||||
5. **Stay Clean Under Pressure** → `references/pressure/rules.md`
|
||||
|
||||
## Guidelines
|
||||
|
||||
See `guidelines.md` for:
|
||||
- Task → workflow mapping
|
||||
- Situation → reference file mapping
|
||||
- Decision tree for common scenarios
|
||||
|
||||
## Reference Loading Checklist
|
||||
|
||||
Before giving any code advice or writing code, verify:
|
||||
- [ ] Identified which reference categories apply to the current task
|
||||
- [ ] Loaded the `rules.md` for each applicable category
|
||||
- [ ] Loaded `examples.md` if demonstrating patterns or reviewing code
|
||||
- [ ] Loaded the relevant `workflow/*.md` if executing a multi-step task
|
||||
- [ ] Will cite specific rules/files in recommendations
|
||||
249
.agents/skills/typescript-clean-code/guidelines.md
Normal file
249
.agents/skills/typescript-clean-code/guidelines.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Clean Code Guidelines
|
||||
|
||||
Quick reference for finding the right workflow or reference file.
|
||||
|
||||
**How to use**:
|
||||
1. Find your task below → use the recommended **workflow**
|
||||
2. Need specific rules → load the **reference files**
|
||||
|
||||
---
|
||||
|
||||
## Workflows
|
||||
|
||||
Step-by-step processes for common tasks. **Start here for known tasks.**
|
||||
|
||||
### Code Tasks
|
||||
|
||||
| Task | Workflow |
|
||||
|------|----------|
|
||||
| Review code quality | `workflows/code-review/workflow.md` |
|
||||
| Review a pull request | `workflows/pr-review/workflow.md` |
|
||||
| Build new feature | `workflows/new-feature.md` |
|
||||
| Fix a bug | `workflows/bug-fix.md` |
|
||||
| Refactor code | `workflows/refactoring/workflow.md` |
|
||||
| Write tests (TDD) | `workflows/tdd.md` |
|
||||
| Plan test coverage | `workflows/test-strategy.md` |
|
||||
|
||||
### Professional Tasks
|
||||
|
||||
| Task | Workflow |
|
||||
|------|----------|
|
||||
| Estimate a task | `workflows/estimation.md` |
|
||||
| Handle unrealistic deadline | `workflows/deadline-negotiation.md` |
|
||||
|
||||
---
|
||||
|
||||
## Part 1: Code Quality References
|
||||
|
||||
### By Task - Code Review
|
||||
|
||||
| Reviewing... | Load these files |
|
||||
|--------------|------------------|
|
||||
| Function quality | `references/functions/rules.md`, `references/functions/checklist.md` |
|
||||
| Variable/function names | `references/naming/rules.md` |
|
||||
| Class design | `references/classes/rules.md` |
|
||||
| Comment quality | `references/comments/rules.md` |
|
||||
| Error handling | `references/error-handling/rules.md` |
|
||||
| Test quality | `references/unit-tests/rules.md` |
|
||||
| Code formatting | `references/formatting/rules.md` |
|
||||
| General code smells | `references/smells/rules.md` |
|
||||
|
||||
### By Task - Writing New Code
|
||||
|
||||
| Creating... | Load these files |
|
||||
|-------------|------------------|
|
||||
| New function | `references/functions/rules.md`, `references/naming/rules.md` |
|
||||
| New class/module | `references/classes/rules.md`, `references/naming/rules.md` |
|
||||
| Unit tests | `references/unit-tests/rules.md`, `references/tdd/rules.md` |
|
||||
| Acceptance tests | `references/acceptance-testing/rules.md` |
|
||||
| Error handling | `references/error-handling/rules.md` |
|
||||
| Comments/docs | `references/comments/rules.md` |
|
||||
|
||||
### By Task - Refactoring
|
||||
|
||||
| Fixing... | Load these files |
|
||||
|-----------|------------------|
|
||||
| Long function | `references/functions/rules.md`, `references/functions/examples.md` |
|
||||
| Large class | `references/classes/rules.md`, `references/classes/examples.md` |
|
||||
| Bad names | `references/naming/rules.md`, `references/naming/examples.md` |
|
||||
| Poor error handling | `references/error-handling/rules.md`, `references/error-handling/examples.md` |
|
||||
| Messy tests | `references/unit-tests/rules.md`, `references/unit-tests/examples.md` |
|
||||
| Any smell | `references/smells/rules.md`, `references/smells/examples.md` |
|
||||
|
||||
### By Code Element
|
||||
|
||||
| Working with... | Primary | Secondary |
|
||||
|-----------------|---------|-----------|
|
||||
| Functions | `references/functions/rules.md` | `references/naming/rules.md` |
|
||||
| Classes | `references/classes/rules.md` | `references/functions/rules.md` |
|
||||
| Modules | `references/classes/rules.md` | `references/naming/rules.md` |
|
||||
| Variables | `references/naming/rules.md` | `references/comments/rules.md` |
|
||||
| Tests | `references/unit-tests/rules.md` | `references/tdd/rules.md` |
|
||||
| Error handling | `references/error-handling/rules.md` | `references/functions/rules.md` |
|
||||
| Comments | `references/comments/rules.md` | - |
|
||||
|
||||
### By Problem/Symptom
|
||||
|
||||
| If you notice... | Load these files |
|
||||
|------------------|------------------|
|
||||
| Function > 20 lines | `references/functions/rules.md` |
|
||||
| Too many arguments (> 3) | `references/functions/rules.md` |
|
||||
| Unclear variable name | `references/naming/rules.md` |
|
||||
| Class doing too much | `references/classes/rules.md` (SRP) |
|
||||
| Duplicate code blocks | `references/smells/rules.md` (G5) |
|
||||
| Commented-out code | `references/smells/rules.md` (C5) |
|
||||
| Null checks everywhere | `references/error-handling/rules.md` |
|
||||
| Hard-to-read tests | `references/unit-tests/rules.md` |
|
||||
| Inconsistent formatting | `references/formatting/rules.md` |
|
||||
| Feature envy | `references/smells/rules.md` (G14) |
|
||||
| God class | `references/classes/rules.md`, `references/smells/rules.md` |
|
||||
|
||||
---
|
||||
|
||||
## Part 2: Professional Practices References
|
||||
|
||||
### By Situation - Communication
|
||||
|
||||
| Situation | Load these files |
|
||||
|-----------|------------------|
|
||||
| Asked to commit to deadline | `references/commitment/rules.md`, `references/saying-no/rules.md` |
|
||||
| Need to say no | `references/saying-no/rules.md`, `references/saying-no/examples.md` |
|
||||
| Making a promise | `references/commitment/rules.md`, `references/commitment/examples.md` |
|
||||
| Providing estimates | `references/estimation/rules.md`, `references/estimation/examples.md` |
|
||||
| Under pressure from stakeholders | `references/pressure/rules.md`, `references/saying-no/rules.md` |
|
||||
|
||||
### By Situation - Daily Work
|
||||
|
||||
| Situation | Load these files |
|
||||
|-----------|------------------|
|
||||
| Starting TDD workflow | `references/tdd/rules.md`, `references/tdd/examples.md` |
|
||||
| Planning test strategy | `references/testing-strategies/rules.md` |
|
||||
| Writing acceptance tests | `references/acceptance-testing/rules.md` |
|
||||
| Struggling to code | `references/coding-practices/rules.md` |
|
||||
| Too many meetings | `references/time-management/rules.md` |
|
||||
| Feeling stuck | `references/coding-practices/rules.md` (writer's block) |
|
||||
| Working under deadline | `references/pressure/rules.md`, `references/coding-practices/rules.md` |
|
||||
|
||||
### By Situation - Team & Career
|
||||
|
||||
| Situation | Load these files |
|
||||
|-----------|------------------|
|
||||
| Working with others | `references/collaboration/rules.md` |
|
||||
| Pairing decisions | `references/collaboration/rules.md` |
|
||||
| Career development | `references/professionalism/rules.md`, `references/practicing/rules.md` |
|
||||
| Improving skills | `references/practicing/rules.md`, `references/practicing/examples.md` |
|
||||
| New to team | `references/professionalism/rules.md`, `references/collaboration/rules.md` |
|
||||
|
||||
### By Problem - Professional
|
||||
|
||||
| If you're facing... | Load these files |
|
||||
|---------------------|------------------|
|
||||
| Unrealistic deadline | `references/saying-no/rules.md`, `references/estimation/rules.md` |
|
||||
| Pressure to cut corners | `references/pressure/rules.md`, `references/professionalism/rules.md` |
|
||||
| Estimate treated as commitment | `references/estimation/rules.md` |
|
||||
| Too much in meetings | `references/time-management/rules.md` |
|
||||
| Stuck in a mess/swamp | `references/time-management/rules.md` (messes section) |
|
||||
| Code ownership conflict | `references/collaboration/rules.md` |
|
||||
| Not improving skills | `references/practicing/rules.md` |
|
||||
|
||||
---
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```
|
||||
What do you need to do?
|
||||
│
|
||||
├─► KNOWN TASK (use workflow)
|
||||
│ │
|
||||
│ ├─► Code Tasks
|
||||
│ │ ├─► Review code → workflows/code-review/workflow.md
|
||||
│ │ ├─► Review PR → workflows/pr-review/workflow.md
|
||||
│ │ ├─► Build feature → workflows/new-feature.md
|
||||
│ │ ├─► Fix bug → workflows/bug-fix.md
|
||||
│ │ ├─► Refactor → workflows/refactoring/workflow.md
|
||||
│ │ ├─► Write tests → workflows/tdd.md
|
||||
│ │ └─► Plan tests → workflows/test-strategy.md
|
||||
│ │
|
||||
│ └─► Professional Tasks
|
||||
│ ├─► Estimate task → workflows/estimation.md
|
||||
│ └─► Negotiate deadline → workflows/deadline-negotiation.md
|
||||
│
|
||||
├─► NEED REFERENCE (use reference files)
|
||||
│ │
|
||||
│ ├─► Code Quality
|
||||
│ │ ├─► Names → references/naming/rules.md
|
||||
│ │ ├─► Functions → references/functions/rules.md
|
||||
│ │ ├─► Classes → references/classes/rules.md
|
||||
│ │ ├─► Tests → references/unit-tests/rules.md
|
||||
│ │ └─► Smells → references/smells/rules.md
|
||||
│ │
|
||||
│ └─► Professional
|
||||
│ ├─► Saying no → references/saying-no/rules.md
|
||||
│ ├─► Commitments → references/commitment/rules.md
|
||||
│ └─► Estimation → references/estimation/rules.md
|
||||
│
|
||||
└─► LEARNING (use knowledge files)
|
||||
└─► Start with → references/[topic]/knowledge.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Index
|
||||
|
||||
### Workflows (9 files)
|
||||
|
||||
| Workflow | Purpose |
|
||||
|----------|---------|
|
||||
| `workflows/code-review/workflow.md` | Review code for quality |
|
||||
| `workflows/pr-review/workflow.md` | Review pull requests |
|
||||
| `workflows/new-feature.md` | Build new functionality |
|
||||
| `workflows/bug-fix.md` | Fix bugs with test coverage |
|
||||
| `workflows/refactoring/workflow.md` | Safe refactoring process |
|
||||
| `workflows/tdd.md` | Test-driven development |
|
||||
| `workflows/test-strategy.md` | Plan test coverage |
|
||||
| `workflows/estimation.md` | PERT-based estimation |
|
||||
| `workflows/deadline-negotiation.md` | Handle deadline pressure |
|
||||
|
||||
### Code Quality References (25 files)
|
||||
|
||||
| Category | Files | Purpose |
|
||||
|----------|-------|---------|
|
||||
| naming | 3 | Variable, function, class naming |
|
||||
| functions | 4 | Function design and review |
|
||||
| classes | 3 | Class/module design |
|
||||
| comments | 3 | Comment best practices |
|
||||
| error-handling | 3 | Exception handling |
|
||||
| unit-tests | 3 | Clean test principles |
|
||||
| formatting | 3 | Code layout |
|
||||
| smells | 3 | Code smell catalog |
|
||||
|
||||
### Professional Practice References (36 files)
|
||||
|
||||
| Category | Files | Purpose |
|
||||
|----------|-------|---------|
|
||||
| professionalism | 3 | Professional ethics |
|
||||
| saying-no | 3 | Declining requests professionally |
|
||||
| commitment | 3 | Making real commitments |
|
||||
| coding-practices | 3 | Daily coding habits |
|
||||
| tdd | 3 | Test-driven development workflow |
|
||||
| practicing | 3 | Deliberate practice |
|
||||
| acceptance-testing | 3 | Requirements as tests |
|
||||
| testing-strategies | 3 | Test pyramid |
|
||||
| time-management | 3 | Meetings, focus, priorities |
|
||||
| estimation | 3 | Estimating tasks |
|
||||
| pressure | 3 | Working under pressure |
|
||||
| collaboration | 3 | Working with others |
|
||||
|
||||
---
|
||||
|
||||
## Common Combinations
|
||||
|
||||
| Scenario | Files |
|
||||
|----------|-------|
|
||||
| Full code review | `workflows/code-review/workflow.md` or `references/functions/checklist.md` + `references/smells/rules.md` |
|
||||
| Writing a new feature | `workflows/new-feature.md` |
|
||||
| Starting TDD | `workflows/tdd.md` |
|
||||
| Test strategy planning | `workflows/test-strategy.md` |
|
||||
| Estimate + negotiate | `workflows/estimation.md` + `workflows/deadline-negotiation.md` |
|
||||
| Under pressure | `references/pressure/rules.md` + `references/coding-practices/rules.md` |
|
||||
| Career growth | `references/professionalism/rules.md` + `references/practicing/rules.md` |
|
||||
245
.agents/skills/typescript-clean-code/progress.md
Normal file
245
.agents/skills/typescript-clean-code/progress.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# Clean Code Skill - Creation Progress
|
||||
|
||||
## Status Overview
|
||||
|
||||
| Phase | Files | Complete |
|
||||
|-------|-------|----------|
|
||||
| Foundation | 3 | 3/3 |
|
||||
| Workflows | 9 | 9/9 |
|
||||
| **Code Quality (Clean Code book)** | | |
|
||||
| naming | 3 | 3/3 |
|
||||
| functions | 4 | 4/4 |
|
||||
| comments | 3 | 3/3 |
|
||||
| formatting | 3 | 3/3 |
|
||||
| error-handling | 3 | 3/3 |
|
||||
| unit-tests | 3 | 3/3 |
|
||||
| classes | 3 | 3/3 |
|
||||
| smells | 3 | 3/3 |
|
||||
| **Professional Practices (Clean Coder book)** | | |
|
||||
| professionalism | 3 | 3/3 |
|
||||
| saying-no | 3 | 3/3 |
|
||||
| commitment | 3 | 3/3 |
|
||||
| coding-practices | 3 | 3/3 |
|
||||
| tdd | 3 | 3/3 |
|
||||
| practicing | 3 | 3/3 |
|
||||
| acceptance-testing | 3 | 3/3 |
|
||||
| testing-strategies | 3 | 3/3 |
|
||||
| time-management | 3 | 3/3 |
|
||||
| estimation | 3 | 3/3 |
|
||||
| pressure | 3 | 3/3 |
|
||||
| collaboration | 3 | 3/3 |
|
||||
| **Total** | **73** | **73/73** |
|
||||
|
||||
## Legend
|
||||
|
||||
- [ ] Not started
|
||||
- [~] In progress
|
||||
- [x] Completed
|
||||
- [-] Skipped/Not needed
|
||||
|
||||
---
|
||||
|
||||
## Foundation
|
||||
|
||||
- [x] SKILL.md
|
||||
- [x] progress.md
|
||||
- [x] guidelines.md
|
||||
|
||||
## Workflows
|
||||
|
||||
- [x] workflows/code-review/workflow.md - Review code for quality (step-file architecture: 10 step files)
|
||||
- [x] workflows/pr-review/workflow.md - Review pull requests (step-file architecture: 10 step files)
|
||||
- [x] workflows/tdd.md - Test-driven development cycle
|
||||
- [x] workflows/refactoring/workflow.md - Safe refactoring with tests (step-file architecture: 8 step files)
|
||||
- [x] workflows/new-feature.md - Building new functionality
|
||||
- [x] workflows/bug-fix.md - Fixing bugs properly
|
||||
- [x] workflows/test-strategy.md - Planning test coverage
|
||||
- [x] workflows/estimation.md - PERT-based task estimation
|
||||
- [x] workflows/deadline-negotiation.md - Handling deadline pressure
|
||||
|
||||
### Step-File Workflows (new)
|
||||
|
||||
Code review, PR review, and refactoring workflows converted to step-file architecture for context-safe execution. Original files preserved as `.legacy.md`.
|
||||
|
||||
- [x] workflows/code-review.legacy.md (original, preserved for reference)
|
||||
- [x] workflows/pr-review.legacy.md (original, preserved for reference)
|
||||
- [x] workflows/refactoring.legacy.md (original, preserved for reference)
|
||||
|
||||
---
|
||||
|
||||
## Part 1: Code Quality (Clean Code book)
|
||||
|
||||
### naming
|
||||
|
||||
Source: Clean Code, Chapter 2 (lines 749-1179)
|
||||
|
||||
- [x] references/naming/knowledge.md
|
||||
- [x] references/naming/rules.md
|
||||
- [x] references/naming/examples.md
|
||||
|
||||
### functions
|
||||
|
||||
Source: Clean Code, Chapter 3 (lines 1190-1889)
|
||||
|
||||
- [x] references/functions/knowledge.md
|
||||
- [x] references/functions/rules.md
|
||||
- [x] references/functions/examples.md
|
||||
- [x] references/functions/checklist.md
|
||||
|
||||
### comments
|
||||
|
||||
Source: Clean Code, Chapter 4 (lines 1889-2848)
|
||||
|
||||
- [x] references/comments/knowledge.md
|
||||
- [x] references/comments/rules.md
|
||||
- [x] references/comments/examples.md
|
||||
|
||||
### formatting
|
||||
|
||||
Source: Clean Code, Chapter 5 (lines 2848-3455)
|
||||
|
||||
- [x] references/formatting/knowledge.md
|
||||
- [x] references/formatting/rules.md
|
||||
- [x] references/formatting/examples.md
|
||||
|
||||
### error-handling
|
||||
|
||||
Source: Clean Code, Chapter 7 (lines 3772-4165)
|
||||
|
||||
- [x] references/error-handling/knowledge.md
|
||||
- [x] references/error-handling/rules.md
|
||||
- [x] references/error-handling/examples.md
|
||||
|
||||
### unit-tests
|
||||
|
||||
Source: Clean Code, Chapter 9 (lines 4381-4785)
|
||||
|
||||
- [x] references/unit-tests/knowledge.md
|
||||
- [x] references/unit-tests/rules.md
|
||||
- [x] references/unit-tests/examples.md
|
||||
|
||||
### classes
|
||||
|
||||
Source: Clean Code, Chapter 10 (lines 4785-5417)
|
||||
|
||||
- [x] references/classes/knowledge.md
|
||||
- [x] references/classes/rules.md
|
||||
- [x] references/classes/examples.md
|
||||
|
||||
### smells
|
||||
|
||||
Source: Clean Code, Chapter 17 (lines 10864-11962)
|
||||
|
||||
- [x] references/smells/knowledge.md
|
||||
- [x] references/smells/rules.md
|
||||
- [x] references/smells/examples.md
|
||||
|
||||
---
|
||||
|
||||
## Part 2: Professional Practices (Clean Coder book)
|
||||
|
||||
### professionalism
|
||||
|
||||
Source: Clean Coder, Chapter 1 (lines 822-1083)
|
||||
|
||||
- [x] references/professionalism/knowledge.md
|
||||
- [x] references/professionalism/rules.md
|
||||
- [x] references/professionalism/examples.md
|
||||
|
||||
### saying-no
|
||||
|
||||
Source: Clean Coder, Chapter 2 (lines 1083-1573)
|
||||
|
||||
- [x] references/saying-no/knowledge.md
|
||||
- [x] references/saying-no/rules.md
|
||||
- [x] references/saying-no/examples.md
|
||||
|
||||
### commitment
|
||||
|
||||
Source: Clean Coder, Chapter 3 (lines 1573-1811)
|
||||
|
||||
- [x] references/commitment/knowledge.md
|
||||
- [x] references/commitment/rules.md
|
||||
- [x] references/commitment/examples.md
|
||||
|
||||
### coding-practices
|
||||
|
||||
Source: Clean Coder, Chapter 4 (lines 1811-2105)
|
||||
|
||||
- [x] references/coding-practices/knowledge.md
|
||||
- [x] references/coding-practices/rules.md
|
||||
- [x] references/coding-practices/examples.md
|
||||
|
||||
### tdd
|
||||
|
||||
Source: Clean Coder, Chapter 5 (lines 2105-2235)
|
||||
|
||||
- [x] references/tdd/knowledge.md
|
||||
- [x] references/tdd/rules.md
|
||||
- [x] references/tdd/examples.md
|
||||
|
||||
### practicing
|
||||
|
||||
Source: Clean Coder, Chapter 6 (lines 2235-2387)
|
||||
|
||||
- [x] references/practicing/knowledge.md
|
||||
- [x] references/practicing/rules.md
|
||||
- [x] references/practicing/examples.md
|
||||
|
||||
### acceptance-testing
|
||||
|
||||
Source: Clean Coder, Chapter 7 (lines 2387-2804)
|
||||
|
||||
- [x] references/acceptance-testing/knowledge.md
|
||||
- [x] references/acceptance-testing/rules.md
|
||||
- [x] references/acceptance-testing/examples.md
|
||||
|
||||
### testing-strategies
|
||||
|
||||
Source: Clean Coder, Chapter 8 (lines 2804-2903)
|
||||
|
||||
- [x] references/testing-strategies/knowledge.md
|
||||
- [x] references/testing-strategies/rules.md
|
||||
- [x] references/testing-strategies/examples.md
|
||||
|
||||
### time-management
|
||||
|
||||
Source: Clean Coder, Chapter 9 (lines 2903-3122)
|
||||
|
||||
- [x] references/time-management/knowledge.md
|
||||
- [x] references/time-management/rules.md
|
||||
- [x] references/time-management/examples.md
|
||||
|
||||
### estimation
|
||||
|
||||
Source: Clean Coder, Chapter 10 (lines 3122-3361)
|
||||
|
||||
- [x] references/estimation/knowledge.md
|
||||
- [x] references/estimation/rules.md
|
||||
- [x] references/estimation/examples.md
|
||||
|
||||
### pressure
|
||||
|
||||
Source: Clean Coder, Chapter 11 (lines 3361-3457)
|
||||
|
||||
- [x] references/pressure/knowledge.md
|
||||
- [x] references/pressure/rules.md
|
||||
- [x] references/pressure/examples.md
|
||||
|
||||
### collaboration
|
||||
|
||||
Source: Clean Coder, Chapter 12 (lines 3457-3543)
|
||||
|
||||
- [x] references/collaboration/knowledge.md
|
||||
- [x] references/collaboration/rules.md
|
||||
- [x] references/collaboration/examples.md
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Part 1 (Clean Code): All Java examples converted to TypeScript
|
||||
- Part 2 (Clean Coder): Professional practices, scenario-based examples
|
||||
- Workflows: Step-by-step processes for consistent task execution
|
||||
- Skipped: Java-specific chapters, case studies, appendices
|
||||
- Completed: 2026-01-26
|
||||
@@ -0,0 +1,200 @@
|
||||
# Acceptance Testing Examples
|
||||
|
||||
Code examples demonstrating acceptance testing principles in modern TypeScript/Gherkin style.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Vague Requirement Without Test
|
||||
|
||||
```typescript
|
||||
// Requirement: "Log files need to be backed up daily"
|
||||
// No acceptance test - ambiguity remains:
|
||||
// - Backup one file or all files?
|
||||
// - Keep history or overwrite?
|
||||
// - What time of day?
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Developers interpret differently than stakeholders
|
||||
- No formal definition of "done"
|
||||
- Bugs discovered only after deployment
|
||||
|
||||
### GUI Position-Based Test
|
||||
|
||||
```typescript
|
||||
// Bad: Tests break when layout changes
|
||||
test('submit form', async ({ page }) => {
|
||||
await page.click('button:nth-child(3)'); // Position-based
|
||||
await page.click('div.grid > div:nth-child(4) > button');
|
||||
});
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Fragile - breaks on any layout change
|
||||
- Hard to understand intent
|
||||
- Maintenance nightmare
|
||||
|
||||
### Testing Business Rules Through GUI
|
||||
|
||||
```typescript
|
||||
// Bad: Business logic tested through UI layer
|
||||
test('calculate discount', async ({ page }) => {
|
||||
await page.goto('/checkout');
|
||||
await page.fill('#quantity', '100');
|
||||
await page.fill('#coupon', 'SAVE20');
|
||||
await page.click('#calculate');
|
||||
await expect(page.locator('#total')).toContainText('$800');
|
||||
});
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Slow execution through full UI
|
||||
- Breaks when UI changes
|
||||
- Business rule buried in GUI test
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Gherkin Feature Specification
|
||||
|
||||
```gherkin
|
||||
Feature: Log File Backup
|
||||
As a system administrator
|
||||
I want log files archived daily
|
||||
So that I can review historical logs for audits
|
||||
|
||||
Scenario: Create backup directory on startup
|
||||
Given the command LogFileDirectoryStartupCommand
|
||||
And the old_inactive_logs directory does not exist
|
||||
When the command is executed
|
||||
Then the old_inactive_logs directory should exist
|
||||
And it should be empty
|
||||
|
||||
Scenario: Preserve existing backups on restart
|
||||
Given the command LogFileDirectoryStartupCommand
|
||||
And the old_inactive_logs directory exists
|
||||
And it contains a file named "2024-01-15.log"
|
||||
When the command is executed
|
||||
Then the old_inactive_logs directory should still exist
|
||||
And it should still contain a file named "2024-01-15.log"
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Unambiguous specification
|
||||
- Readable by business stakeholders
|
||||
- Executable documentation
|
||||
|
||||
### Step Definitions in TypeScript
|
||||
|
||||
```typescript
|
||||
import { Given, When, Then } from '@cucumber/cucumber';
|
||||
import { LogFileDirectoryStartupCommand } from '../src/commands';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const LOG_DIR = './old_inactive_logs';
|
||||
let command: LogFileDirectoryStartupCommand;
|
||||
|
||||
Given('the command LogFileDirectoryStartupCommand', () => {
|
||||
command = new LogFileDirectoryStartupCommand();
|
||||
});
|
||||
|
||||
Given('the old_inactive_logs directory does not exist', () => {
|
||||
if (fs.existsSync(LOG_DIR)) fs.rmSync(LOG_DIR, { recursive: true });
|
||||
});
|
||||
|
||||
When('the command is executed', async () => {
|
||||
await command.execute();
|
||||
});
|
||||
|
||||
Then('the old_inactive_logs directory should exist', () => {
|
||||
expect(fs.existsSync(LOG_DIR)).toBe(true);
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Tests through command API, not GUI
|
||||
- Reusable step definitions
|
||||
- Clear mapping from spec to code
|
||||
|
||||
### Statistical Performance Test
|
||||
|
||||
```gherkin
|
||||
Scenario: Response time meets SLA
|
||||
When 15 post transactions are executed
|
||||
Then the odds should be 99.5% that response time is less than 2 seconds
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Statistical guarantee instead of impossible absolute
|
||||
- Negotiated with stakeholders for realistic criteria
|
||||
- Business-readable requirement
|
||||
|
||||
### Testing Through API, Not GUI
|
||||
|
||||
```typescript
|
||||
// Good: Test business rules through service layer
|
||||
describe('Discount Calculator', () => {
|
||||
it('applies 20% discount for orders over $1000', async () => {
|
||||
const order = new Order({ items: [{ price: 100, quantity: 12 }], couponCode: 'SAVE20' });
|
||||
const result = await discountService.calculateTotal(order);
|
||||
expect(result.total).toBe(960); // 20% off $1200
|
||||
});
|
||||
});
|
||||
|
||||
// Separate: Minimal GUI test with stubbed business logic
|
||||
describe('Checkout UI', () => {
|
||||
it('displays calculated total', async ({ page }) => {
|
||||
await mockDiscountService({ total: 960 });
|
||||
await page.goto('/checkout');
|
||||
await expect(page.getByTestId('total')).toContainText('$960');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Business rules tested fast through API
|
||||
- GUI tests only verify display logic
|
||||
- GUI changes don't break business tests
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before: Ambiguous Manual Process
|
||||
|
||||
```
|
||||
Requirements Document:
|
||||
"The system shall backup log files daily."
|
||||
|
||||
Manual Test Plan:
|
||||
1. Wait until midnight
|
||||
2. Check if backup exists
|
||||
3. Mark pass/fail in spreadsheet
|
||||
```
|
||||
|
||||
### After: Executable Specification
|
||||
|
||||
```gherkin
|
||||
Feature: Daily Log Backup
|
||||
|
||||
Background:
|
||||
Given the system clock can be controlled
|
||||
And the backup service is initialized
|
||||
|
||||
Scenario: Midnight triggers backup of current log
|
||||
Given today is "2024-01-15"
|
||||
And the active log file contains 500 entries
|
||||
When the clock reaches midnight
|
||||
Then a file "2024-01-15.log" should exist in old_inactive_logs
|
||||
And it should contain 500 entries
|
||||
And a new empty active log should be created
|
||||
|
||||
Scenario: Backup preserves all historical logs
|
||||
Given old_inactive_logs contains 30 log files
|
||||
When a new backup is triggered
|
||||
Then old_inactive_logs should contain 31 log files
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. **Converted prose to scenarios** - Each behavior is testable
|
||||
2. **Added specific assertions** - "500 entries" not "backup exists"
|
||||
3. **Made time controllable** - No waiting for midnight
|
||||
4. **Specified edge cases** - Historical logs preserved
|
||||
@@ -0,0 +1,101 @@
|
||||
# Acceptance Testing Knowledge
|
||||
|
||||
Core concepts and foundational understanding for acceptance testing as a communication and specification tool.
|
||||
|
||||
## Overview
|
||||
|
||||
Acceptance tests are formal specifications written collaboratively by stakeholders, testers, and developers to define when a requirement is truly "done." They serve primarily as communication tools that eliminate ambiguity, not just as verification mechanisms. The tests become executable requirements documents that cannot get out of sync with the application.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Acceptance Tests as Communication
|
||||
|
||||
**Definition**: Tests written by collaboration of stakeholders and programmers to define when a requirement is complete.
|
||||
|
||||
Acceptance tests are documents first, tests second. Their primary purpose is to formally specify system behavior in unambiguous terms that all parties understand.
|
||||
|
||||
**Key points**:
|
||||
- Eliminate communication errors between programmers and stakeholders
|
||||
- Force precision in requirements discussions
|
||||
- Create shared understanding of expected behavior
|
||||
|
||||
### The Definition of "Done"
|
||||
|
||||
**Definition**: Done means all code written, all tests pass, QA and stakeholders have accepted.
|
||||
|
||||
Professional developers have a single, unambiguous definition of done. When acceptance tests pass, the feature is complete and ready for deployment.
|
||||
|
||||
**Key points**:
|
||||
- Not "done" vs "done-done" - just done
|
||||
- Automated tests define the finish line
|
||||
- Eliminates ambiguity about completion status
|
||||
|
||||
### Premature Precision
|
||||
|
||||
**Definition**: The trap of trying to specify requirements with exact detail too early in the process.
|
||||
|
||||
Both business and developers want precision before it's achievable. Business wants to know exactly what they'll get; developers want to know exactly what to build. This precision cannot be achieved early and wastes resources.
|
||||
|
||||
**Key points**:
|
||||
- Requirements appear different on paper vs. running system
|
||||
- Stakeholders change minds when they see working software
|
||||
- Early precision becomes irrelevant as understanding evolves
|
||||
|
||||
### The Uncertainty Principle (Requirements)
|
||||
|
||||
**Definition**: Demonstrating a feature gives stakeholders new information that changes how they see the whole system.
|
||||
|
||||
When you show working software, stakeholders gain insight that impacts their view of requirements. The more precise early requirements are, the less relevant they become during implementation.
|
||||
|
||||
### Late Ambiguity
|
||||
|
||||
**Definition**: Unresolved disagreements or assumptions hidden in vague requirement language.
|
||||
|
||||
Stakeholders may "wordsmith" around disagreements rather than resolve them. Ambiguity in requirements often represents unresolved arguments or unstated assumptions.
|
||||
|
||||
**Key points**:
|
||||
- Defer precision as long as possible, but resolve ambiguity before coding
|
||||
- Context differs between stakeholders and developers
|
||||
- Professional developers ensure all ambiguity is removed
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Acceptance Test | Automated test defining when a requirement is done |
|
||||
| Happy Path | Tests describing features with business value |
|
||||
| Unhappy Path | Tests for boundary conditions, exceptions, corner cases |
|
||||
| Fixture | Code connecting test statements to the system under test |
|
||||
| Scenario | Reusable test pattern matching statements to functions |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Unit Tests**: Different audience and purpose; unit tests are for programmers, acceptance tests are for business
|
||||
- **Requirements Documents**: Acceptance tests ARE the requirements, but executable
|
||||
- **Continuous Integration**: All acceptance tests should run on every commit
|
||||
- **Definition of Done**: Acceptance tests formally define completion criteria
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Acceptance tests are extra work on top of requirements
|
||||
**Reality**: They ARE the work of specifying requirements, just in executable form
|
||||
|
||||
- **Myth**: Unit tests and acceptance tests are redundant
|
||||
**Reality**: They test through different pathways and serve different audiences
|
||||
|
||||
- **Myth**: Acceptance tests are primarily for verification
|
||||
**Reality**: Their primary purpose is specification and communication; verification is secondary
|
||||
|
||||
- **Myth**: Stakeholders should write all acceptance tests
|
||||
**Reality**: Collaboration is key; BAs write happy paths, QA writes edge cases, developers may help
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Acceptance Tests | Executable requirements written collaboratively |
|
||||
| Definition of Done | All tests pass, QA and stakeholders accept |
|
||||
| Premature Precision | Specifying details too early wastes effort |
|
||||
| Uncertainty Principle | Seeing software changes stakeholder understanding |
|
||||
| Late Ambiguity | Hidden disagreements in vague requirements |
|
||||
| Communication Purpose | Tests eliminate ambiguity between all parties |
|
||||
@@ -0,0 +1,119 @@
|
||||
# Acceptance Testing Rules
|
||||
|
||||
Rules for writing, managing, and executing acceptance tests as professional developers.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Always Automate Acceptance Tests
|
||||
|
||||
Acceptance tests must always be automated. Manual test plans are economically unsustainable.
|
||||
|
||||
- Manual testing costs grow with each execution
|
||||
- Automated tests can run multiple times daily
|
||||
- Manual tests get cut when budgets tighten, leaving gaps in coverage
|
||||
|
||||
**The cost comparison**: A manual test plan costing $1M+ per execution vs. one-time automation investment.
|
||||
|
||||
### 2. Define "Done" With Passing Tests
|
||||
|
||||
Done means done: all code written, all tests pass, QA and stakeholders accept.
|
||||
|
||||
- Create automated tests that define completion criteria
|
||||
- When acceptance tests pass, the feature is deployable
|
||||
- No ambiguous states like "done" vs "done-done"
|
||||
|
||||
### 3. Write Tests Late, But Before Implementation
|
||||
|
||||
Follow "late precision" - write acceptance tests just before implementing the feature.
|
||||
|
||||
- Tests should be ready by the first day of iteration/sprint
|
||||
- All tests ready by iteration midpoint
|
||||
- If tests aren't ready, developers help complete them
|
||||
|
||||
### 4. Separate Test Authors From Implementers
|
||||
|
||||
The developer who writes the test should not implement the tested feature.
|
||||
|
||||
- Business analysts write happy path tests (business value)
|
||||
- QA writes unhappy path tests (edge cases, exceptions)
|
||||
- Developers review for consistency and feasibility
|
||||
|
||||
### 5. Never Be Passive-Aggressive With Tests
|
||||
|
||||
If a test doesn't make sense, negotiate - don't blindly implement.
|
||||
|
||||
- Tests may be too complicated, awkward, or wrong
|
||||
- Professionals help the team create the best software
|
||||
- "That's what the test says" is not a valid excuse
|
||||
|
||||
**Bad approach**:
|
||||
```
|
||||
// Test says 2-second guarantee, so I'll implement exactly that
|
||||
// even though it's statistically impossible
|
||||
```
|
||||
|
||||
**Good approach**:
|
||||
```
|
||||
// Negotiate with test author for realistic criteria
|
||||
// e.g., "99.5% of requests complete in 2 seconds"
|
||||
```
|
||||
|
||||
### 6. Test Through APIs, Not GUIs
|
||||
|
||||
Write business rule tests through an API below the GUI.
|
||||
|
||||
- GUIs change frequently, making tests fragile
|
||||
- GUI changes shouldn't break business rule tests
|
||||
- Keep GUI-specific tests minimal
|
||||
- Decouple GUI from business rules for testing
|
||||
|
||||
### 7. Keep CI Tests Always Passing
|
||||
|
||||
Broken tests in continuous integration are emergencies.
|
||||
|
||||
- Run all tests on every commit
|
||||
- Failed tests = "stop the presses"
|
||||
- Never disable failing tests to meet deadlines
|
||||
- Never remove tests from the build
|
||||
|
||||
## Guidelines
|
||||
|
||||
Less strict recommendations for effective acceptance testing:
|
||||
|
||||
- Use unique IDs for GUI elements rather than positional selectors
|
||||
- Make intermediate calculations visible in test reports for verification
|
||||
- Create reusable scenarios and fixtures across tests
|
||||
- Include error bars in estimates when requirements are imprecise
|
||||
- Involve testers early in requirements discussions
|
||||
|
||||
## Exceptions
|
||||
|
||||
When rules may be relaxed:
|
||||
|
||||
- **Manual testing**: Acceptable for exploratory testing and aesthetics, but not for acceptance criteria
|
||||
- **Developer-written tests**: When stakeholders lack time, developers may write tests - but not for features they implement
|
||||
- **GUI testing**: Necessary when testing GUI behavior specifically, but use stubs for business rules
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Automate | Never use manual acceptance tests |
|
||||
| Define Done | Passing tests = feature complete |
|
||||
| Late Precision | Write tests just before implementation |
|
||||
| Separate Authors | Test writer != implementer |
|
||||
| Negotiate Tests | Push back on problematic tests |
|
||||
| API Over GUI | Test business rules below UI layer |
|
||||
| CI Always Green | Broken build = emergency |
|
||||
|
||||
## Acceptance Tests vs Unit Tests
|
||||
|
||||
| Aspect | Acceptance Tests | Unit Tests |
|
||||
|--------|-----------------|------------|
|
||||
| Written by | Business/QA/BAs | Programmers |
|
||||
| Written for | Business + Programmers | Programmers |
|
||||
| Tests through | API or UI level | Method calls |
|
||||
| Documents | System behavior | Code structure |
|
||||
| Primary purpose | Specification | Design documentation |
|
||||
|
||||
Both test similar things through different pathways - neither is redundant.
|
||||
@@ -0,0 +1,200 @@
|
||||
# Classes Examples
|
||||
|
||||
Code examples demonstrating clean class design principles in TypeScript.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### God Class with Too Many Responsibilities
|
||||
|
||||
```typescript
|
||||
class SuperDashboard {
|
||||
getLastFocusedComponent(): Component { }
|
||||
setLastFocused(component: Component): void { }
|
||||
getMajorVersionNumber(): number { }
|
||||
getMinorVersionNumber(): number { }
|
||||
getBuildNumber(): number { }
|
||||
getProject(): Project { }
|
||||
addProject(project: Project): void { }
|
||||
getConfigManager(): ConfigManager { }
|
||||
// ... 60+ more methods
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Multiple reasons to change (versioning, UI focus, projects, config)
|
||||
- Name uses "Super" - a weasel word indicating too many responsibilities
|
||||
|
||||
### Class Violating Open-Closed Principle
|
||||
|
||||
```typescript
|
||||
class Sql {
|
||||
constructor(private table: string, private columns: Column[]) { }
|
||||
create(): string { }
|
||||
insert(fields: object[]): string { }
|
||||
selectAll(): string { }
|
||||
select(criteria: Criteria): string { }
|
||||
private columnList(): string { }
|
||||
private valuesList(fields: object[]): string { }
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Must open class to add new statement types (UPDATE, DELETE)
|
||||
- Private methods relate only to specific public methods (hints at extraction)
|
||||
|
||||
### Concrete Dependency Making Testing Hard
|
||||
|
||||
```typescript
|
||||
class Portfolio {
|
||||
private exchange = new TokyoStockExchange();
|
||||
|
||||
getValue(): Money {
|
||||
return this.holdings.reduce((total, h) => {
|
||||
const price = this.exchange.currentPrice(h.symbol);
|
||||
return total.add(price.times(h.shares));
|
||||
}, Money.zero());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Cannot test without hitting real stock exchange API
|
||||
- Test results vary based on actual stock prices
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Single Responsibility - Version Class
|
||||
|
||||
```typescript
|
||||
class Version {
|
||||
constructor(
|
||||
private readonly major: number,
|
||||
private readonly minor: number,
|
||||
private readonly build: number
|
||||
) { }
|
||||
|
||||
getMajorVersionNumber(): number { return this.major; }
|
||||
getMinorVersionNumber(): number { return this.minor; }
|
||||
getBuildNumber(): number { return this.build; }
|
||||
toString(): string { return `${this.major}.${this.minor}.${this.build}`; }
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: Single responsibility, one reason to change, highly reusable.
|
||||
|
||||
### Highly Cohesive Class
|
||||
|
||||
```typescript
|
||||
class Stack<T> {
|
||||
private topOfStack = 0;
|
||||
private elements: T[] = [];
|
||||
|
||||
size(): number { return this.topOfStack; }
|
||||
|
||||
push(element: T): void {
|
||||
this.topOfStack++;
|
||||
this.elements.push(element);
|
||||
}
|
||||
|
||||
pop(): T {
|
||||
if (this.topOfStack === 0) throw new Error('Stack is empty');
|
||||
this.topOfStack--;
|
||||
return this.elements.pop()!;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: All methods use shared instance variables, forming a logical whole.
|
||||
|
||||
### Dependency Injection for Testability
|
||||
|
||||
```typescript
|
||||
interface StockExchange {
|
||||
currentPrice(symbol: string): Money;
|
||||
}
|
||||
|
||||
class Portfolio {
|
||||
constructor(private readonly exchange: StockExchange) { }
|
||||
|
||||
getValue(): Money {
|
||||
return this.holdings.reduce((total, h) => {
|
||||
const price = this.exchange.currentPrice(h.symbol);
|
||||
return total.add(price.times(h.shares));
|
||||
}, Money.zero());
|
||||
}
|
||||
}
|
||||
|
||||
// Test stub
|
||||
class FixedStockExchangeStub implements StockExchange {
|
||||
private prices = new Map<string, number>();
|
||||
fix(symbol: string, price: number): void { this.prices.set(symbol, price); }
|
||||
currentPrice(symbol: string): Money { return Money.dollars(this.prices.get(symbol) ?? 0); }
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: Depends on abstraction, easy to test with stub.
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before: Monolithic SQL Class
|
||||
|
||||
```typescript
|
||||
class Sql {
|
||||
constructor(private table: string, private columns: Column[]) { }
|
||||
create(): string { /* ... */ }
|
||||
insert(fields: object[]): string { /* ... */ }
|
||||
selectAll(): string { /* ... */ }
|
||||
private columnList(): string { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
### After: Family of Single-Purpose Classes
|
||||
|
||||
```typescript
|
||||
abstract class Sql {
|
||||
constructor(protected table: string, protected columns: Column[]) { }
|
||||
abstract generate(): string;
|
||||
}
|
||||
|
||||
class CreateSql extends Sql {
|
||||
generate(): string {
|
||||
const defs = this.columns.map(c => `${c.name} ${c.type}`).join(', ');
|
||||
return `CREATE TABLE ${this.table} (${defs})`;
|
||||
}
|
||||
}
|
||||
|
||||
class SelectSql extends Sql {
|
||||
generate(): string {
|
||||
return `SELECT ${new ColumnList(this.columns).generate()} FROM ${this.table}`;
|
||||
}
|
||||
}
|
||||
|
||||
class InsertSql extends Sql {
|
||||
constructor(table: string, columns: Column[], private fields: object[]) { super(table, columns); }
|
||||
generate(): string {
|
||||
const cols = new ColumnList(this.columns).generate();
|
||||
return `INSERT INTO ${this.table} (${cols}) VALUES (${this.fields.map(f => `'${f}'`).join(', ')})`;
|
||||
}
|
||||
}
|
||||
|
||||
// Adding UPDATE requires NO changes to existing classes
|
||||
class UpdateSql extends Sql {
|
||||
constructor(table: string, columns: Column[], private values: Record<string, unknown>, private where: string) { super(table, columns); }
|
||||
generate(): string {
|
||||
return `UPDATE ${this.table} SET ${Object.entries(this.values).map(([k, v]) => `${k} = '${v}'`).join(', ')} WHERE ${this.where}`;
|
||||
}
|
||||
}
|
||||
|
||||
class ColumnList {
|
||||
constructor(private columns: Column[]) { }
|
||||
generate(): string { return this.columns.map(c => c.name).join(', '); }
|
||||
}
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. **Extracted abstract base class** - Common structure shared by all SQL types
|
||||
2. **Created subclass per statement type** - Each has single responsibility
|
||||
3. **Moved private methods** - `valuesList` now lives only where needed
|
||||
4. **Extracted shared utilities** - `ColumnList` for common formatting
|
||||
5. **Open for extension** - Adding `UpdateSql` required zero changes to existing classes
|
||||
@@ -0,0 +1,115 @@
|
||||
# Classes Knowledge
|
||||
|
||||
Core concepts and foundational understanding for designing clean classes and modules.
|
||||
|
||||
## Overview
|
||||
|
||||
Classes are the higher-level organizational units of code. While functions handle blocks of code, classes organize functions and data into cohesive units with clear responsibilities. Clean classes are small (measured by responsibilities, not lines), highly cohesive, and loosely coupled.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Class Organization
|
||||
|
||||
**Definition**: The standard ordering of elements within a class file.
|
||||
|
||||
Classes should follow a consistent structure for readability:
|
||||
|
||||
**Ordering (top to bottom)**:
|
||||
- Public static constants
|
||||
- Private static variables
|
||||
- Private instance variables (rarely public)
|
||||
- Public methods
|
||||
- Private utility methods (placed after the public method that calls them)
|
||||
|
||||
This follows the "stepdown rule" - the class reads like a newspaper article.
|
||||
|
||||
### Encapsulation
|
||||
|
||||
**Definition**: Keeping variables and utility functions private, exposing only what's necessary.
|
||||
|
||||
**Key points**:
|
||||
- Default to private for variables and helper methods
|
||||
- Use protected only when tests require access (same package)
|
||||
- Loosening encapsulation is always a last resort
|
||||
- Tests can justify relaxing access, but first look for alternatives
|
||||
|
||||
### Single Responsibility Principle (SRP)
|
||||
|
||||
**Definition**: A class should have one, and only one, reason to change.
|
||||
|
||||
SRP is the most important concept in class design. A "responsibility" equals a "reason to change."
|
||||
|
||||
**Key points**:
|
||||
- The class name should describe its single responsibility
|
||||
- If you need "and," "or," "if," or "but" to describe it, it's too big
|
||||
- Weasel words hint at multiple responsibilities: `Processor`, `Manager`, `Super`, `Handler`
|
||||
- A 25-word description without conjunctions is a good test
|
||||
|
||||
### Cohesion
|
||||
|
||||
**Definition**: The degree to which methods use the class's instance variables.
|
||||
|
||||
A highly cohesive class has methods that work together with shared state.
|
||||
|
||||
**Key points**:
|
||||
- Each method should manipulate one or more instance variables
|
||||
- More shared variable usage = higher cohesion
|
||||
- When cohesion drops, it signals time to split the class
|
||||
- Ideal: variables exist because multiple methods need them together
|
||||
|
||||
### Open-Closed Principle (OCP)
|
||||
|
||||
**Definition**: Classes should be open for extension but closed for modification.
|
||||
|
||||
**Key points**:
|
||||
- Add new functionality by creating new classes (subclasses)
|
||||
- Existing classes should not need modification for new features
|
||||
- Reduces risk when adding features to a system
|
||||
|
||||
### Dependency Inversion Principle (DIP)
|
||||
|
||||
**Definition**: Classes should depend upon abstractions, not concrete details.
|
||||
|
||||
**Key points**:
|
||||
- Use interfaces to decouple from implementation details
|
||||
- Makes testing easier (can substitute test doubles)
|
||||
- Promotes flexibility and reuse
|
||||
- Isolates classes from changes in dependencies
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Responsibility | A reason for a class to change |
|
||||
| Cohesion | How closely methods relate via shared instance variables |
|
||||
| Coupling | Degree of interdependence between classes |
|
||||
| God Class | A class that knows/does too much (anti-pattern) |
|
||||
| Abstraction | Interface or abstract class representing a concept |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Functions**: Classes organize functions; both should be small and focused
|
||||
- **Modules**: TypeScript modules follow similar principles - single responsibility, cohesion
|
||||
- **Testing**: Decoupled classes are easier to test in isolation
|
||||
- **Refactoring**: Breaking large classes maintains cohesion as code evolves
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Many small classes are harder to understand than few large ones
|
||||
**Reality**: The total complexity is the same; small classes make it easier to find and understand relevant code
|
||||
|
||||
- **Myth**: A class with few methods is small enough
|
||||
**Reality**: Size is measured by responsibilities, not method count - 5 methods with 2 responsibilities is too big
|
||||
|
||||
- **Myth**: Encapsulation must never be broken
|
||||
**Reality**: Tests can justify protected access when no other option exists
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| SRP | One reason to change per class |
|
||||
| Cohesion | Methods should share instance variables |
|
||||
| OCP | Extend, don't modify |
|
||||
| DIP | Depend on abstractions |
|
||||
| Organization | Constants, variables, public methods, private methods |
|
||||
147
.agents/skills/typescript-clean-code/references/classes/rules.md
Normal file
147
.agents/skills/typescript-clean-code/references/classes/rules.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Classes Rules
|
||||
|
||||
Guidelines for designing clean, maintainable classes and modules in TypeScript.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Classes Should Be Small (Measured by Responsibilities)
|
||||
|
||||
Size is measured by counting responsibilities, not lines of code.
|
||||
|
||||
- A class should have ONE reason to change
|
||||
- If the class name needs weasel words (`Manager`, `Processor`, `Handler`, `Super`), it's too big
|
||||
- You should describe the class in ~25 words without "if," "and," "or," or "but"
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - multiple responsibilities
|
||||
class SuperDashboard {
|
||||
getLastFocusedComponent(): Component { }
|
||||
setLastFocused(component: Component): void { }
|
||||
getMajorVersionNumber(): number { }
|
||||
getMinorVersionNumber(): number { }
|
||||
getBuildNumber(): number { }
|
||||
}
|
||||
|
||||
// Good - single responsibility extracted
|
||||
class Version {
|
||||
getMajorVersionNumber(): number { }
|
||||
getMinorVersionNumber(): number { }
|
||||
getBuildNumber(): number { }
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Follow the Single Responsibility Principle (SRP)
|
||||
|
||||
Every class should have one, and only one, reason to change.
|
||||
|
||||
- Identify responsibilities by asking "what would cause this class to change?"
|
||||
- Different reasons to change = different classes
|
||||
- Extract responsibilities into their own classes
|
||||
|
||||
### 3. Maintain High Cohesion
|
||||
|
||||
Methods should use the class's instance variables.
|
||||
|
||||
- Each method should manipulate one or more instance variables
|
||||
- If a subset of variables is only used by a subset of methods, consider splitting
|
||||
- When cohesion drops after extracting functions, split the class
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Good - highly cohesive
|
||||
class Stack<T> {
|
||||
private topOfStack = 0;
|
||||
private elements: T[] = [];
|
||||
|
||||
size(): number {
|
||||
return this.topOfStack;
|
||||
}
|
||||
|
||||
push(element: T): void {
|
||||
this.topOfStack++;
|
||||
this.elements.push(element);
|
||||
}
|
||||
|
||||
pop(): T {
|
||||
if (this.topOfStack === 0) throw new Error('Stack is empty');
|
||||
this.topOfStack--;
|
||||
return this.elements.pop()!;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Organize for Change (Open-Closed Principle)
|
||||
|
||||
Structure classes to minimize modification when adding features.
|
||||
|
||||
- Prefer extension over modification
|
||||
- New features should be new classes, not changes to existing ones
|
||||
- Private methods used by only one public method may indicate extraction opportunity
|
||||
|
||||
### 5. Isolate from Change (Dependency Inversion)
|
||||
|
||||
Depend on abstractions, not concrete implementations.
|
||||
|
||||
- Use interfaces to decouple from external dependencies
|
||||
- Inject dependencies through constructors
|
||||
- Enables testing with mocks/stubs
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - depends on concrete class
|
||||
class Portfolio {
|
||||
private exchange = new TokyoStockExchange();
|
||||
|
||||
getValue(): Money { }
|
||||
}
|
||||
|
||||
// Good - depends on abstraction
|
||||
interface StockExchange {
|
||||
currentPrice(symbol: string): Money;
|
||||
}
|
||||
|
||||
class Portfolio {
|
||||
constructor(private exchange: StockExchange) { }
|
||||
|
||||
getValue(): Money { }
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Follow Standard Class Organization
|
||||
|
||||
Order elements consistently within a class.
|
||||
|
||||
- Public static constants first
|
||||
- Private static variables
|
||||
- Private instance variables
|
||||
- Public methods
|
||||
- Private methods (after the public method that calls them)
|
||||
|
||||
## Guidelines
|
||||
|
||||
Less strict recommendations:
|
||||
|
||||
- Prefer many small, single-purpose classes over few large ones
|
||||
- When breaking up functions creates shared variables, consider if those variables define a new class
|
||||
- Tests can justify relaxing encapsulation (protected access), but try other approaches first
|
||||
- A class that is "logically complete" with no anticipated changes can stay as-is
|
||||
|
||||
## Exceptions
|
||||
|
||||
When these rules may be relaxed:
|
||||
|
||||
- **Testing**: Make methods/properties protected when tests need access (last resort)
|
||||
- **Stable code**: Don't preemptively split a class if no changes are anticipated
|
||||
- **Simple utilities**: Pure utility functions may stay together if truly cohesive
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Small classes | Measure by responsibilities, not lines |
|
||||
| SRP | One reason to change |
|
||||
| Cohesion | Methods share instance variables |
|
||||
| OCP | Extend, don't modify |
|
||||
| DIP | Depend on abstractions |
|
||||
| Organization | Constants > variables > public > private |
|
||||
@@ -0,0 +1,195 @@
|
||||
# Coding Practices Examples
|
||||
|
||||
Scenarios demonstrating professional vs unprofessional coding behavior.
|
||||
|
||||
## Bad Scenarios
|
||||
|
||||
### The 3 AM Hero
|
||||
|
||||
**Situation**: Developer works 18-hour days, 60-70 hour weeks, feeling dedicated and professional. At 3 AM, solves a timing problem by having code send messages to itself.
|
||||
|
||||
**What happened**:
|
||||
- The "solution" instituted a faulty design structure
|
||||
- Everyone had to work around it constantly
|
||||
- Caused strange timing errors and infinite loops
|
||||
- Became team cruft surrounded by workarounds
|
||||
- Years of accumulated patches and side effects
|
||||
|
||||
**Problems**:
|
||||
- Tired brain chose wrong solution that "looked good enough"
|
||||
- Confusion of long hours with professionalism
|
||||
- Technical debt that lasted years
|
||||
- Team morale impact (became a running joke)
|
||||
|
||||
**Lesson**: Dedication is about discipline, not hours. Eight good hours beats eighteen exhausted ones.
|
||||
|
||||
---
|
||||
|
||||
### The Worried Coder
|
||||
|
||||
**Situation**: Developer has argument with spouse, then tries to code. Sits with eyes on screen, fingers on keyboard, doing nothing.
|
||||
|
||||
**What happened**:
|
||||
- Background process runs in mind reviewing the argument
|
||||
- Physical stress felt in chest and stomach
|
||||
- Forces self to write a line or two, but can't sustain
|
||||
- Descends into "stupefied insensibility"
|
||||
- Any code produced is trash
|
||||
|
||||
**Problems**:
|
||||
- Wasted time producing nothing
|
||||
- Code produced needs rework
|
||||
- Prolonged personal issue by not addressing it
|
||||
|
||||
**Better approach**: Spend a dedicated hour addressing the worry, then return to coding with clearer mind.
|
||||
|
||||
---
|
||||
|
||||
### The Zone Addict
|
||||
|
||||
**Situation**: Developer measures self-worth by time spent in the Zone, enjoying the euphoria and sense of conquest.
|
||||
|
||||
**What happened**:
|
||||
- Wrote more code, went through TDD loops faster
|
||||
- Lost big-picture perspective
|
||||
- Made decisions that needed reversal later
|
||||
- Snapped at colleagues who interrupted
|
||||
|
||||
**Problems**:
|
||||
- Net productivity lower due to rework
|
||||
- Damaged team relationships
|
||||
- Missed better design solutions
|
||||
|
||||
**Better approach**: Walk away when feeling the Zone coming on; find a pair partner.
|
||||
|
||||
---
|
||||
|
||||
### The Hope-Based Estimate
|
||||
|
||||
**Situation**: Trade show in 10 days. Developer's three-number estimate is 8/12/20 days. Developer hopes to make it in 10.
|
||||
|
||||
**What happened**:
|
||||
- Didn't communicate reality to stakeholders
|
||||
- Took shortcuts, worked extra hours
|
||||
- Team had false hope
|
||||
- Delayed necessary tough decisions
|
||||
- Missed deadline anyway
|
||||
|
||||
**Problems**:
|
||||
- Hope destroyed schedule and reputation
|
||||
- Everyone avoided facing the real issue
|
||||
- No fall-back plan was created
|
||||
- Rushed code created technical debt
|
||||
|
||||
**Better approach**: Immediately communicate the 12-day nominal estimate, request scope reduction or fall-back plan.
|
||||
|
||||
---
|
||||
|
||||
### The False Delivery
|
||||
|
||||
**Situation**: Pressure to show progress. Developer convinces self that feature is "done enough" and moves to next task.
|
||||
|
||||
**What happened**:
|
||||
- Practice became contagious on team
|
||||
- Definition of "done" stretched further and further
|
||||
- One team defined "done" as "checked-in" (didn't have to compile)
|
||||
- Managers heard everything was fine
|
||||
- "Freight train of unfinished work" hit the team
|
||||
|
||||
**Problems**:
|
||||
- Status reports became fiction
|
||||
- No visibility into real project state
|
||||
- Catastrophic surprise when truth emerged
|
||||
|
||||
**Better approach**: Create independent definition of "done" with automated acceptance tests.
|
||||
|
||||
---
|
||||
|
||||
## Good Scenarios
|
||||
|
||||
### The Partitioned Worrier
|
||||
|
||||
**Situation**: Developer has financial worries nagging during work.
|
||||
|
||||
**What they did**:
|
||||
- Recognized the background process consuming focus
|
||||
- Dedicated one hour specifically to financial planning
|
||||
- Didn't solve the problem but reduced anxiety
|
||||
- Returned to coding with quieted background process
|
||||
- Produced quality code for rest of day
|
||||
|
||||
**Why it works**:
|
||||
- Acknowledges human reality
|
||||
- One hour of worry-time beats full day of half-focused coding
|
||||
- Professional allocation of mental resources
|
||||
|
||||
---
|
||||
|
||||
### The Gracious Interruptee
|
||||
|
||||
**Situation**: Developer deep in complex problem when colleague asks for help.
|
||||
|
||||
**What they did**:
|
||||
- Stopped what they were doing politely
|
||||
- Helped colleague work through their issue
|
||||
- Spent 30 minutes pairing on the problem
|
||||
- Returned to own work refreshed with new perspective
|
||||
- Used failing test to reconstruct context
|
||||
|
||||
**Why it works**:
|
||||
- Professional ethics require mutual help
|
||||
- Fresh perspective often solves problems quickly
|
||||
- TDD maintained context for return
|
||||
- Built team trust and collaboration
|
||||
|
||||
---
|
||||
|
||||
### The Honest Estimator
|
||||
|
||||
**Situation**: Manager asks developer to "try" to make impossible deadline.
|
||||
|
||||
**What they did**:
|
||||
- Held to original estimates
|
||||
- Presented three-number estimate (best/nominal/worst)
|
||||
- Suggested scope reduction as only viable option
|
||||
|
||||
**Why it works**: Original estimates more accurate than pressure-modified ones; no false hope.
|
||||
|
||||
---
|
||||
|
||||
### The Block Breaker
|
||||
|
||||
**Situation**: Developer sits at workstation but code won't come.
|
||||
|
||||
**What they did**:
|
||||
- Recognized writer's block symptoms
|
||||
- Found a pair partner
|
||||
- Felt the physiological change that breaks blockage
|
||||
|
||||
**Why it works**: Pairing has chemical/physiological effect on brain that breaks mental logjams.
|
||||
|
||||
---
|
||||
|
||||
### The Strategic Walker
|
||||
|
||||
**Situation**: Developer stuck on problem late in the day, tempted to stay until solved.
|
||||
|
||||
**What they did**:
|
||||
- Went home at normal time instead of pushing through
|
||||
- Let subconscious work on problem overnight
|
||||
- Solved problem in shower next morning
|
||||
|
||||
**Why it works**: Disengagement allows creative hunting; fresh perspective sees options intensity missed.
|
||||
|
||||
---
|
||||
|
||||
## Key Patterns
|
||||
|
||||
| Scenario | Unprofessional | Professional |
|
||||
|----------|---------------|--------------|
|
||||
| Tired | Push through, "heroic" hours | Go home, get sleep |
|
||||
| Worried | Force yourself to code | Partition time for the worry |
|
||||
| In Zone | Stay there, feel productive | Walk away, find partner |
|
||||
| Interrupted | Snap, glare, protect turf | Help graciously, use TDD for context |
|
||||
| Behind schedule | Hope, rush, false delivery | Honest estimates, reduce scope |
|
||||
| Stuck | Stay stuck, avoid asking | Ask for help, pair up |
|
||||
@@ -0,0 +1,103 @@
|
||||
# Coding Practices Knowledge
|
||||
|
||||
Core concepts and foundational understanding for the mental and behavioral aspects of professional coding.
|
||||
|
||||
## Overview
|
||||
|
||||
Coding is an intellectually challenging activity requiring concentration, focus, and the right mental state. Professional coding is about behavior, mood, and attitude while writing code - not just the code itself. Understanding these mental aspects is essential for sustainable, high-quality output.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Preparedness
|
||||
|
||||
**Definition**: The mental and physical readiness required to write quality code.
|
||||
|
||||
Coding requires juggling multiple concerns simultaneously: making code work, solving customer problems, fitting into existing systems, and maintaining readability.
|
||||
|
||||
**Key points**:
|
||||
- Code must work and faithfully represent the solution
|
||||
- Code must solve the customer's true needs (not just stated requirements)
|
||||
- Code must follow solid engineering principles
|
||||
- Code must be readable and reveal intent
|
||||
|
||||
### The Flow Zone
|
||||
|
||||
**Definition**: A hyper-focused, tunnel-vision state of consciousness during coding that feels productive but diminishes rational faculties.
|
||||
|
||||
**Key points**:
|
||||
- You write more code in the Zone, but lose big-picture thinking
|
||||
- Decisions made in Zone often need reversal later
|
||||
- The Zone is a mild meditative state, not true hyper-productivity
|
||||
- The feeling of productivity is deceptive
|
||||
|
||||
### Writer's Block
|
||||
|
||||
**Definition**: The inability to produce code despite sitting at the workstation, often caused by fatigue, worry, fear, or depression.
|
||||
|
||||
**Key points**:
|
||||
- Manifests as avoiding the keyboard through busy work
|
||||
- Pair programming is the most effective solution
|
||||
- There's a physiological change when working with others
|
||||
- Creative input helps prevent blockage
|
||||
|
||||
### Pacing
|
||||
|
||||
**Definition**: The practice of conserving energy and creativity over time, treating software development as a marathon rather than sprint.
|
||||
|
||||
**Key points**:
|
||||
- Creativity and intelligence are fleeting states
|
||||
- Tired minds produce poor solutions
|
||||
- Disengagement allows creative subconscious to work
|
||||
- Learn your personal patterns of creativity
|
||||
|
||||
### Debugging Mindset
|
||||
|
||||
**Definition**: The professional attitude toward debugging as expensive coding time that should be minimized, not accepted as inevitable.
|
||||
|
||||
**Key points**:
|
||||
- Debugging time is as expensive as coding time
|
||||
- Professionals work to reduce debugging time toward zero
|
||||
- TDD can reduce debugging time by a factor of ten
|
||||
- Creating bugs frequently is unprofessional behavior
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Error-sense | The ability to feel when you're making a mistake |
|
||||
| Confidence | Trust in your abilities that enables flow without constant verification |
|
||||
| Background process | Mental processing of worries that consumes focus |
|
||||
| Creative input | External stimulation that primes creative output |
|
||||
| False delivery | Claiming work is done when it isn't |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Time Management**: Knowing when to code and when to step away
|
||||
- **Collaboration**: Pair programming as solution for blocks and interruptions
|
||||
- **Estimation**: Being honest about progress and avoiding hope-based estimates
|
||||
- **Professionalism**: Taking responsibility for debugging time and quality
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: The Zone is the optimal state for programming
|
||||
**Reality**: The Zone diminishes rational faculties; code written there often needs rework
|
||||
|
||||
- **Myth**: Long hours show dedication and professionalism
|
||||
**Reality**: Professionalism is about discipline, not hours; 3 AM code creates long-term problems
|
||||
|
||||
- **Myth**: Interruptions are always bad for productivity
|
||||
**Reality**: Being helpful to others is professional; rudeness often stems from Zone attachment
|
||||
|
||||
- **Myth**: You can rush to meet deadlines
|
||||
**Reality**: You cannot make yourself code faster; rushing creates messes that slow everyone
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Preparedness | Must be mentally ready before coding; distracted coding creates waste |
|
||||
| Flow Zone | Avoid it - feels productive but loses big picture |
|
||||
| Writer's Block | Solved by pair programming and creative input |
|
||||
| Pacing | Conserve energy; walk away when stuck |
|
||||
| Debugging | Expensive time that professionals minimize |
|
||||
| Hope | Project killer; never incorporate into estimates |
|
||||
@@ -0,0 +1,153 @@
|
||||
# Coding Practices Rules
|
||||
|
||||
Rules for professional behavior, mindset, and practices while coding.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Never Code When Impaired
|
||||
|
||||
**Don't write code when you are tired, worried, or distracted.**
|
||||
|
||||
- Tired coding produces bugs, wrong structure, and opaque solutions
|
||||
- Code written at 3 AM will haunt you for years
|
||||
- Worry creates a background process that consumes focus
|
||||
- Any code produced while impaired will need rework
|
||||
|
||||
**Action**: Find a way to eliminate distractions and settle your mind first.
|
||||
|
||||
### 2. Avoid the Flow Zone
|
||||
|
||||
**Deliberately exit the Zone when you feel yourself slipping into it.**
|
||||
|
||||
- Walk away for a few minutes
|
||||
- Answer emails, check messages
|
||||
- Take a lunch break
|
||||
- Find a pair partner (pairing blocks Zone entry)
|
||||
|
||||
**Why**: Zone code comes out faster but needs more revisiting.
|
||||
|
||||
### 3. Handle Interruptions Gracefully
|
||||
|
||||
**Respond to interruptions with polite willingness to help.**
|
||||
|
||||
- Don't snap or glare at people asking questions
|
||||
- Treat others as you'd want to be treated when stuck
|
||||
- Use pair programming to maintain context during interruptions
|
||||
- Use TDD - failing tests hold your context
|
||||
|
||||
**Remember**: Next time you may need to interrupt someone else.
|
||||
|
||||
### 4. Seek Creative Input
|
||||
|
||||
**Feed your creativity to prevent blockages.**
|
||||
|
||||
- Read broadly (software, science, fiction, varied topics)
|
||||
- Find what works for you (novels, movies, music)
|
||||
- Escapism combined with creative stimulation fuels output
|
||||
- Creativity breeds creativity
|
||||
|
||||
### 5. Minimize Debugging Time
|
||||
|
||||
**Treat debugging time as expensive coding time to be reduced.**
|
||||
|
||||
- Adopt TDD or equivalent discipline
|
||||
- Aim for zero debugging time (asymptotic goal)
|
||||
- Creating many bugs is unprofessional
|
||||
- Track and measure debugging time
|
||||
|
||||
### 6. Know When to Walk Away
|
||||
|
||||
**Disengage when stuck or tired.**
|
||||
|
||||
- Can't solve a problem? Go home anyway
|
||||
- Pounding a nonfunctioning brain wastes time
|
||||
- Give your creative subconscious a chance
|
||||
- Solutions come in showers, cars, and off-hours
|
||||
|
||||
### 7. Never Incorporate Hope Into Estimates
|
||||
|
||||
**Be honest about schedule reality.**
|
||||
|
||||
- Provide three estimates: best case, nominal, worst case
|
||||
- Update estimates daily with fact-based data
|
||||
- Hope destroys schedules and ruins reputations
|
||||
- Make sure everyone understands the situation
|
||||
|
||||
### 8. Don't Rush
|
||||
|
||||
**Hold to your original estimates under pressure.**
|
||||
|
||||
- You cannot make yourself code faster
|
||||
- Rushing creates messes that slow everyone
|
||||
- Shortcuts and extra hours provide false hope
|
||||
- Reduce scope instead of promising miracles
|
||||
|
||||
### 9. Agree to Overtime Only Conditionally
|
||||
|
||||
**Accept overtime only when all three conditions are met:**
|
||||
|
||||
1. You can personally afford it
|
||||
2. It is short term (two weeks or less)
|
||||
3. Your boss has a fall-back plan if overtime fails
|
||||
|
||||
**Note**: 20% more hours does not yield 20% more work.
|
||||
|
||||
### 10. Never Falsely Deliver
|
||||
|
||||
**Don't say you're done when you aren't.**
|
||||
|
||||
- Overt lies are bad; rationalized definitions of "done" are worse
|
||||
- False delivery is contagious on teams
|
||||
- Create independent definition of "done" with automated acceptance tests
|
||||
- "Checked-in" does not mean "done"
|
||||
|
||||
### 11. Help Others and Accept Help
|
||||
|
||||
**Make yourself available; accept help graciously.**
|
||||
|
||||
- Sequestering yourself is a violation of professional ethics
|
||||
- Set specific available hours if you need alone time
|
||||
- When stuck, ask for help - remaining stuck is unprofessional
|
||||
- Give help sessions at least 30 minutes; accept help for 30 minutes before excusing yourself
|
||||
|
||||
### 12. Partition Worry Time
|
||||
|
||||
**Deal with personal worries deliberately, not during coding.**
|
||||
|
||||
- Dedicate a block of time to the worry (perhaps an hour)
|
||||
- Call home, address financial issues, talk through arguments
|
||||
- Reduce anxiety to quiet the background process
|
||||
- Ideally handle on personal time; if at office, better than forcing bad code
|
||||
|
||||
## Guidelines
|
||||
|
||||
Less strict recommendations:
|
||||
|
||||
- Be suspicious of music while coding - it may just help you enter the Zone
|
||||
- Use pair programming as first resort for writer's block
|
||||
- Learn your patterns of creativity and brilliance
|
||||
- Recognize that the drive home and shower are problem-solving time
|
||||
- Mentor junior developers; seek mentoring from seniors
|
||||
|
||||
## Exceptions
|
||||
|
||||
When these rules may be relaxed:
|
||||
|
||||
- **Zone for practice**: The Zone is fine during deliberate practice sessions (katas, exercises)
|
||||
- **Short overtime**: Acceptable for genuine short-term crunches with fall-back plans
|
||||
- **Urgent production issues**: May need to code tired, but expect to revisit the code
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| 3 AM Code | Never code when tired - produces lasting problems |
|
||||
| Worry Code | Resolve worries first or partition time for them |
|
||||
| Flow Zone | Avoid it - walk away when you feel it coming |
|
||||
| Interruptions | Be politely helpful; rudeness is unprofessional |
|
||||
| Walking Away | Do it when stuck; subconscious will work |
|
||||
| Hope | Never include in estimates; it kills projects |
|
||||
| Rushing | Impossible; reduces scope instead |
|
||||
| Overtime | Only if affordable, short, and has fall-back |
|
||||
| False Delivery | Never; define "done" with automated tests |
|
||||
| Helping | Honor bound to offer and accept help |
|
||||
@@ -0,0 +1,148 @@
|
||||
# Collaboration Examples
|
||||
|
||||
Scenarios demonstrating good and bad collaboration patterns in software teams.
|
||||
|
||||
## Bad Scenarios
|
||||
|
||||
### The Printer Company Fiasco
|
||||
|
||||
**Situation**: A company built high-end printers with many components (feeders, printers, stackers, staplers, cutters).
|
||||
|
||||
**What happened**:
|
||||
- Each programmer worked on "their" device
|
||||
- One person owned the feeder code, another owned the stapler code
|
||||
- Each kept their technology to themselves
|
||||
- No one could touch anyone else's code
|
||||
- Political clout was tied to how much the business valued each device
|
||||
- The printer programmer was "unassailable"
|
||||
- Salary reviews were tied to device importance
|
||||
|
||||
**Problems observed**:
|
||||
- Massive duplication across the codebase
|
||||
- Completely skewed interfaces between modules
|
||||
- No amount of consulting could convince them to change
|
||||
- Business incentives reinforced bad technical practices
|
||||
|
||||
**Root cause**: Owned code culture backed by misaligned incentives.
|
||||
|
||||
---
|
||||
|
||||
### The Cubicle Corner Anti-Pattern
|
||||
|
||||
**Situation**: A team of programmers working in a modern office.
|
||||
|
||||
**What it looks like**:
|
||||
- Programmers sitting in cubicle corners
|
||||
- Backs turned to each other
|
||||
- Staring at screens
|
||||
- Wearing headphones all day
|
||||
- No spontaneous conversation
|
||||
|
||||
**Problems**:
|
||||
- No serendipitous communication
|
||||
- Can't sense when teammates are struggling
|
||||
- No knowledge sharing happening
|
||||
- Not actually functioning as a team
|
||||
- Might as well be remote contractors
|
||||
|
||||
**The metaphor**: "Rubbing cerebellums" - the cerebellum is at the back of the brain, so to rub them you'd face away from each other. That's not collaboration.
|
||||
|
||||
---
|
||||
|
||||
### The "I Work Better Alone" Fallacy
|
||||
|
||||
**Situation**: A developer believes they are more productive working solo.
|
||||
|
||||
**The problem**:
|
||||
- Even if individually true, the *team* doesn't work better
|
||||
- Creates knowledge silos
|
||||
- No code review happening
|
||||
- When that person is sick or leaves, their code is a mystery
|
||||
- Other team members can't help or contribute
|
||||
|
||||
**Reality check**: Programming is about working with people, not avoiding them.
|
||||
|
||||
## Good Scenarios
|
||||
|
||||
### Collective Ownership in Practice
|
||||
|
||||
**Situation**: A team that practices true collective ownership.
|
||||
|
||||
**What it looks like**:
|
||||
- Any team member can check out any module
|
||||
- Changes are made where needed, not based on "ownership"
|
||||
- Team members learn by working on unfamiliar parts
|
||||
- No single points of failure in knowledge
|
||||
- Code reviews happen naturally through pairing
|
||||
|
||||
**Benefits**:
|
||||
- Less duplication (multiple eyes see patterns)
|
||||
- Better interfaces (designed for team, not individual)
|
||||
- Resilient team (anyone can cover for anyone)
|
||||
- Continuous learning built into daily work
|
||||
|
||||
---
|
||||
|
||||
### Effective Pairing
|
||||
|
||||
**Situation**: Two developers working together on a challenging problem.
|
||||
|
||||
**What it looks like**:
|
||||
- Sharing a workstation (or screen in remote)
|
||||
- Discussing approaches before coding
|
||||
- One types while other reviews in real-time
|
||||
- Frequent role swaps
|
||||
- Knowledge transfers naturally
|
||||
|
||||
**Why it works**:
|
||||
- "Two heads are better than one"
|
||||
- Real-time code review (most efficient form)
|
||||
- Knowledge sharing happens automatically
|
||||
- No code goes unreviewed
|
||||
- Both can now work on this part of the system
|
||||
|
||||
**When to pair**:
|
||||
- Complex problems (most efficient approach)
|
||||
- Unfamiliar code areas (learning opportunity)
|
||||
- Critical features (need the review)
|
||||
- Emergency fixes (why do we pair then but not normally?)
|
||||
|
||||
---
|
||||
|
||||
### The Collaborative Workspace
|
||||
|
||||
**Situation**: A team arranged for maximum collaboration.
|
||||
|
||||
**What it looks like**:
|
||||
- Team members sitting around tables
|
||||
- Facing each other, not walls
|
||||
- Can see body language and facial expressions
|
||||
- Overhear when someone is frustrated
|
||||
- Spontaneous conversations happen naturally
|
||||
- Headphones off most of the time
|
||||
|
||||
**Benefits**:
|
||||
- Serendipitous communication
|
||||
- Problems surface quickly
|
||||
- Help offered before it's requested
|
||||
- Team feels like a unit
|
||||
- Shared context and awareness
|
||||
|
||||
## Comparison Table
|
||||
|
||||
| Aspect | Bad Pattern | Good Pattern |
|
||||
|--------|-------------|--------------|
|
||||
| Code Ownership | Individual ("my code") | Collective (team code) |
|
||||
| Physical Setup | Corners, backs turned | Tables, facing each other |
|
||||
| Knowledge | Silos, single experts | Shared, everyone learns |
|
||||
| Code Review | Rare, formal, after-the-fact | Constant through pairing |
|
||||
| Communication | Scheduled meetings only | Continuous, serendipitous |
|
||||
| Incentives | Tied to individual ownership | Tied to team outcomes |
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
1. **Owned code is a disaster** - It creates duplication, bad interfaces, and political dysfunction
|
||||
2. **Collective ownership works** - When everyone can work anywhere, the code improves
|
||||
3. **Pairing is efficient** - If it works in emergencies, it works for regular work
|
||||
4. **Physical collaboration matters** - Face each other, communicate constantly
|
||||
5. **Programming is about people** - Accept it and learn to work well with others
|
||||
@@ -0,0 +1,97 @@
|
||||
# Collaboration Knowledge
|
||||
|
||||
Core concepts and foundational understanding for team collaboration in software development.
|
||||
|
||||
## Overview
|
||||
|
||||
Software is created by teams, and teams are most effective when members collaborate professionally. Being a loner or recluse on a team is unprofessional. Programming is fundamentally about working with people - both with business stakeholders and with fellow developers.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Owned Code
|
||||
|
||||
**Definition**: A dysfunctional pattern where each programmer builds walls around "their" code and refuses to let others touch or even see it.
|
||||
|
||||
This creates silos where political clout is tied to code ownership rather than team success.
|
||||
|
||||
**Key problems**:
|
||||
- Massive code duplication across modules
|
||||
- Skewed interfaces between components
|
||||
- Knowledge silos that make the team fragile
|
||||
- Political dynamics that hinder technical improvement
|
||||
|
||||
### Collective Ownership
|
||||
|
||||
**Definition**: A healthy team pattern where all team members own all the code, and anyone can check out any module and make appropriate changes.
|
||||
|
||||
Professional developers do not prevent others from working in the code. They work with each other on as much of the system as they can.
|
||||
|
||||
**Key points**:
|
||||
- The *team* owns the code, not individuals
|
||||
- No walls of ownership around code
|
||||
- Team members learn from each other by working together
|
||||
- Everyone can work on any part of the system
|
||||
|
||||
### Pairing
|
||||
|
||||
**Definition**: Two programmers working together on the same code, sharing a workstation and collaborating in real-time.
|
||||
|
||||
Professionals pair because it is often the most efficient way to solve problems. Most programmers will pair in emergencies - because "two heads are better than one."
|
||||
|
||||
**Key points**:
|
||||
- Best way to share knowledge across the team
|
||||
- Most efficient and effective form of code review
|
||||
- Prevents knowledge silos
|
||||
- Ensures all team members can play any position in a pinch
|
||||
|
||||
### Cerebellums (Working Together)
|
||||
|
||||
**Definition**: The importance of physical proximity and face-to-face collaboration rather than isolated work.
|
||||
|
||||
Teams need serendipitous communication - both verbal and body language. Working in isolation (facing corners, wearing headphones) prevents true teamwork.
|
||||
|
||||
**Key points**:
|
||||
- Sit around tables facing each other
|
||||
- Enable overhearing and spontaneous communication
|
||||
- Communicate as a unit, not as individuals
|
||||
- Physical presence matters for team cohesion
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Owned Code | Dysfunctional pattern of individual code ownership |
|
||||
| Collective Ownership | Team-wide shared responsibility for all code |
|
||||
| Pairing | Two programmers collaborating on the same code |
|
||||
| Knowledge Silo | When only one person understands a part of the system |
|
||||
| Serendipitous Communication | Unplanned, spontaneous information sharing |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Code Quality**: Collective ownership prevents duplication and improves interfaces
|
||||
- **Code Review**: Pairing is the most effective form of review
|
||||
- **Team Resilience**: No single points of failure when everyone can work on any code
|
||||
- **Professional Development**: Learning from each other by working together
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: I work better when I work alone
|
||||
**Reality**: Even if true for you individually, the team doesn't work better when you work alone
|
||||
|
||||
- **Myth**: Pairing is only for emergencies
|
||||
**Reality**: If pairing is most efficient in emergencies, it's efficient for regular work too
|
||||
|
||||
- **Myth**: Code ownership creates accountability
|
||||
**Reality**: It creates silos, duplication, and political dynamics that harm the codebase
|
||||
|
||||
- **Myth**: Programmers don't need to work with people
|
||||
**Reality**: Programming is fundamentally about working with people - business and teammates
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Owned Code | Anti-pattern where individuals wall off "their" code |
|
||||
| Collective Ownership | Team owns all code; anyone can modify any module |
|
||||
| Pairing | Two programmers working together - efficient and educational |
|
||||
| Working Together | Face each other, communicate, don't isolate in corners |
|
||||
@@ -0,0 +1,78 @@
|
||||
# Collaboration Rules
|
||||
|
||||
Rules for professional collaboration between developers and with business stakeholders.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Practice Collective Code Ownership
|
||||
|
||||
Break down all walls of code ownership and have the team own all the code.
|
||||
|
||||
- Any team member can check out any module
|
||||
- Any team member can make changes they think are appropriate
|
||||
- The *team* owns the code, not individuals
|
||||
- Do not prevent others from working in the code
|
||||
|
||||
### 2. Do Not Build Walls Around Code
|
||||
|
||||
Professional developers do not build walls of ownership around code.
|
||||
|
||||
- Don't refuse to let others touch "your" code
|
||||
- Don't hide your code from other programmers
|
||||
- Don't tie your identity or status to particular modules
|
||||
- Work with each other on as much of the system as possible
|
||||
|
||||
### 3. Pair with Other Programmers
|
||||
|
||||
Professionals pair because it is often the most efficient way to solve problems.
|
||||
|
||||
- Pair to share knowledge with each other
|
||||
- Pair to review code collaboratively (most effective review method)
|
||||
- Pair to ensure no knowledge silos exist
|
||||
- All team members should be able to play another position in a pinch
|
||||
|
||||
### 4. Work Together Physically
|
||||
|
||||
Face each other, communicate constantly, work as a unit.
|
||||
|
||||
- Sit around tables facing each other, not in corners
|
||||
- Don't isolate with headphones all day
|
||||
- Enable serendipitous communication (verbal and body language)
|
||||
- Overhear frustrated mutterings, sense the team's state
|
||||
|
||||
### 5. Review All Code
|
||||
|
||||
No system should consist of code that hasn't been reviewed by other programmers.
|
||||
|
||||
- Pairing is the most efficient code review method
|
||||
- Collaboration in writing code is better than after-the-fact review
|
||||
- Every piece of code needs another set of eyes
|
||||
|
||||
## Guidelines
|
||||
|
||||
Less strict recommendations:
|
||||
|
||||
- Learn different parts of the system by pairing with others
|
||||
- Learn the business domain by working with teammates who know it
|
||||
- Recognize that all team members have a primary position but should be versatile
|
||||
- Accept that programming is fundamentally about working with people
|
||||
|
||||
## Exceptions
|
||||
|
||||
When these rules may be relaxed:
|
||||
|
||||
- **Deep thinking time**: Sometimes you need to think long and hard about a problem alone
|
||||
- **Trivial tasks**: When the task is so simple that another person would be wasted
|
||||
- **Personal focus time**: Short periods of solo work are acceptable within a collaborative culture
|
||||
|
||||
However, "in general, it is best to collaborate closely with others and to pair with them a large fraction of the time."
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Collective Ownership | Team owns all code; anyone can modify anything |
|
||||
| No Walls | Don't hide or protect "your" code from teammates |
|
||||
| Pair Regularly | Best way to share knowledge and review code |
|
||||
| Face Each Other | Physical proximity enables team communication |
|
||||
| Review Everything | All code needs review; pairing is most efficient |
|
||||
@@ -0,0 +1,241 @@
|
||||
# Comments Examples
|
||||
|
||||
Code examples demonstrating comment principles.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Explaining What Instead of Refactoring
|
||||
|
||||
```typescript
|
||||
// Check to see if the employee is eligible for full benefits
|
||||
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Comment explains WHAT, not WHY
|
||||
- Code could be made self-explanatory
|
||||
|
||||
### Redundant Comment
|
||||
|
||||
```typescript
|
||||
// Utility method that returns when this.closed is true.
|
||||
// Throws an exception if the timeout is reached.
|
||||
async waitForClose(timeoutMillis: number): Promise<void> {
|
||||
if (!this.closed) {
|
||||
await this.wait(timeoutMillis);
|
||||
if (!this.closed) {
|
||||
throw new Error('Could not be closed');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Comment is less precise than code
|
||||
- Takes longer to read than the code itself
|
||||
- Actually misleading (returns IF closed, not WHEN)
|
||||
|
||||
### Noise Comments
|
||||
|
||||
```typescript
|
||||
/** The processor delay for this component */
|
||||
protected backgroundProcessorDelay = -1;
|
||||
|
||||
/** The lifecycle event support for this component */
|
||||
protected lifecycle = new LifecycleSupport(this);
|
||||
|
||||
/** The container event listeners for this Container */
|
||||
protected listeners: EventListener[] = [];
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Comments add zero information
|
||||
- Variable names already describe purpose
|
||||
- Creates clutter that obscures code
|
||||
|
||||
### Commented-Out Code
|
||||
|
||||
```typescript
|
||||
const response = new InputStreamResponse();
|
||||
response.setBody(formatter.getResultStream(), formatter.getByteCount());
|
||||
// const resultsStream = formatter.getResultStream();
|
||||
// const reader = new StreamReader(resultsStream);
|
||||
// response.setContent(reader.read(formatter.getByteCount()));
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- No one will delete it (assumes it's important)
|
||||
- Gathers like sediment over time
|
||||
- Version control already preserves history
|
||||
|
||||
### Frustration Venting
|
||||
|
||||
```typescript
|
||||
catch (e) {
|
||||
// Give me a break!
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Provides no useful information
|
||||
- Signals need for refactoring, not commenting
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Explanation of Intent
|
||||
|
||||
```typescript
|
||||
compareTo(other: unknown): number {
|
||||
if (other instanceof WikiPagePath) {
|
||||
const compressedName = this.names.join('');
|
||||
const compressedOther = other.names.join('');
|
||||
return compressedName.localeCompare(compressedOther);
|
||||
}
|
||||
return 1; // We are greater because we are the right type
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Explains the decision, not the mechanics
|
||||
- Documents non-obvious business logic
|
||||
|
||||
### Warning of Consequences
|
||||
|
||||
```typescript
|
||||
// Intl.DateTimeFormat caches results per locale, but options vary
|
||||
// Create new instance to avoid stale format conflicts
|
||||
function makeStandardHttpDateFormat(): Intl.DateTimeFormat {
|
||||
return new Intl.DateTimeFormat('en-US', {
|
||||
weekday: 'short',
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
timeZone: 'GMT'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Prevents future "optimization" that would break code
|
||||
- Explains WHY, not WHAT
|
||||
|
||||
### Clarifying External Library Returns
|
||||
|
||||
```typescript
|
||||
test('compareTo ordering', () => {
|
||||
const a = PathParser.parse('PageA');
|
||||
const b = PathParser.parse('PageB');
|
||||
const ab = PathParser.parse('PageA.PageB');
|
||||
|
||||
expect(a.compareTo(a)).toBe(0); // a == a
|
||||
expect(a.compareTo(b)).toBe(-1); // a < b
|
||||
expect(b.compareTo(a)).toBe(1); // b > a
|
||||
expect(ab.compareTo(a)).toBe(1); // ab > a (child > parent)
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Makes test assertions readable
|
||||
- Clarifies comparison semantics
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before
|
||||
|
||||
```typescript
|
||||
// Check to see if the employee is eligible for full benefits
|
||||
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```typescript
|
||||
if (employee.isEligibleForFullBenefits())
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. Extracted condition to descriptive method name
|
||||
2. Comment became unnecessary - code speaks for itself
|
||||
3. Logic now reusable across codebase
|
||||
|
||||
---
|
||||
|
||||
### Before
|
||||
|
||||
```typescript
|
||||
// does the module from the global list <mod> depend on the
|
||||
// subsystem we are part of?
|
||||
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```typescript
|
||||
const moduleDependencies = smodule.getDependSubsystems();
|
||||
const ourSubSystem = subSysMod.getSubSystem();
|
||||
if (moduleDependencies.includes(ourSubSystem))
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. Extracted intermediate variables with descriptive names
|
||||
2. Comment became redundant
|
||||
3. Code now reads as English
|
||||
|
||||
---
|
||||
|
||||
### Before (with frustration comment)
|
||||
|
||||
```typescript
|
||||
private startSending(): void {
|
||||
try {
|
||||
this.doSending();
|
||||
} catch (e) {
|
||||
if (e instanceof SocketException) {
|
||||
// normal. someone stopped the request.
|
||||
} else {
|
||||
try {
|
||||
this.response.add(ErrorResponder.makeExceptionString(e));
|
||||
this.response.closeAll();
|
||||
} catch {
|
||||
// Give me a break!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```typescript
|
||||
private startSending(): void {
|
||||
try {
|
||||
this.doSending();
|
||||
} catch (e) {
|
||||
if (e instanceof SocketException) {
|
||||
// Expected when client disconnects mid-request
|
||||
return;
|
||||
}
|
||||
this.addExceptionAndCloseResponse(e);
|
||||
}
|
||||
}
|
||||
|
||||
private addExceptionAndCloseResponse(e: Error): void {
|
||||
try {
|
||||
this.response.add(ErrorResponder.makeExceptionString(e));
|
||||
this.response.closeAll();
|
||||
} catch {
|
||||
// Response already closed, safe to ignore
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. Extracted nested try/catch to separate method
|
||||
2. Replaced frustrated comment with meaningful one
|
||||
3. Reduced complexity, improved readability
|
||||
@@ -0,0 +1,86 @@
|
||||
# Comments Knowledge
|
||||
|
||||
Core concepts and foundational understanding for code comments.
|
||||
|
||||
## Overview
|
||||
|
||||
Comments are, at best, a necessary evil used to compensate for our failure to express intent in code. The proper use of comments is to explain what cannot be made clear through code alone. Truth can only be found in the code itself.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Comments as Failure
|
||||
|
||||
**Definition**: Every comment represents a failure to express yourself clearly in code.
|
||||
|
||||
When you write a comment, it means the code wasn't expressive enough on its own. The goal should be to minimize comments by writing clearer, more self-documenting code.
|
||||
|
||||
**Key points**:
|
||||
- Comments should trigger reflection: "Can I express this in code instead?"
|
||||
- Energy spent on comments is better spent improving code clarity
|
||||
- Success is measured by how few comments are needed
|
||||
|
||||
### Comment Decay
|
||||
|
||||
**Definition**: Comments become increasingly inaccurate over time as code evolves.
|
||||
|
||||
Code changes and moves, but comments often don't follow. Comments become "orphaned blurbs of ever-decreasing accuracy."
|
||||
|
||||
**Key points**:
|
||||
- Comments lie - not intentionally, but inevitably
|
||||
- The older and farther from code, the more likely to be wrong
|
||||
- Inaccurate comments are worse than no comments
|
||||
|
||||
### Code as Truth
|
||||
|
||||
**Definition**: Only the code truly tells you what the system does.
|
||||
|
||||
The code is the only source of accurate information. Comments can mislead, but executable code cannot lie about what it actually does.
|
||||
|
||||
**Key points**:
|
||||
- Trust the code over the comment when they conflict
|
||||
- Make the code tell the truth clearly
|
||||
- Use comments only when code cannot express intent
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Self-documenting code | Code that explains itself through clear naming and structure |
|
||||
| Comment decay | The process by which comments become inaccurate over time |
|
||||
| Noise comment | A comment that adds no information beyond what code already shows |
|
||||
| Orphaned comment | A comment separated from the code it was meant to describe |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Naming**: Good names eliminate need for explanatory comments
|
||||
- **Functions**: Small, well-named functions replace comment blocks
|
||||
- **Refactoring**: Often the solution to "needing" a comment is refactoring
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: More comments mean better documented code
|
||||
**Reality**: Comments often indicate code that needs to be clearer
|
||||
|
||||
- **Myth**: Every function needs a doc comment
|
||||
**Reality**: Good function names make most doc comments redundant
|
||||
|
||||
- **Myth**: Comments help future maintainers
|
||||
**Reality**: Outdated comments actively mislead maintainers
|
||||
|
||||
## The Comment Test
|
||||
|
||||
Before writing a comment, ask:
|
||||
|
||||
1. Can I rename a variable to make this clear?
|
||||
2. Can I extract a well-named function?
|
||||
3. Can I restructure the code to be self-explanatory?
|
||||
4. Is this comment explaining WHAT (bad) or WHY (potentially good)?
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Comments as failure | Every comment means code wasn't clear enough |
|
||||
| Comment decay | Comments become lies over time |
|
||||
| Code as truth | Only executable code is reliable documentation |
|
||||
| Self-documenting | The goal is code that needs no comments |
|
||||
@@ -0,0 +1,196 @@
|
||||
# Comments Rules
|
||||
|
||||
Guidelines for when comments are acceptable and when to avoid them.
|
||||
|
||||
## Good Comments
|
||||
|
||||
### 1. Legal Comments
|
||||
|
||||
Copyright and license statements at file start are acceptable.
|
||||
|
||||
- Keep brief - reference external license files
|
||||
- Let IDE collapse them to reduce clutter
|
||||
|
||||
```typescript
|
||||
// Copyright (C) 2024 Company Inc. All rights reserved.
|
||||
// Released under the terms of the MIT License.
|
||||
```
|
||||
|
||||
### 2. Explanation of Intent
|
||||
|
||||
Explain WHY a decision was made, not WHAT the code does.
|
||||
|
||||
```typescript
|
||||
// We sort our type higher than others to ensure consistent ordering
|
||||
// when mixed with external types in collections
|
||||
return 1;
|
||||
```
|
||||
|
||||
### 3. Clarification
|
||||
|
||||
Translate obscure library returns or arguments when you can't modify them.
|
||||
|
||||
```typescript
|
||||
expect(a.compareTo(a)).toBe(0); // a == a
|
||||
expect(a.compareTo(b)).toBe(-1); // a < b
|
||||
expect(b.compareTo(a)).toBe(1); // b > a
|
||||
```
|
||||
|
||||
**Warning**: Verify accuracy - clarifying comments are high-risk for errors.
|
||||
|
||||
### 4. Warning of Consequences
|
||||
|
||||
Alert other developers to non-obvious risks.
|
||||
|
||||
```typescript
|
||||
// DateTimeFormat is not thread-safe, create new instance each call
|
||||
function makeStandardDateFormat(): Intl.DateTimeFormat {
|
||||
return new Intl.DateTimeFormat('en-US', { /* options */ });
|
||||
}
|
||||
```
|
||||
|
||||
### 5. TODO Comments
|
||||
|
||||
Mark planned work with context, but clean them up regularly.
|
||||
|
||||
```typescript
|
||||
// TODO: Remove after checkout model migration (Q2 2024)
|
||||
function makeVersion(): VersionInfo | null {
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Amplification
|
||||
|
||||
Highlight importance of something that appears trivial.
|
||||
|
||||
```typescript
|
||||
const listItemContent = match[3].trim();
|
||||
// trim() is critical - leading spaces cause incorrect list detection
|
||||
```
|
||||
|
||||
### 7. JSDoc for Public APIs
|
||||
|
||||
Document public APIs that others will consume.
|
||||
|
||||
- Keep concise and accurate
|
||||
- Update when API changes
|
||||
- Skip for internal/private code
|
||||
|
||||
## Bad Comments
|
||||
|
||||
### 1. Mumbling
|
||||
|
||||
Unclear comments that require reading other code to understand.
|
||||
|
||||
- If the comment needs explanation, rewrite it or delete it
|
||||
|
||||
### 2. Redundant Comments
|
||||
|
||||
Comments that restate what the code already says.
|
||||
|
||||
```typescript
|
||||
// Bad - says nothing the code doesn't
|
||||
/** Returns the day of the month */
|
||||
getDayOfMonth(): number {
|
||||
return this.dayOfMonth;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Misleading Comments
|
||||
|
||||
Comments that are subtly inaccurate.
|
||||
|
||||
- Causes debugging nightmares when developers trust the comment over code
|
||||
|
||||
### 4. Mandated Comments
|
||||
|
||||
Requiring comments on every function/variable creates noise.
|
||||
|
||||
```typescript
|
||||
// Bad - adds no value, potential for lies
|
||||
/**
|
||||
* @param title The title of the CD
|
||||
* @param author The author of the CD
|
||||
*/
|
||||
function addCD(title: string, author: string): void { }
|
||||
```
|
||||
|
||||
### 5. Journal Comments
|
||||
|
||||
Change logs at file top - use version control instead.
|
||||
|
||||
### 6. Noise Comments
|
||||
|
||||
Comments restating the obvious.
|
||||
|
||||
```typescript
|
||||
// Bad
|
||||
/** Default constructor */
|
||||
constructor() { }
|
||||
|
||||
/** The day of the month */
|
||||
private dayOfMonth: number;
|
||||
```
|
||||
|
||||
### 7. Position Markers / Banners
|
||||
|
||||
```typescript
|
||||
// Bad - use sparingly if at all
|
||||
// //////////////// Actions ////////////////
|
||||
```
|
||||
|
||||
### 8. Closing Brace Comments
|
||||
|
||||
If you need these, your function is too long.
|
||||
|
||||
```typescript
|
||||
// Bad
|
||||
} // end while
|
||||
} // end try
|
||||
```
|
||||
|
||||
### 9. Attributions
|
||||
|
||||
Use version control, not comments like `// Added by Rick`.
|
||||
|
||||
### 10. Commented-Out Code
|
||||
|
||||
Delete it. Version control remembers everything.
|
||||
|
||||
### 11. Nonlocal Information
|
||||
|
||||
Don't describe system-wide behavior in local comments.
|
||||
|
||||
### 12. Too Much Information
|
||||
|
||||
Skip historical context, RFCs, or algorithms - link to external docs instead.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Prefer extracting a well-named function over writing a comment
|
||||
- If comment explains WHAT, refactor the code instead
|
||||
- If comment explains WHY, it might be valuable
|
||||
- Scan TODOs regularly and resolve them
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **Third-party code**: Clarifying comments acceptable when you can't modify the source
|
||||
- **Complex algorithms**: Mathematical or algorithmic intent may need explanation
|
||||
- **Regulatory requirements**: Some domains require specific documentation
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Comment Type | Verdict |
|
||||
|--------------|---------|
|
||||
| Legal headers | OK |
|
||||
| Intent explanation | OK |
|
||||
| Warning of consequences | OK |
|
||||
| TODO with context | OK (clean up regularly) |
|
||||
| Amplification | OK |
|
||||
| Public API docs | OK |
|
||||
| Redundant/obvious | Bad |
|
||||
| Commented-out code | Bad |
|
||||
| Journal/changelog | Bad |
|
||||
| Attributions | Bad |
|
||||
| Closing brace markers | Bad |
|
||||
@@ -0,0 +1,132 @@
|
||||
# Commitment Examples
|
||||
|
||||
Dialogue examples demonstrating commitment vs non-commitment language.
|
||||
|
||||
## Non-Commitment Language
|
||||
|
||||
### Weasel Words to Avoid
|
||||
|
||||
| Word/Phrase | Example | Problem |
|
||||
|-------------|---------|---------|
|
||||
| Need/Should | "We need to get this done." | No personal ownership |
|
||||
| Hope/Wish | "I hope to get this done by tomorrow." | No commitment to outcome |
|
||||
| Let's | "Let's meet sometime." | Vague, no specific action |
|
||||
| Try | "I'll try to get that done as well." | Maybe/maybe not hedge |
|
||||
|
||||
### Bad Response Examples
|
||||
|
||||
**IT guy avoiding responsibility**:
|
||||
> "Yeah. We really need to get some new routers."
|
||||
|
||||
**Problem**: Uses "need" - nothing will happen.
|
||||
|
||||
**Developer hedging on testing**:
|
||||
> "Sure. I hope to get to it by the end of the day."
|
||||
|
||||
**Problem**: Uses "hope" - no real commitment made.
|
||||
|
||||
**Manager deflecting**:
|
||||
> "We have to move faster."
|
||||
|
||||
**Problem**: Uses "we" and "have to" - really means YOU should do something.
|
||||
|
||||
### The "Doable" Trap
|
||||
|
||||
> Marge: "Peter, will you have the rating engine mods done by Friday?"
|
||||
>
|
||||
> Peter: "I think that's doable."
|
||||
>
|
||||
> Marge: "Will that include the documentation?"
|
||||
>
|
||||
> Peter: "I'll try to get that done as well."
|
||||
|
||||
**Problems**:
|
||||
- "I think that's doable" is not yes or no
|
||||
- "I'll try" is the maybe/maybe not hedge
|
||||
- Marge asked boolean questions, got fuzzy answers
|
||||
|
||||
## Commitment Language
|
||||
|
||||
### The Formula
|
||||
|
||||
**Pattern**: "I will [specific action] by [specific time]."
|
||||
|
||||
**Good examples**:
|
||||
- "I will finish this by Tuesday."
|
||||
- "I will have the docs ready by Monday morning."
|
||||
- "I will call you by 3pm today with an update."
|
||||
|
||||
### Honest Uncertainty
|
||||
|
||||
Better than hedging - express uncertainty explicitly:
|
||||
|
||||
> Marge: "Peter, will you have the rating engine mods done by Friday?"
|
||||
>
|
||||
> Peter: "Probably, but it might be Monday."
|
||||
>
|
||||
> Marge: "Will that include the documentation?"
|
||||
>
|
||||
> Peter: "The documentation will take me another few hours, so Monday is possible, but it might be as late as Tuesday."
|
||||
|
||||
**Why it works**:
|
||||
- Describes his actual uncertainty
|
||||
- Gives Marge real information to work with
|
||||
- No false promises
|
||||
|
||||
### Saying No Professionally
|
||||
|
||||
When you can't commit, say so clearly:
|
||||
|
||||
> Marge: "Peter, I need a definite yes or no. Will you have the rating engine finished and documented by Friday?"
|
||||
>
|
||||
> Peter: "In that case, Marge, I'll have to say no. The soonest I can be sure that I'll be done with the mods and the docs is Tuesday."
|
||||
>
|
||||
> Marge: "You are committing to Tuesday?"
|
||||
>
|
||||
> Peter: "Yes, I will have it all ready on Tuesday."
|
||||
|
||||
**Why it works**:
|
||||
- Clear no to the impossible date
|
||||
- Offers alternative he CAN commit to
|
||||
- Final statement is real commitment language
|
||||
|
||||
### Negotiating a Real Commitment
|
||||
|
||||
When there's pressure but a path forward:
|
||||
|
||||
> Marge: "Peter, look, I know this is a huge imposition, but I really need you to find a way to get this all done by Monday morning."
|
||||
>
|
||||
> Peter: "OK, Marge, I'll tell you what. I'll call home and clear some overtime with my family. If they are OK with it, then I'll get this task done by Monday morning. I'll even come in on Monday morning to make sure everything goes smoothly with Willy. But then I'll go home and won't be back until Wednesday. Deal?"
|
||||
|
||||
**Why it works**:
|
||||
- Acknowledges real constraints (family, recovery time)
|
||||
- Makes explicit trade-off visible
|
||||
- Conditional commitment with clear terms
|
||||
- Real "I will" language with specific time
|
||||
|
||||
### Breaking Dependencies into Controllable Actions
|
||||
|
||||
**Instead of**: "I will finish the module with full integration."
|
||||
|
||||
**Commit to**:
|
||||
- "I will sit down for an hour with Gary to understand dependencies."
|
||||
- "I will create an interface that abstracts my module's dependency."
|
||||
- "I will meet three times this week with the build guy."
|
||||
- "I will create my own personal build that runs integration tests."
|
||||
|
||||
**Instead of**: "I will fix all 25 bugs before release."
|
||||
|
||||
**Commit to**:
|
||||
- "I will go through all 25 bugs and try to recreate them."
|
||||
- "I will sit down with the QA who found each bug to see a repro."
|
||||
- "I will spend all my time this week trying to fix each bug."
|
||||
|
||||
## Summary Comparison
|
||||
|
||||
| Non-Commitment | Commitment |
|
||||
|----------------|------------|
|
||||
| "We need to..." | "I will..." |
|
||||
| "I hope to..." | "I will... by..." |
|
||||
| "I'll try to..." | "Yes, I will" or "No, I cannot" |
|
||||
| "That's doable" | "Yes" or "Probably, but might be [date]" |
|
||||
| "Let's meet sometime" | "I will meet you at [place] at [time]" |
|
||||
@@ -0,0 +1,86 @@
|
||||
# Commitment Knowledge
|
||||
|
||||
Core concepts and foundational understanding for making real commitments.
|
||||
|
||||
## Overview
|
||||
|
||||
Commitment is a three-part process: saying, meaning, and doing. Most communication failures stem from unclear commitment language. Professionals use specific language patterns that signal genuine commitment versus vague promises.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### The Say/Mean/Do Framework
|
||||
|
||||
**Definition**: A commitment requires three sequential parts that must all be present.
|
||||
|
||||
1. You **say** you'll do it
|
||||
2. You **mean** it
|
||||
3. You **actually do** it
|
||||
|
||||
**Key points**:
|
||||
- Most people fail at one or more stages
|
||||
- Saying without meaning creates false expectations
|
||||
- Meaning without doing breaks trust
|
||||
|
||||
### Language of Commitment
|
||||
|
||||
**Definition**: Specific verbal patterns that indicate genuine commitment versus hedging.
|
||||
|
||||
The formula for real commitment: **"I will... by..."**
|
||||
|
||||
**Key points**:
|
||||
- States a fact about what YOU will do
|
||||
- Includes a clear end time
|
||||
- Creates binary outcome (done or not done)
|
||||
- Takes full personal responsibility
|
||||
|
||||
### Victim vs. Owner Mindset
|
||||
|
||||
**Definition**: The distinction between treating situations as outside your control versus taking ownership.
|
||||
|
||||
Non-commitment phrases assume things are out of "my" hands. Real commitment acknowledges you ALWAYS have something under your control.
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Weasel words | Language that sounds like commitment but avoids it |
|
||||
| Red flag | Early warning that a commitment may not be met |
|
||||
| Binary response | A clear yes or no answer, not fuzzy language |
|
||||
| Commitment language | "I will X by Y" - specific, time-bound promise |
|
||||
| Stakeholder | Anyone relying on your commitment |
|
||||
|
||||
## The "Try" Problem
|
||||
|
||||
There are two meanings of "try":
|
||||
1. **Extra effort** - Working harder to achieve something
|
||||
2. **Maybe, maybe not** - Hedging without real commitment
|
||||
|
||||
When someone says "I'll try," they often mean the second definition while implying the first.
|
||||
|
||||
## Why Commitment Matters
|
||||
|
||||
- **Estimations**: Clear commitments improve planning accuracy
|
||||
- **Deadlines**: Binary language eliminates ambiguity
|
||||
- **Trust**: Following through builds professional reputation
|
||||
- **Collaboration**: Others can depend on your promises
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: You can only commit to final outcomes
|
||||
**Reality**: You can commit to specific actions that move toward a goal
|
||||
|
||||
- **Myth**: Commitment means never failing
|
||||
**Reality**: Commitment means raising red flags early when problems arise
|
||||
|
||||
- **Myth**: Professionals always say yes
|
||||
**Reality**: Professionals say no when they can't genuinely commit
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Say/Mean/Do | Three parts must all be present for real commitment |
|
||||
| "I will... by..." | The language formula for genuine commitment |
|
||||
| Weasel words | need, should, hope, wish, let's (without "I") |
|
||||
| Red flag protocol | Raise issues immediately when commitment is at risk |
|
||||
| Binary response | Give clear yes/no, not fuzzy answers |
|
||||
@@ -0,0 +1,100 @@
|
||||
# Commitment Rules
|
||||
|
||||
Rules for making and keeping professional commitments.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Use "I will... by..." Language
|
||||
|
||||
State what YOU will do with a clear end time.
|
||||
|
||||
- First person singular ("I"), not "we" or "someone"
|
||||
- Specific action, not vague intention
|
||||
- Concrete deadline, not "soon" or "when I can"
|
||||
|
||||
**Example**:
|
||||
```
|
||||
// Bad
|
||||
"We need to get this done."
|
||||
"I'll try to finish it."
|
||||
"Hopefully by next week."
|
||||
|
||||
// Good
|
||||
"I will finish the API integration by Thursday 5pm."
|
||||
```
|
||||
|
||||
### 2. Only Commit to What You Control
|
||||
|
||||
You can only commit to things fully under your control.
|
||||
|
||||
- If outcome depends on others, commit to YOUR actions
|
||||
- Break external dependencies into controllable steps
|
||||
- Commit to actions that move you toward the goal
|
||||
|
||||
**Example**:
|
||||
```
|
||||
// Bad - depends on others
|
||||
"I will finish the module with full integration with the other team."
|
||||
|
||||
// Good - actions you control
|
||||
"I will meet with Gary from infrastructure for one hour tomorrow."
|
||||
"I will create an interface that abstracts the dependency."
|
||||
"I will create a personal build that runs integration tests."
|
||||
```
|
||||
|
||||
### 3. Raise Red Flags Immediately
|
||||
|
||||
When a commitment is at risk, notify stakeholders as soon as possible.
|
||||
|
||||
- Don't wait until the deadline passes
|
||||
- Earlier notice = more options for the team
|
||||
- Enables replanning, reprioritizing, or getting help
|
||||
|
||||
### 4. Give Binary Responses
|
||||
|
||||
Answer yes/no questions with yes or no.
|
||||
|
||||
- Avoid fuzzy responses to boolean questions
|
||||
- Express uncertainty explicitly with ranges
|
||||
- "Probably, but it might be Monday" is better than "I think that's doable"
|
||||
|
||||
### 5. Maintain Discipline Under Pressure
|
||||
|
||||
Never sacrifice professional standards to meet a commitment.
|
||||
|
||||
- Don't skip tests to go faster (you won't)
|
||||
- Don't skip refactoring (it slows you down)
|
||||
- Don't skip regression suites
|
||||
- Your commitment to standards comes first
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Know your limits for overtime and its costs
|
||||
- Clear overtime with family/stakeholders before committing to it
|
||||
- Plan for recovery time after intense effort
|
||||
- Be honest with yourself about stamina and reserves
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **Discovery work**: When you genuinely don't know if something is possible, commit to actions that will find out
|
||||
- **External blockers**: When dependencies are truly outside your control, commit to escalation and communication actions
|
||||
|
||||
## When NOT to Commit
|
||||
|
||||
Situations where you should say no:
|
||||
|
||||
- You don't have full control over the outcome
|
||||
- Meeting the deadline would require breaking professional standards
|
||||
- You're being asked to commit to "try" harder (vague effort)
|
||||
- The timeline doesn't account for realistic uncertainty
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| "I will... by..." | The only commitment language that counts |
|
||||
| Control test | Only commit to what YOU control |
|
||||
| Red flag rule | Raise issues immediately when at risk |
|
||||
| Binary answers | Yes or no, not maybe |
|
||||
| Standards first | Never sacrifice quality to meet deadlines |
|
||||
| Know your limits | Be realistic about overtime capacity |
|
||||
@@ -0,0 +1,200 @@
|
||||
# Error Handling Examples
|
||||
|
||||
Code examples demonstrating clean error handling principles.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Error Codes Cluttering Logic
|
||||
|
||||
```typescript
|
||||
class DeviceController {
|
||||
sendShutDown(): void {
|
||||
const handle = this.getHandle(DEV1);
|
||||
if (handle !== INVALID_HANDLE) {
|
||||
const record = this.retrieveDeviceRecord(handle);
|
||||
if (record.status !== DeviceStatus.SUSPENDED) {
|
||||
this.pauseDevice(handle);
|
||||
this.clearDeviceWorkQueue(handle);
|
||||
this.closeDevice(handle);
|
||||
} else {
|
||||
logger.log('Device suspended. Unable to shut down');
|
||||
}
|
||||
} else {
|
||||
logger.log(`Invalid handle for: ${DEV1}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**: Business logic tangled with error checks; hard to see intent.
|
||||
|
||||
### Multiple Catch Blocks with Duplication
|
||||
|
||||
```typescript
|
||||
try {
|
||||
port.open();
|
||||
} catch (e) {
|
||||
if (e instanceof DeviceResponseException) {
|
||||
reportPortError(e);
|
||||
logger.log('Device response exception', e);
|
||||
} else if (e instanceof ATM1212UnlockedException) {
|
||||
reportPortError(e);
|
||||
logger.log('Unlock exception', e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**: Duplicated handling; tied to third-party types.
|
||||
|
||||
### Excessive Null Checks
|
||||
|
||||
```typescript
|
||||
function registerItem(item: Item | null): void {
|
||||
if (item !== null) {
|
||||
const registry = persistentStore.getItemRegistry();
|
||||
if (registry !== null) {
|
||||
const existing = registry.getItem(item.id);
|
||||
if (existing.billingPeriod.hasRetailOwner()) {
|
||||
existing.register(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**: Nested conditionals; easy to miss checks; obscures intent.
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Clean Exception-Based Code
|
||||
|
||||
```typescript
|
||||
class DeviceController {
|
||||
sendShutDown(): void {
|
||||
try {
|
||||
this.tryToShutDown();
|
||||
} catch (e) {
|
||||
if (e instanceof DeviceShutDownError) logger.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
private tryToShutDown(): void {
|
||||
const handle = this.getHandle(DEV1);
|
||||
this.pauseDevice(handle);
|
||||
this.clearDeviceWorkQueue(handle);
|
||||
this.closeDevice(handle);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: Shutdown algorithm clearly visible; concerns separated.
|
||||
|
||||
### Wrapped Third-Party API
|
||||
|
||||
```typescript
|
||||
class LocalPort {
|
||||
constructor(private innerPort: ACMEPort) {}
|
||||
|
||||
open(): void {
|
||||
try {
|
||||
this.innerPort.open();
|
||||
} catch (e) {
|
||||
throw new PortDeviceFailure('Failed to open port', { cause: e });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Usage - single exception type
|
||||
try {
|
||||
port.open();
|
||||
} catch (e) {
|
||||
if (e instanceof PortDeviceFailure) reportError(e);
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: Single exception type; not tied to vendor; easy to mock.
|
||||
|
||||
### Special Case Pattern
|
||||
|
||||
```typescript
|
||||
interface MealExpenses {
|
||||
getTotal(): number;
|
||||
}
|
||||
|
||||
class PerDiemMealExpenses implements MealExpenses {
|
||||
getTotal(): number { return DEFAULT_PER_DIEM; }
|
||||
}
|
||||
|
||||
// DAO returns Special Case when no expenses found
|
||||
function getMeals(employeeId: string): MealExpenses {
|
||||
const expenses = db.findExpenses(employeeId);
|
||||
return expenses ?? new PerDiemMealExpenses();
|
||||
}
|
||||
|
||||
// Clean business logic - no exception handling
|
||||
const expenses = getMeals(employee.id);
|
||||
total += expenses.getTotal();
|
||||
```
|
||||
|
||||
**Why it works**: No try/catch; special case handled internally.
|
||||
|
||||
### Empty Collection Instead of Null
|
||||
|
||||
```typescript
|
||||
// Bad
|
||||
function getEmployees(): Employee[] | null {
|
||||
if (noEmployees) return null;
|
||||
return employees;
|
||||
}
|
||||
|
||||
// Good
|
||||
function getEmployees(): Employee[] {
|
||||
return employees ?? [];
|
||||
}
|
||||
|
||||
// Usage - no null check needed
|
||||
for (const e of getEmployees()) {
|
||||
totalPay += e.getPay();
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: Safe to iterate; cleaner code.
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before
|
||||
|
||||
```typescript
|
||||
async function processOrder(orderId: string): Promise<void> {
|
||||
const order = await getOrder(orderId);
|
||||
if (order === null) { logger.error('Order not found'); return; }
|
||||
|
||||
const customer = await getCustomer(order.customerId);
|
||||
if (customer === null) { logger.error('Customer not found'); return; }
|
||||
|
||||
await fulfillOrder(order);
|
||||
}
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```typescript
|
||||
async function processOrder(orderId: string): Promise<void> {
|
||||
try {
|
||||
const order = await getOrder(orderId); // Throws if not found
|
||||
const customer = await getCustomer(order.customerId);
|
||||
await fulfillOrder(order, customer);
|
||||
} catch (e) {
|
||||
if (e instanceof OrderError) {
|
||||
logger.error(e.message, { orderId });
|
||||
} else throw e;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. **Removed null returns** - Functions throw specific errors
|
||||
2. **Separated concerns** - Happy path clearly visible
|
||||
3. **Consolidated handling** - Single catch for all order errors
|
||||
4. **Added context** - Errors include orderId for debugging
|
||||
@@ -0,0 +1,93 @@
|
||||
# Error Handling Knowledge
|
||||
|
||||
Core concepts and foundational understanding for error handling in clean code.
|
||||
|
||||
## Overview
|
||||
|
||||
Error handling is essential but should not obscure business logic. Clean error handling separates error concerns from main algorithms, making code both readable and robust. The goal is graceful, stylish error management that enhances rather than clutters code.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Separation of Concerns
|
||||
|
||||
**Definition**: Error handling logic should be separate from business logic.
|
||||
|
||||
When error handling dominates code, it becomes impossible to see what the code actually does. Error handling is important, but if it obscures logic, it's wrong.
|
||||
|
||||
**Key points**:
|
||||
- Two concerns should not be tangled together
|
||||
- You should be able to understand each concern independently
|
||||
- Business logic should read like a clean, unadorned algorithm
|
||||
|
||||
### Exception-Based Error Handling
|
||||
|
||||
**Definition**: Using exceptions instead of return codes or error flags to signal errors.
|
||||
|
||||
Exceptions allow error handling at a distance, separating where errors occur from where they're handled. This keeps the happy path clean.
|
||||
|
||||
**Key points**:
|
||||
- Return codes clutter the caller with immediate checks
|
||||
- Exceptions let calling code focus on its primary purpose
|
||||
- Missing a return code check is easy; exceptions can't be ignored
|
||||
|
||||
### Normal Flow Pattern
|
||||
|
||||
**Definition**: Designing code so that special cases don't require exception handling in business logic.
|
||||
|
||||
Instead of using try/catch for expected variations, design objects that handle special cases internally. This keeps business logic clean and linear.
|
||||
|
||||
**Key points**:
|
||||
- Push error detection to the edges of your program
|
||||
- Use Special Case objects for expected variations
|
||||
- Business logic shouldn't deal with exceptional behavior directly
|
||||
|
||||
### Transaction Scope
|
||||
|
||||
**Definition**: A try block defines a scope where execution can abort and resume at catch.
|
||||
|
||||
Think of try blocks as transactions - the catch must leave the program in a consistent state regardless of what happens in the try block.
|
||||
|
||||
**Key points**:
|
||||
- Start with try-catch-finally when writing code that could throw
|
||||
- Define what users should expect regardless of failures
|
||||
- Build up logic inside the try block after establishing the scope
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Exception | An object thrown to signal an error condition |
|
||||
| Try-Catch-Finally | Structure that defines error handling scope |
|
||||
| Special Case Pattern | Object that handles edge cases internally |
|
||||
| Wrapper | Class that translates third-party exceptions to your own |
|
||||
| Context | Information provided with exceptions for debugging |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Functions**: Error handling should be extracted into separate functions
|
||||
- **Testing**: Write tests that force exceptions first (TDD approach)
|
||||
- **Third-Party APIs**: Wrap external APIs to control exception types
|
||||
- **Null Safety**: Avoiding null prevents many error conditions
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: More null checks make code safer
|
||||
**Reality**: Too many null checks indicate a design problem; return empty collections or Special Case objects instead
|
||||
|
||||
- **Myth**: Exceptions should match their technical source
|
||||
**Reality**: Define exceptions based on how callers will catch them, not where they originate
|
||||
|
||||
- **Myth**: Each error type needs its own exception class
|
||||
**Reality**: Often a single exception class per area is sufficient; use different classes only when callers need to handle them differently
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Separation | Error handling separate from business logic |
|
||||
| Exceptions | Prefer throwing over return codes |
|
||||
| Try-First | Start with try-catch-finally structure |
|
||||
| Normal Flow | Design to minimize exception handling in business code |
|
||||
| Context | Exceptions should explain what failed and why |
|
||||
| Wrapping | Translate third-party exceptions to your types |
|
||||
| No Null | Don't return null; use empty collections or Special Case |
|
||||
@@ -0,0 +1,196 @@
|
||||
# Error Handling Rules
|
||||
|
||||
Rules for writing clean, robust error handling code.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Use Exceptions, Not Return Codes
|
||||
|
||||
Throw exceptions when you encounter errors instead of returning error codes or flags.
|
||||
|
||||
- Return codes clutter caller with immediate checks
|
||||
- Easy to forget to check return codes
|
||||
- Exceptions separate error handling from main logic
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - return codes
|
||||
function getUser(id: string): User | null {
|
||||
const user = db.find(id);
|
||||
if (!user) return null; // Caller must check
|
||||
return user;
|
||||
}
|
||||
|
||||
// Good - exceptions
|
||||
function getUser(id: string): User {
|
||||
const user = db.find(id);
|
||||
if (!user) throw new UserNotFoundError(`User ${id} not found`);
|
||||
return user;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Write Try-Catch-Finally First
|
||||
|
||||
Start with the error handling structure before writing the logic.
|
||||
|
||||
- Try blocks define a transaction scope
|
||||
- Catch must leave program in consistent state
|
||||
- Establishes expectations for callers upfront
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Start with the structure
|
||||
async function readConfig(path: string): Promise<Config> {
|
||||
try {
|
||||
// Add logic here after establishing scope
|
||||
const content = await fs.readFile(path, 'utf-8');
|
||||
return JSON.parse(content);
|
||||
} catch (error) {
|
||||
throw new ConfigError(`Failed to read config: ${path}`, { cause: error });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Provide Context with Exceptions
|
||||
|
||||
Include enough information to determine source and location of errors.
|
||||
|
||||
- Mention the operation that failed
|
||||
- Include the type of failure
|
||||
- Provide enough info for logging
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - no context
|
||||
throw new Error('Failed');
|
||||
|
||||
// Good - rich context
|
||||
throw new StorageError(
|
||||
`Failed to save user ${userId} to database: connection timeout after ${timeout}ms`
|
||||
);
|
||||
```
|
||||
|
||||
### 4. Define Exceptions by Caller's Needs
|
||||
|
||||
Classify exceptions based on how they will be caught, not their source.
|
||||
|
||||
- Wrap third-party exceptions into your own types
|
||||
- One exception class per area is often sufficient
|
||||
- Use different classes only when callers handle them differently
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - exposing third-party exceptions
|
||||
try {
|
||||
await externalApi.call();
|
||||
} catch (e) {
|
||||
if (e instanceof AxiosError) { /* ... */ }
|
||||
if (e instanceof TimeoutError) { /* ... */ }
|
||||
if (e instanceof NetworkError) { /* ... */ }
|
||||
}
|
||||
|
||||
// Good - wrapped with common type
|
||||
try {
|
||||
await apiClient.call();
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
logger.error(e.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Define the Normal Flow
|
||||
|
||||
Use Special Case pattern to avoid exceptions in business logic.
|
||||
|
||||
- Return default objects instead of throwing for expected cases
|
||||
- Encapsulate special behavior in the object itself
|
||||
- Keep business logic clean and linear
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - exception for normal case
|
||||
try {
|
||||
const expenses = await getMealExpenses(employeeId);
|
||||
total += expenses.getTotal();
|
||||
} catch (e) {
|
||||
if (e instanceof NoExpensesError) {
|
||||
total += getPerDiem();
|
||||
}
|
||||
}
|
||||
|
||||
// Good - Special Case pattern
|
||||
const expenses = await getMealExpenses(employeeId); // Returns PerDiemExpenses if none
|
||||
total += expenses.getTotal();
|
||||
```
|
||||
|
||||
### 6. Don't Return Null
|
||||
|
||||
Return empty collections, throw exceptions, or use Special Case objects.
|
||||
|
||||
- Null forces callers to add null checks everywhere
|
||||
- One missing check causes runtime errors
|
||||
- Empty collections are safe to iterate
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - returning null
|
||||
function getEmployees(): Employee[] | null {
|
||||
if (noEmployees) return null;
|
||||
return employees;
|
||||
}
|
||||
|
||||
// Good - return empty array
|
||||
function getEmployees(): Employee[] {
|
||||
if (noEmployees) return [];
|
||||
return employees;
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Don't Pass Null
|
||||
|
||||
Avoid passing null as function arguments.
|
||||
|
||||
- Forces defensive null checks in every function
|
||||
- No good way to handle accidentally passed null
|
||||
- Forbid null by default in your codebase
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - allows null
|
||||
function calculateDistance(p1: Point | null, p2: Point | null): number {
|
||||
if (!p1 || !p2) throw new Error('Invalid arguments');
|
||||
return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
|
||||
}
|
||||
|
||||
// Good - require valid arguments, use TypeScript strict mode
|
||||
function calculateDistance(p1: Point, p2: Point): number {
|
||||
return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
|
||||
}
|
||||
```
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Wrap third-party APIs to minimize dependencies and enable mocking
|
||||
- Use a single exception class per module/area when possible
|
||||
- Push error detection to the edges of your program
|
||||
- Use TypeScript strict null checks to catch null issues at compile time
|
||||
- Use optional chaining (`?.`) and nullish coalescing (`??`) for safe property access
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **External APIs expecting null**: When calling APIs that require null, pass it
|
||||
- **Performance-critical paths**: Return codes may be acceptable in hot loops
|
||||
- **Optional parameters**: Use TypeScript optional parameters (`param?: Type`) instead of null
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Use Exceptions | Throw instead of return error codes |
|
||||
| Try-First | Start with try-catch-finally structure |
|
||||
| Context | Include operation and failure type in errors |
|
||||
| Caller's Needs | Define exceptions by how they're caught |
|
||||
| Normal Flow | Use Special Case pattern for expected variations |
|
||||
| No Return Null | Return empty collections or throw |
|
||||
| No Pass Null | Forbid null arguments by default |
|
||||
@@ -0,0 +1,183 @@
|
||||
# Estimation Examples
|
||||
|
||||
Examples demonstrating estimation principles and calculations.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Vague Single-Number Estimate
|
||||
|
||||
```
|
||||
Mike: "What is your estimate for completing the Frazzle task?"
|
||||
Peter: "Three days."
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- No information about likelihood
|
||||
- Mike doesn't know if 3 days is certain or optimistic
|
||||
- No way to plan for delays
|
||||
- Creates implied expectation of completion in 3 days
|
||||
|
||||
### The "Try" Trap
|
||||
|
||||
```
|
||||
Mike: "Peter, can you give me a hard date when you'll be done?"
|
||||
Peter: "No, Mike. Like I said, it'll probably be done in three,
|
||||
maybe four, days."
|
||||
Mike: "Can we say four then?"
|
||||
Peter: "No, it could be five or six."
|
||||
Mike: "OK, Peter, but can you try to make it no more than six days?"
|
||||
Peter: "Sure, I'll try."
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Peter just made a commitment disguised as "trying"
|
||||
- If not done in 6 days, Mike can accuse him of "not trying hard enough"
|
||||
- Peter is now implicitly agreeing to work overtime, weekends
|
||||
- The word "try" is loaded - agreeing to try is agreeing to succeed
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Communicating Uncertainty
|
||||
|
||||
```
|
||||
Mike: "What is your estimate for completing the Frazzle task?"
|
||||
Peter: "I'd estimate three days as most likely."
|
||||
Mike: "How likely is it that you'll be done in three days?"
|
||||
Peter: "Fifty or sixty percent."
|
||||
Mike: "So there's a good chance that it'll take you four days."
|
||||
Peter: "Yes, in fact it might even take me five or six, though I doubt it."
|
||||
Mike: "How much do you doubt it?"
|
||||
Peter: "I'm ninety-five percent certain I'll be done before six days.
|
||||
If everything goes wrong, it could take ten or eleven days.
|
||||
But it's not very likely that so much will go wrong."
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Peter shares the full probability distribution
|
||||
- Mike understands the range of possibilities
|
||||
- No false precision or hidden commitments
|
||||
- Mike can plan appropriately for the uncertainty
|
||||
|
||||
### Declining a Commitment Request
|
||||
|
||||
```
|
||||
Mike: "Peter, can you give me a hard date when you'll be done?"
|
||||
Peter: "No, Mike. Like I said, it'll probably be done in three,
|
||||
maybe four, days."
|
||||
Mike: "Can we say four then?"
|
||||
Peter: "No, it could be five or six."
|
||||
Mike: "Can you try to make it no more than six days?"
|
||||
Peter: "I can't commit to that, Mike. I've given you my best estimate -
|
||||
probably 3-4 days, could be 5-6, outside chance of longer."
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Peter avoids the "try" trap
|
||||
- Clear distinction between estimate and commitment
|
||||
- Repeats the probability information
|
||||
- Professional and honest
|
||||
|
||||
## PERT Calculation Examples
|
||||
|
||||
### Single Task Estimate
|
||||
|
||||
**Scenario**: Peter estimates the Frazzle task
|
||||
|
||||
| Estimate Type | Value | Meaning |
|
||||
|--------------|-------|---------|
|
||||
| O (Optimistic) | 1 day | If everything goes perfectly |
|
||||
| N (Nominal) | 3 days | Most likely duration |
|
||||
| P (Pessimistic) | 12 days | If everything goes wrong |
|
||||
|
||||
**Calculations**:
|
||||
- Expected duration (mu) = (1 + 4(3) + 12) / 6 = (1 + 12 + 12) / 6 = **4.2 days**
|
||||
- Standard deviation (sigma) = (12 - 1) / 6 = **1.8 days**
|
||||
|
||||
**Interpretation**: Task will likely take ~4 days, could reasonably take 6 days (mu + sigma), possibly even 8 days (mu + 2*sigma).
|
||||
|
||||
### Multiple Tasks Combined
|
||||
|
||||
**Scenario**: Peter has three sequential tasks
|
||||
|
||||
| Task | O | N | P | mu | sigma |
|
||||
|------|---|---|---|-----|-------|
|
||||
| Alpha | 1 | 3 | 12 | 4.2 | 1.8 |
|
||||
| Beta | 1 | 1 | 14 | 3.5 | 2.2 |
|
||||
| Gamma | 3 | 6 | 8 | 6.5 | 1.3 |
|
||||
|
||||
**Combined Calculations**:
|
||||
- Total expected = 4.2 + 3.5 + 6.5 = **14 days**
|
||||
- Combined sigma = sqrt(1.8^2 + 2.2^2 + 1.3^2)
|
||||
- = sqrt(3.24 + 4.84 + 1.69)
|
||||
- = sqrt(9.77)
|
||||
- = **~3 days**
|
||||
|
||||
**Interpretation**:
|
||||
- Likely completion: 14 days
|
||||
- Could take 17 days (1 sigma)
|
||||
- Possibly 20 days (2 sigma)
|
||||
|
||||
**The Surprise**:
|
||||
- Optimistic totals: 1 + 1 + 3 = 5 days
|
||||
- Nominal totals: 3 + 1 + 6 = 10 days
|
||||
- Yet realistic expectation: 14+ days
|
||||
|
||||
This is why projects estimated optimistically take 3-5x longer than hoped!
|
||||
|
||||
## Estimation Technique Examples
|
||||
|
||||
### Flying Fingers
|
||||
|
||||
```
|
||||
Moderator: "Let's estimate the user authentication task.
|
||||
Scale is days. Discussion first..."
|
||||
|
||||
[Team discusses requirements, complications, implementation approaches]
|
||||
|
||||
Moderator: "Hands under the table... 1, 2, 3, show!"
|
||||
|
||||
Alice: 3 fingers
|
||||
Bob: 4 fingers
|
||||
Carol: 3 fingers
|
||||
Dave: 1 finger <-- outlier
|
||||
|
||||
Moderator: "Dave, why 1 day?"
|
||||
Dave: "I thought we could reuse the OAuth library from project X."
|
||||
Alice: "Oh, I didn't know about that. That changes things."
|
||||
|
||||
[Further discussion, then re-vote]
|
||||
|
||||
All: 2 fingers
|
||||
Moderator: "Consensus at 2 days."
|
||||
```
|
||||
|
||||
### Planning Poker with Trivariate
|
||||
|
||||
```
|
||||
Moderator: "Task: Implement payment processing.
|
||||
First, show cards for OPTIMISTIC estimate."
|
||||
|
||||
[Team shows cards]
|
||||
Results: 2, 1, 2, 1, 2
|
||||
Take lowest: O = 1 day
|
||||
|
||||
Moderator: "Now NOMINAL estimate."
|
||||
Results: 5, 5, 3, 5, 5
|
||||
Take consensus: N = 5 days
|
||||
|
||||
Moderator: "Now PESSIMISTIC estimate."
|
||||
Results: 10, 14, 12, 10, 8
|
||||
Take highest: P = 14 days
|
||||
|
||||
Expected = (1 + 20 + 14) / 6 = 5.8 days
|
||||
Sigma = (14 - 1) / 6 = 2.2 days
|
||||
```
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
| Situation | Bad Response | Good Response |
|
||||
|-----------|--------------|---------------|
|
||||
| "How long?" | "3 days" | "Probably 3, could be 5-6, possibly longer" |
|
||||
| "Can you commit?" | "I'll try" | "I can't commit, but here's my estimate..." |
|
||||
| "Just give me a number" | Single number | O/N/P with calculation |
|
||||
| Estimating alone | Trust your gut | Use team consensus techniques |
|
||||
@@ -0,0 +1,99 @@
|
||||
# Estimation Knowledge
|
||||
|
||||
Core concepts and foundational understanding for software estimation.
|
||||
|
||||
## Overview
|
||||
|
||||
Estimation is one of the most misunderstood activities in software development. The fundamental problem is that business and developers view estimates differently - business sees commitments, developers see guesses. Understanding this distinction is critical for professional software development.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Estimate vs Commitment
|
||||
|
||||
**Definition**: An estimate is a guess with no commitment implied; a commitment is a promise that must be achieved.
|
||||
|
||||
This is the most important distinction in professional estimation. Confusing these two concepts leads to broken promises, damaged reputations, and failed projects.
|
||||
|
||||
**Key points**:
|
||||
- Estimates have no promises attached - missing an estimate is not dishonorable
|
||||
- Commitments require certainty - professionals only commit when they know they can deliver
|
||||
- Business needs both, but they serve different purposes
|
||||
|
||||
### An Estimate is a Distribution
|
||||
|
||||
**Definition**: An estimate is not a single number but a probability distribution describing the range of possible completion times.
|
||||
|
||||
When someone says "three days," they're giving you the most likely duration, not a guarantee. The actual completion time could fall anywhere on a probability curve, with a tail extending toward longer durations.
|
||||
|
||||
**Key points**:
|
||||
- The "estimate" typically represents the peak of probability (most likely)
|
||||
- There's always a tail extending toward longer times
|
||||
- Professional estimates communicate this uncertainty explicitly
|
||||
|
||||
### Implied Commitments
|
||||
|
||||
**Definition**: Implicit promises created through language that sounds like agreement, especially the word "try."
|
||||
|
||||
When you agree to "try" to meet a deadline, you've made a commitment. There's no other interpretation - agreeing to try is agreeing to succeed.
|
||||
|
||||
**Key points**:
|
||||
- "Can you try?" = "Will you commit?"
|
||||
- Saying yes to "try" implies working extra hours, weekends, skipping vacations
|
||||
- Professionals carefully avoid implied commitments
|
||||
|
||||
### PERT (Program Evaluation and Review Technique)
|
||||
|
||||
**Definition**: A technique using three estimates (optimistic, nominal, pessimistic) to create probability distributions suitable for planning.
|
||||
|
||||
Created in 1957 for the U.S. Navy's Polaris submarine project, PERT converts estimates into realistic probability distributions.
|
||||
|
||||
**Key points**:
|
||||
- Uses trivariate analysis: O (optimistic), N (nominal), P (pessimistic)
|
||||
- Expected duration: (O + 4N + P) / 6
|
||||
- Standard deviation: (P - O) / 6
|
||||
|
||||
### Law of Large Numbers
|
||||
|
||||
**Definition**: Breaking large tasks into smaller ones and estimating independently produces more accurate total estimates because errors tend to integrate out.
|
||||
|
||||
**Key points**:
|
||||
- Small task errors tend to cancel each other
|
||||
- Underestimation bias means integration isn't perfect
|
||||
- Breaking tasks up also helps discover hidden complexity
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Commitment | A promise you must achieve, regardless of required effort |
|
||||
| Estimate | A guess about duration with no promise implied |
|
||||
| Trivariate Analysis | Using three estimates (O, N, P) to model uncertainty |
|
||||
| Expected Duration (mu) | The statistically expected completion time |
|
||||
| Standard Deviation (sigma) | A measure of uncertainty in the estimate |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Saying No**: Declining impossible commitments while providing honest estimates
|
||||
- **Time Management**: Realistic estimates enable better planning
|
||||
- **Professionalism**: Clear communication about certainty builds trust
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: A good developer should be able to estimate accurately
|
||||
**Reality**: Estimates are inherently uncertain - that's why they're called estimates
|
||||
|
||||
- **Myth**: Agreeing to "try" is different from committing
|
||||
**Reality**: Agreeing to try IS committing - there's no other interpretation
|
||||
|
||||
- **Myth**: The nominal estimate is when you'll be done
|
||||
**Reality**: The nominal is just the most likely point on a distribution curve
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Estimate | A probability distribution, not a single number |
|
||||
| Commitment | A promise requiring certainty before making |
|
||||
| PERT Formula | (O + 4N + P) / 6 for expected duration |
|
||||
| Standard Deviation | (P - O) / 6 measures uncertainty |
|
||||
| "Try" | Agreeing to try = agreeing to succeed |
|
||||
@@ -0,0 +1,106 @@
|
||||
# Estimation Rules
|
||||
|
||||
Rules for providing professional estimates and avoiding commitment traps.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Never Confuse Estimates with Commitments
|
||||
|
||||
Estimates are guesses; commitments are promises. Keep them separate.
|
||||
|
||||
- Only commit when you are certain you can deliver
|
||||
- Communicate estimates as probability distributions, not single numbers
|
||||
- Make the distinction explicit in conversations with stakeholders
|
||||
|
||||
### 2. Never Make Implied Commitments
|
||||
|
||||
Watch for language that creates hidden commitments.
|
||||
|
||||
- Never agree to "try" - it's a commitment in disguise
|
||||
- If you say yes to "try," you're committing to work extra hours, weekends, etc.
|
||||
- If pressed for commitment, decline clearly rather than hedge
|
||||
|
||||
**Example**:
|
||||
```
|
||||
// Bad - creates implied commitment
|
||||
Mike: "Can you try to make it no more than six days?"
|
||||
Peter: "I'll try."
|
||||
|
||||
// Good - maintains honesty
|
||||
Mike: "Can you try to make it no more than six days?"
|
||||
Peter: "I can't commit to that. It could be five or six days,
|
||||
possibly longer if things go wrong."
|
||||
```
|
||||
|
||||
### 3. Provide Trivariate Estimates (PERT)
|
||||
|
||||
Give three numbers, not one.
|
||||
|
||||
- **O (Optimistic)**: Everything goes perfectly (< 1% chance)
|
||||
- **N (Nominal)**: Most likely duration (highest probability)
|
||||
- **P (Pessimistic)**: Everything goes wrong (< 1% chance)
|
||||
|
||||
Calculate expected duration: **(O + 4N + P) / 6**
|
||||
Calculate uncertainty: **(P - O) / 6**
|
||||
|
||||
### 4. Use Team Estimation Techniques
|
||||
|
||||
Don't estimate alone - use the people around you.
|
||||
|
||||
- **Wideband Delphi**: Team discusses and estimates until consensus
|
||||
- **Flying Fingers**: Show 0-5 fingers simultaneously after discussion
|
||||
- **Planning Poker**: Use cards (0, 1, 3, 5, 10) to estimate simultaneously
|
||||
- **Affinity Estimation**: Silently sort task cards by size, then discuss
|
||||
|
||||
### 5. Break Large Tasks into Smaller Ones
|
||||
|
||||
The Law of Large Numbers improves accuracy.
|
||||
|
||||
- Estimate small tasks independently
|
||||
- Errors tend to cancel out (though underestimation bias persists)
|
||||
- Breaking tasks up reveals hidden complexity
|
||||
|
||||
## Guidelines
|
||||
|
||||
### Communicating Estimates
|
||||
|
||||
- Always include the probability distribution, not just the peak
|
||||
- "Probably 3 days, could be 5-6, unlikely but possibly 10-11"
|
||||
- Let managers understand the uncertainty to make appropriate plans
|
||||
|
||||
### When Asked for Commitment
|
||||
|
||||
- Ask yourself: Am I *certain* I can achieve this?
|
||||
- If not certain, decline to commit
|
||||
- Offer an estimate with probability distribution instead
|
||||
|
||||
### Combining Multiple Task Estimates
|
||||
|
||||
For a sequence of tasks:
|
||||
- Expected total = sum of individual expected durations
|
||||
- Combined sigma = sqrt(sum of squared individual sigmas)
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **Crisis situations**: May need to provide quick rough estimates, but clarify they are rough
|
||||
- **Trivial tasks**: Very small, well-understood tasks may not need trivariate analysis
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Estimate != Commitment | Keep guesses and promises separate |
|
||||
| Never "try" | Agreeing to try is agreeing to commit |
|
||||
| Use PERT | Three numbers: O, N, P -> calculate expected |
|
||||
| Team estimation | Use consensus techniques, don't estimate alone |
|
||||
| Break it down | Smaller tasks = better accuracy |
|
||||
| Communicate uncertainty | Share the distribution, not just the peak |
|
||||
|
||||
## Estimation Techniques Summary
|
||||
|
||||
| Technique | How It Works | Best For |
|
||||
|-----------|--------------|----------|
|
||||
| Flying Fingers | Hold up 0-5 fingers simultaneously | Quick team estimates |
|
||||
| Planning Poker | Cards (0,1,3,5,10) shown simultaneously | Distributed teams |
|
||||
| Affinity Estimation | Silent sorting, then discussion | Many tasks at once |
|
||||
| Trivariate (PERT) | O + 4N + P / 6 | Individual task probability |
|
||||
@@ -0,0 +1,278 @@
|
||||
# Formatting Examples
|
||||
|
||||
Code examples demonstrating formatting principles in TypeScript.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Missing Vertical Openness
|
||||
|
||||
```typescript
|
||||
import { regex } from './utils';
|
||||
import { ParentWidget } from './widgets';
|
||||
export class BoldWidget extends ParentWidget {
|
||||
private static readonly REGEXP = /'''.+?'''/;
|
||||
private pattern = /'''(.+?)'''/gs;
|
||||
constructor(parent: ParentWidget, text: string) {
|
||||
super(parent);
|
||||
const match = this.pattern.exec(text);
|
||||
if (match) this.addChildWidgets(match[1]);
|
||||
}
|
||||
render(): string {
|
||||
return `<b>${this.childHtml()}</b>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- No blank lines between imports and class
|
||||
- No separation between static fields and constructor
|
||||
- No separation between constructor and methods
|
||||
- Code appears as an unstructured block
|
||||
|
||||
### Excessive Comments Breaking Density
|
||||
|
||||
```typescript
|
||||
class ReporterConfig {
|
||||
/**
|
||||
* The class name of the reporter listener
|
||||
*/
|
||||
private className: string;
|
||||
|
||||
/**
|
||||
* The properties of the reporter listener
|
||||
*/
|
||||
private properties: Property[] = [];
|
||||
|
||||
public addProperty(property: Property): void {
|
||||
this.properties.push(property);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Trivial comments separate related declarations
|
||||
- Forces reader to scan more vertical space
|
||||
- Comments add no information beyond the names
|
||||
|
||||
### Instance Variables Hidden in Class
|
||||
|
||||
```typescript
|
||||
class TestSuite implements Test {
|
||||
static createTest(testClass: TestClass, name: string): Test {
|
||||
// ... implementation
|
||||
}
|
||||
|
||||
static getTestConstructor(testClass: TestClass): Constructor {
|
||||
// ... implementation
|
||||
}
|
||||
|
||||
// Instance variables buried after static methods!
|
||||
private name: string;
|
||||
private tests: Test[] = [];
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Instance variables declared in middle of class
|
||||
- Reader must hunt to find class state
|
||||
- Violates convention of declarations at top
|
||||
|
||||
### No Indentation
|
||||
|
||||
```typescript
|
||||
class FitNesseServer implements SocketServer { private context: FitNesseContext; constructor(context: FitNesseContext) { this.context = context; } serve(socket: Socket): void { this.serveWithTimeout(socket, 10000); } serveWithTimeout(socket: Socket, timeout: number): void { try { const sender = new FitNesseExpediter(socket, this.context); sender.setRequestParsingTimeLimit(timeout); sender.start(); } catch (e) { console.error(e); } } }
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Impossible to discern structure
|
||||
- Cannot identify method boundaries
|
||||
- Cannot see nesting levels
|
||||
|
||||
### Collapsed One-Liners
|
||||
|
||||
```typescript
|
||||
class CommentWidget extends TextWidget {
|
||||
static readonly REGEXP = /^#[^\r\n]*(?:(?:\r\n)|\n|\r)?/;
|
||||
|
||||
constructor(parent: ParentWidget, text: string) { super(parent, text); }
|
||||
render(): string { return ''; }
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Methods are hard to scan
|
||||
- Inconsistent visual rhythm
|
||||
- Easy to miss method boundaries
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Proper Vertical Openness
|
||||
|
||||
```typescript
|
||||
import { regex } from './utils';
|
||||
|
||||
import { ParentWidget } from './widgets';
|
||||
|
||||
export class BoldWidget extends ParentWidget {
|
||||
private static readonly REGEXP = /'''.+?'''/;
|
||||
private pattern = /'''(.+?)'''/gs;
|
||||
|
||||
constructor(parent: ParentWidget, text: string) {
|
||||
super(parent);
|
||||
const match = this.pattern.exec(text);
|
||||
if (match) {
|
||||
this.addChildWidgets(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
render(): string {
|
||||
return `<b>${this.childHtml()}</b>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Blank lines separate logical groups
|
||||
- Easy to scan and identify structure
|
||||
- Each concept is visually distinct
|
||||
|
||||
### Related Code Kept Dense
|
||||
|
||||
```typescript
|
||||
class ReporterConfig {
|
||||
private className: string;
|
||||
private properties: Property[] = [];
|
||||
|
||||
addProperty(property: Property): void {
|
||||
this.properties.push(property);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Related properties grouped together
|
||||
- No unnecessary vertical separation
|
||||
- Fits in one "eye-full"
|
||||
|
||||
### Proper Function Ordering (Caller Above Callee)
|
||||
|
||||
```typescript
|
||||
class WikiPageResponder implements SecureResponder {
|
||||
private page: WikiPage | null = null;
|
||||
private pageData: PageData | null = null;
|
||||
private crawler: PageCrawler;
|
||||
|
||||
makeResponse(context: FitNesseContext, request: Request): Response {
|
||||
const pageName = this.getPageNameOrDefault(request, 'FrontPage');
|
||||
this.loadPage(pageName, context);
|
||||
|
||||
if (!this.page) {
|
||||
return this.notFoundResponse(context, request);
|
||||
}
|
||||
return this.makePageResponse(context);
|
||||
}
|
||||
|
||||
private getPageNameOrDefault(request: Request, defaultName: string): string {
|
||||
const pageName = request.getResource();
|
||||
return pageName || defaultName;
|
||||
}
|
||||
|
||||
private loadPage(resource: string, context: FitNesseContext): void {
|
||||
const path = PathParser.parse(resource);
|
||||
this.crawler = context.root.getPageCrawler();
|
||||
this.page = this.crawler.getPage(context.root, path);
|
||||
|
||||
if (this.page) {
|
||||
this.pageData = this.page.getData();
|
||||
}
|
||||
}
|
||||
|
||||
private notFoundResponse(context: FitNesseContext, request: Request): Response {
|
||||
return new NotFoundResponder().makeResponse(context, request);
|
||||
}
|
||||
|
||||
private makePageResponse(context: FitNesseContext): SimpleResponse {
|
||||
const html = this.makeHtml(context);
|
||||
const response = new SimpleResponse();
|
||||
response.setMaxAge(0);
|
||||
response.setContent(html);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Public entry point at top
|
||||
- Called functions follow their callers
|
||||
- Creates natural top-down reading flow
|
||||
- Easy to trace execution path
|
||||
|
||||
### Proper Indentation
|
||||
|
||||
```typescript
|
||||
class FitNesseServer implements SocketServer {
|
||||
private context: FitNesseContext;
|
||||
|
||||
constructor(context: FitNesseContext) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
serve(socket: Socket): void {
|
||||
this.serveWithTimeout(socket, 10000);
|
||||
}
|
||||
|
||||
serveWithTimeout(socket: Socket, timeout: number): void {
|
||||
try {
|
||||
const sender = new FitNesseExpediter(socket, this.context);
|
||||
sender.setRequestParsingTimeLimit(timeout);
|
||||
sender.start();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Structure immediately visible
|
||||
- Scopes clearly delineated
|
||||
- Easy to navigate mentally
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before
|
||||
|
||||
```typescript
|
||||
class Assert {
|
||||
static assertTrue(message: string, condition: boolean): void { if (!condition) fail(message); }
|
||||
static assertTrue(condition: boolean): void { assertTrue(null, condition); }
|
||||
static assertFalse(message: string, condition: boolean): void { assertTrue(message, !condition); }
|
||||
static assertFalse(condition: boolean): void { assertFalse(null, condition); }
|
||||
}
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```typescript
|
||||
class Assert {
|
||||
static assertTrue(condition: boolean, message?: string): void {
|
||||
if (!condition) {
|
||||
fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
static assertFalse(condition: boolean, message?: string): void {
|
||||
Assert.assertTrue(!condition, message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. Added proper indentation to reveal class structure
|
||||
2. Expanded method bodies with braces
|
||||
3. Combined overloads using optional parameters (TypeScript idiom)
|
||||
4. Added blank line between methods for visual separation
|
||||
5. Used proper braces for if statement
|
||||
@@ -0,0 +1,86 @@
|
||||
# Formatting Knowledge
|
||||
|
||||
Core concepts and foundational understanding for code formatting.
|
||||
|
||||
## Overview
|
||||
|
||||
Code formatting is about communication, not aesthetics. The readability of your code affects all future changes, long after the original functionality has been modified. Good formatting makes code feel professional and establishes trust with readers.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### The Communication Principle
|
||||
|
||||
**Definition**: Code formatting is primarily about communicating intent to other developers.
|
||||
|
||||
Functionality changes frequently, but readability affects maintainability forever. Your coding style and discipline survive even when your original code does not.
|
||||
|
||||
**Key points**:
|
||||
- Formatting is too important to ignore
|
||||
- Formatting is too important to treat religiously
|
||||
- Professional developers prioritize communication
|
||||
|
||||
### Vertical Formatting
|
||||
|
||||
**Definition**: How code is organized from top to bottom within a file.
|
||||
|
||||
Controls file size, concept separation, and the reading flow through your code.
|
||||
|
||||
**Key points**:
|
||||
- Files should typically be 200-500 lines
|
||||
- High-level concepts at top, details at bottom
|
||||
- Related concepts should be vertically close
|
||||
|
||||
### Horizontal Formatting
|
||||
|
||||
**Definition**: How code is organized from left to right within a line.
|
||||
|
||||
Controls line length, whitespace usage, and visual grouping of related elements.
|
||||
|
||||
**Key points**:
|
||||
- Keep lines under 100-120 characters
|
||||
- Use whitespace to show relationships
|
||||
- Indentation reveals code hierarchy
|
||||
|
||||
### The Newspaper Metaphor
|
||||
|
||||
**Definition**: Source files should read like newspaper articles - headline first, then synopsis, then increasing detail.
|
||||
|
||||
The file name is the headline. Top-level code provides the overview. Details and low-level functions come last.
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Vertical openness | Blank lines that separate distinct concepts |
|
||||
| Vertical density | Tightly related code appearing close together |
|
||||
| Vertical distance | Physical separation between related concepts |
|
||||
| Conceptual affinity | Degree to which code elements belong together |
|
||||
| Horizontal density | Elements without spaces indicating close relationship |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Functions**: Small functions enable proper vertical formatting
|
||||
- **Naming**: Good names support the newspaper metaphor
|
||||
- **Classes**: Class structure determines file organization
|
||||
- **Comments**: Excessive comments disrupt vertical density
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Formatting is just about aesthetics
|
||||
**Reality**: Formatting directly impacts code comprehension and maintenance
|
||||
|
||||
- **Myth**: Personal style preferences should prevail
|
||||
**Reality**: Team consistency trumps individual preferences
|
||||
|
||||
- **Myth**: Modern IDEs eliminate formatting concerns
|
||||
**Reality**: Tools help enforce rules, but humans must choose good rules
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Purpose | Formatting is communication, not decoration |
|
||||
| File size | Target 200 lines, rarely exceed 500 |
|
||||
| Line width | Stay under 100-120 characters |
|
||||
| Newspaper metaphor | High-level first, details last |
|
||||
| Team rules | Consistency matters more than personal preference |
|
||||
@@ -0,0 +1,209 @@
|
||||
# Formatting Rules
|
||||
|
||||
Specific guidelines for vertical and horizontal code formatting.
|
||||
|
||||
## Vertical Formatting Rules
|
||||
|
||||
### 1. Keep Files Small
|
||||
|
||||
Target 200 lines, with an upper limit of 500 lines. Significant systems can be built with small files.
|
||||
|
||||
- Small files are easier to understand
|
||||
- If a file grows too large, split it
|
||||
|
||||
### 2. Follow the Newspaper Metaphor
|
||||
|
||||
Structure files with high-level concepts first, details last.
|
||||
|
||||
- File name should tell you if you're in the right module
|
||||
- Top of file: high-level concepts and algorithms
|
||||
- Bottom of file: lowest-level functions and details
|
||||
|
||||
### 3. Separate Concepts with Blank Lines
|
||||
|
||||
Use blank lines between package/import statements, classes, and functions.
|
||||
|
||||
```typescript
|
||||
// Good - concepts separated
|
||||
import { Logger } from './logger';
|
||||
|
||||
import { Config } from './config';
|
||||
|
||||
export class BoldWidget extends ParentWidget {
|
||||
private static readonly REGEXP = /'''.+?'''/;
|
||||
|
||||
constructor(parent: ParentWidget, text: string) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
render(): string {
|
||||
return `<b>${this.childHtml()}</b>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Keep Related Code Dense
|
||||
|
||||
Lines of tightly related code should appear together without blank lines.
|
||||
|
||||
```typescript
|
||||
// Bad - unnecessary separation
|
||||
class ReporterConfig {
|
||||
/**
|
||||
* The class name of the reporter listener
|
||||
*/
|
||||
private className: string;
|
||||
|
||||
/**
|
||||
* The properties of the reporter listener
|
||||
*/
|
||||
private properties: Property[] = [];
|
||||
}
|
||||
|
||||
// Good - related items together
|
||||
class ReporterConfig {
|
||||
private className: string;
|
||||
private properties: Property[] = [];
|
||||
|
||||
addProperty(property: Property): void {
|
||||
this.properties.push(property);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Minimize Vertical Distance
|
||||
|
||||
Related concepts should be vertically close to each other.
|
||||
|
||||
- **Variables**: Declare close to usage, typically at function top
|
||||
- **Instance variables**: Declare at top of class
|
||||
- **Dependent functions**: Caller above callee
|
||||
- **Conceptually related functions**: Keep near each other
|
||||
|
||||
### 6. Order Functions Top-Down
|
||||
|
||||
Functions that are called should appear below their callers.
|
||||
|
||||
```typescript
|
||||
// Good - caller above callee
|
||||
class WikiPageResponder {
|
||||
makeResponse(context: Context, request: Request): Response {
|
||||
const pageName = this.getPageNameOrDefault(request, 'FrontPage');
|
||||
this.loadPage(pageName, context);
|
||||
return this.page ? this.makePageResponse(context) : this.notFoundResponse();
|
||||
}
|
||||
|
||||
private getPageNameOrDefault(request: Request, defaultName: string): string {
|
||||
return request.getResource() || defaultName;
|
||||
}
|
||||
|
||||
private loadPage(resource: string, context: Context): void {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Horizontal Formatting Rules
|
||||
|
||||
### 7. Keep Lines Short
|
||||
|
||||
Limit lines to 100-120 characters maximum.
|
||||
|
||||
- Programmers prefer short lines
|
||||
- Don't rely on horizontal scrolling
|
||||
|
||||
### 8. Use Whitespace to Show Relationships
|
||||
|
||||
Surround operators with spaces; keep function names attached to parentheses.
|
||||
|
||||
```typescript
|
||||
// Good - whitespace shows relationships
|
||||
function measureLine(line: string): void {
|
||||
lineCount++;
|
||||
const lineSize = line.length;
|
||||
totalChars += lineSize;
|
||||
lineWidthHistogram.addLine(lineSize, lineCount);
|
||||
recordWidestLine(lineSize);
|
||||
}
|
||||
```
|
||||
|
||||
### 9. Avoid Column Alignment
|
||||
|
||||
Don't align variable names or values in columns - it emphasizes wrong things.
|
||||
|
||||
```typescript
|
||||
// Bad - aligned columns
|
||||
private socket: Socket;
|
||||
private input: InputStream;
|
||||
private requestParsingTimeLimit: number;
|
||||
|
||||
// Good - natural formatting
|
||||
private socket: Socket;
|
||||
private input: InputStream;
|
||||
private requestParsingTimeLimit: number;
|
||||
```
|
||||
|
||||
### 10. Use Proper Indentation
|
||||
|
||||
Indent code proportionally to its hierarchy level.
|
||||
|
||||
- Class-level declarations: no indent
|
||||
- Methods: one level in from class
|
||||
- Method bodies: one level in from method
|
||||
- Nested blocks: one level per nesting
|
||||
|
||||
### 11. Never Collapse Short Statements
|
||||
|
||||
Always expand and indent scopes, even for one-liners.
|
||||
|
||||
```typescript
|
||||
// Bad - collapsed
|
||||
constructor(parent: ParentWidget, text: string) { super(parent, text); }
|
||||
render(): string { return ''; }
|
||||
|
||||
// Good - expanded
|
||||
constructor(parent: ParentWidget, text: string) {
|
||||
super(parent, text);
|
||||
}
|
||||
|
||||
render(): string {
|
||||
return '';
|
||||
}
|
||||
```
|
||||
|
||||
## Team Rules
|
||||
|
||||
### 12. Agree on Team Standards
|
||||
|
||||
Every team should agree on a single formatting style.
|
||||
|
||||
- Decide on braces, indentation, naming conventions
|
||||
- Encode rules in automated formatters (Prettier, ESLint)
|
||||
- All members follow team rules, not personal preferences
|
||||
- Consistency across the codebase is the goal
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Use automated formatters (Prettier) to enforce consistency
|
||||
- Configure ESLint for style enforcement
|
||||
- Keep format configuration in version control
|
||||
- Run formatters on save or pre-commit
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **Legacy code**: May need gradual reformatting to avoid massive diffs
|
||||
- **Generated code**: May have different formatting requirements
|
||||
- **External dependencies**: Don't reformat third-party code
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| File size | 200-500 lines max |
|
||||
| Line width | 100-120 characters max |
|
||||
| Blank lines | Separate concepts, not related items |
|
||||
| Variable declarations | Close to usage |
|
||||
| Instance properties | Top of class |
|
||||
| Function order | Caller above callee |
|
||||
| Indentation | Always use, never collapse |
|
||||
| Team rules | Follow team standards, use formatters |
|
||||
@@ -0,0 +1,68 @@
|
||||
# Functions Checklist
|
||||
|
||||
Use when writing new functions or reviewing existing code.
|
||||
|
||||
## Size and Structure
|
||||
|
||||
- [ ] Function is 20 lines or less
|
||||
- [ ] Indent level is 2 or less
|
||||
- [ ] Blocks in `if`/`else`/`while` are one line (function calls)
|
||||
- [ ] No sections or comments dividing the function
|
||||
|
||||
## Single Responsibility
|
||||
|
||||
- [ ] Can describe what function does in one sentence without "and"
|
||||
- [ ] Cannot extract another function with a meaningful name
|
||||
- [ ] All statements are at the same abstraction level
|
||||
- [ ] Function does not have hidden side effects
|
||||
|
||||
## Arguments
|
||||
|
||||
- [ ] Has 2 or fewer arguments (3 max in rare cases)
|
||||
- [ ] No boolean/flag arguments
|
||||
- [ ] No output arguments (use return values instead)
|
||||
- [ ] Related arguments grouped into objects
|
||||
|
||||
## Naming
|
||||
|
||||
- [ ] Name describes what the function does
|
||||
- [ ] Verb/noun pair for monadic functions
|
||||
- [ ] Consistent with similar functions in the codebase
|
||||
- [ ] Long enough to be clear (don't abbreviate)
|
||||
|
||||
## Error Handling
|
||||
|
||||
- [ ] Uses exceptions, not error codes
|
||||
- [ ] Try/catch bodies extracted to separate functions
|
||||
- [ ] Error handling function does nothing else
|
||||
- [ ] No error code enums
|
||||
|
||||
## Command Query Separation
|
||||
|
||||
- [ ] Function either changes state OR returns information, not both
|
||||
- [ ] If returning boolean, it's asking a question, not performing action
|
||||
- [ ] State changes don't return status codes
|
||||
|
||||
## Red Flags
|
||||
|
||||
Stop and refactor if you find:
|
||||
|
||||
- Function longer than 20 lines
|
||||
- More than 3 arguments
|
||||
- Boolean parameters controlling behavior
|
||||
- Mixed abstraction levels in one function
|
||||
- Same switch statement in multiple places
|
||||
- Function name includes "And" or "Or"
|
||||
- Side effects hidden in function body
|
||||
- Deep nesting (3+ levels)
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Aspect | Ideal | Acceptable | Red Flag |
|
||||
|--------|-------|------------|----------|
|
||||
| Lines | 2-5 | 6-20 | >20 |
|
||||
| Arguments | 0-1 | 2 | 3+ |
|
||||
| Indent depth | 1 | 2 | 3+ |
|
||||
| Abstraction levels | 1 | 1 | 2+ mixed |
|
||||
| Side effects | 0 | Named | Hidden |
|
||||
| Returns per function | 1 | 2-3 small fn | Many in large fn |
|
||||
@@ -0,0 +1,221 @@
|
||||
# Functions Examples
|
||||
|
||||
Code examples demonstrating clean function principles.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Long Function with Mixed Abstraction
|
||||
|
||||
```typescript
|
||||
const testableHtml = async (
|
||||
pageData: PageData,
|
||||
includeSuiteSetup: boolean
|
||||
): Promise<string> => {
|
||||
const wikiPage = pageData.getWikiPage();
|
||||
let buffer = "";
|
||||
if (pageData.hasAttribute("Test")) {
|
||||
if (includeSuiteSetup) {
|
||||
const suiteSetup = await PageCrawlerImpl.getInheritedPage(
|
||||
SuiteResponder.SUITE_SETUP_NAME, wikiPage
|
||||
);
|
||||
if (suiteSetup !== null) {
|
||||
const pagePath = suiteSetup.getPageCrawler().getFullPath(suiteSetup);
|
||||
const pagePathName = PathParser.render(pagePath);
|
||||
buffer += `!include -setup .${pagePathName}\n`;
|
||||
}
|
||||
}
|
||||
// ... 40 more lines of similar code
|
||||
}
|
||||
pageData.setContent(buffer);
|
||||
return pageData.getHtml();
|
||||
};
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Too long - hard to understand in 3 minutes
|
||||
- Mixed abstraction levels (high: `getHtml()`, low: string concatenation)
|
||||
- Duplicated algorithm for setup/teardown handling
|
||||
- Does many things: creates buffers, fetches pages, builds strings, generates HTML
|
||||
|
||||
### Switch Statement Violating OCP
|
||||
|
||||
```typescript
|
||||
const calculatePay = (employee: Employee): Money => {
|
||||
switch (employee.type) {
|
||||
case EmployeeType.COMMISSIONED:
|
||||
return calculateCommissionedPay(employee);
|
||||
case EmployeeType.HOURLY:
|
||||
return calculateHourlyPay(employee);
|
||||
case EmployeeType.SALARIED:
|
||||
return calculateSalariedPay(employee);
|
||||
default:
|
||||
throw new InvalidEmployeeTypeError(employee.type);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Large and grows with each new type
|
||||
- Violates Single Responsibility Principle
|
||||
- Violates Open/Closed Principle
|
||||
- Same structure repeated in `isPayday()`, `deliverPay()`, etc.
|
||||
|
||||
### Hidden Side Effect
|
||||
|
||||
```typescript
|
||||
const checkPassword = (userName: string, password: string): boolean => {
|
||||
const user = UserGateway.findByName(userName);
|
||||
if (user !== null) {
|
||||
const codedPhrase = user.getPhraseEncodedByPassword();
|
||||
const phrase = cryptographer.decrypt(codedPhrase, password);
|
||||
if (phrase === "Valid Password") {
|
||||
Session.initialize(); // Hidden side effect!
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Name says "check password" but also initializes session
|
||||
- Creates temporal coupling - can only call when safe to initialize
|
||||
- Caller may accidentally erase session data
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Small, Focused Function
|
||||
|
||||
```typescript
|
||||
const renderPageWithSetupsAndTeardowns = async (
|
||||
pageData: PageData,
|
||||
isSuite: boolean
|
||||
): Promise<string> => {
|
||||
if (isTestPage(pageData)) {
|
||||
await includeSetupAndTeardownPages(pageData, isSuite);
|
||||
}
|
||||
return pageData.getHtml();
|
||||
};
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Very small (4 lines)
|
||||
- Does one thing: includes setups/teardowns and renders
|
||||
- Clear intent from function name
|
||||
- All statements at same abstraction level
|
||||
|
||||
### Polymorphic Solution to Switch
|
||||
|
||||
```typescript
|
||||
// Abstract base
|
||||
interface Employee {
|
||||
isPayday(): boolean;
|
||||
calculatePay(): Money;
|
||||
deliverPay(pay: Money): void;
|
||||
}
|
||||
|
||||
// Factory hides the switch
|
||||
interface EmployeeFactory {
|
||||
makeEmployee(record: EmployeeRecord): Employee;
|
||||
}
|
||||
|
||||
class EmployeeFactoryImpl implements EmployeeFactory {
|
||||
makeEmployee(record: EmployeeRecord): Employee {
|
||||
switch (record.type) {
|
||||
case EmployeeType.COMMISSIONED:
|
||||
return new CommissionedEmployee(record);
|
||||
case EmployeeType.HOURLY:
|
||||
return new HourlyEmployee(record);
|
||||
case EmployeeType.SALARIED:
|
||||
return new SalariedEmployee(record);
|
||||
default:
|
||||
throw new InvalidEmployeeTypeError(record.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Switch appears only once, in the factory
|
||||
- New types only require adding a new class
|
||||
- Methods dispatch polymorphically
|
||||
- Rest of system doesn't see the switch
|
||||
|
||||
### Command Query Separation
|
||||
|
||||
```typescript
|
||||
// Bad - mixed command and query
|
||||
const set = (attribute: string, value: string): boolean => { /* ... */ };
|
||||
|
||||
// Good - separated
|
||||
const attributeExists = (attribute: string): boolean => {
|
||||
return attributes.has(attribute);
|
||||
};
|
||||
|
||||
const setAttribute = (attribute: string, value: string): void => {
|
||||
attributes.set(attribute, value);
|
||||
};
|
||||
|
||||
// Usage is clear
|
||||
if (attributeExists("username")) {
|
||||
setAttribute("username", "unclebob");
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- No ambiguity about what each function does
|
||||
- Query returns info, command changes state
|
||||
- Code reads naturally
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before
|
||||
|
||||
```typescript
|
||||
const delete_ = async (page: Page): Promise<void> => {
|
||||
if (await deletePage(page) === E_OK) {
|
||||
if (await registry.deleteReference(page.name) === E_OK) {
|
||||
if (await configKeys.deleteKey(page.name.makeKey()) === E_OK) {
|
||||
logger.log("page deleted");
|
||||
} else {
|
||||
logger.log("configKey not deleted");
|
||||
}
|
||||
} else {
|
||||
logger.log("deleteReference from registry failed");
|
||||
}
|
||||
} else {
|
||||
logger.log("delete failed");
|
||||
throw new Error("E_ERROR");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```typescript
|
||||
const deletePage = async (page: Page): Promise<void> => {
|
||||
try {
|
||||
await deletePageAndAllReferences(page);
|
||||
} catch (error) {
|
||||
logError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const deletePageAndAllReferences = async (page: Page): Promise<void> => {
|
||||
await pageService.delete(page);
|
||||
await registry.deleteReference(page.name);
|
||||
await configKeys.deleteKey(page.name.makeKey());
|
||||
};
|
||||
|
||||
const logError = (error: Error): void => {
|
||||
logger.log(error.message);
|
||||
};
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. **Used exceptions instead of error codes** - Eliminated deep nesting
|
||||
2. **Extracted try/catch body** - `deletePageAndAllReferences` handles happy path
|
||||
3. **Extracted error logging** - `logError` handles error path
|
||||
4. **Each function does one thing** - Delete, do operations, or log errors
|
||||
5. **Consistent abstraction level** - Each function stays at one level
|
||||
@@ -0,0 +1,91 @@
|
||||
# Functions Knowledge
|
||||
|
||||
Core concepts and foundational understanding for writing clean functions.
|
||||
|
||||
## Overview
|
||||
|
||||
Functions are the first line of organization in any program. Well-written functions tell a story, are easy to read, and communicate intent clearly. The goal is to make code read like a top-down narrative, with each function leading naturally to the next.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Small Functions
|
||||
|
||||
**Definition**: Functions should be very small, ideally 2-5 lines, rarely exceeding 20 lines.
|
||||
|
||||
Small functions are easier to read, understand, and test. Each function should be "transparently obvious" and tell a story that leads to the next function.
|
||||
|
||||
**Key points**:
|
||||
- Blocks in `if`/`else`/`while` should be one line (a function call)
|
||||
- Indent level should not exceed one or two
|
||||
- If it's hard to shrink further, you've reached the right size
|
||||
|
||||
### Do One Thing
|
||||
|
||||
**Definition**: A function should do one thing, do it well, and do it only.
|
||||
|
||||
A function does "one thing" if you can describe it as a brief TO paragraph and all steps are one level of abstraction below the function name.
|
||||
|
||||
**Key points**:
|
||||
- If you can extract another function with a meaningful (non-restating) name, it does more than one thing
|
||||
- Functions that can be divided into sections are doing more than one thing
|
||||
|
||||
### Abstraction Levels
|
||||
|
||||
**Definition**: All statements within a function should be at the same level of abstraction.
|
||||
|
||||
Mixing high-level concepts (`getHtml()`) with low-level details (`.append("\n")`) is confusing. Readers can't tell what's essential vs. detail.
|
||||
|
||||
**Key points**:
|
||||
- High level: business logic, domain operations
|
||||
- Intermediate level: utility operations
|
||||
- Low level: string manipulation, data formatting
|
||||
|
||||
### The Stepdown Rule
|
||||
|
||||
**Definition**: Code should read like a top-down narrative, with each function followed by those at the next level of abstraction.
|
||||
|
||||
Write code as a set of TO paragraphs, each describing the current level and referencing the next level down.
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Niladic | Function with zero arguments (ideal) |
|
||||
| Monadic | Function with one argument |
|
||||
| Dyadic | Function with two arguments |
|
||||
| Triadic | Function with three arguments (avoid) |
|
||||
| Polyadic | Function with more than three arguments (never) |
|
||||
| Side Effect | Hidden action beyond the function's stated purpose |
|
||||
| Temporal Coupling | When a function can only be called at certain times |
|
||||
| Command | Function that changes state |
|
||||
| Query | Function that returns information |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Naming**: Small, focused functions are easier to name descriptively
|
||||
- **Testing**: Fewer arguments and single responsibility make testing simpler
|
||||
- **Error Handling**: Separate error handling from business logic using exceptions
|
||||
- **DRY Principle**: Extract duplicated code into well-named functions
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Functions need comments to explain what they do
|
||||
**Reality**: A well-named small function is self-documenting
|
||||
|
||||
- **Myth**: Extracting small functions hurts performance
|
||||
**Reality**: Readability matters more; compilers optimize well
|
||||
|
||||
- **Myth**: Functions should have one return statement
|
||||
**Reality**: Multiple returns are fine in small functions
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Small | 2-5 lines ideal, max 20 lines |
|
||||
| Do One Thing | One level of abstraction below the function name |
|
||||
| Abstraction | All statements at same level |
|
||||
| Stepdown | Code reads top-down like TO paragraphs |
|
||||
| Arguments | Zero is best, three is maximum |
|
||||
| Side Effects | Functions should not have hidden behaviors |
|
||||
| Command/Query | Do something OR answer something, not both |
|
||||
@@ -0,0 +1,181 @@
|
||||
# Functions Rules
|
||||
|
||||
Guidelines for writing clean, readable, and maintainable functions.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Keep Functions Small
|
||||
|
||||
Functions should be very small, ideally 2-5 lines, rarely exceeding 20.
|
||||
|
||||
- Blocks in `if`/`else`/`while` should be one line (a function call)
|
||||
- Maximum indent level of one or two
|
||||
- Each function should be transparently obvious
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - too long, multiple levels
|
||||
const processOrder = (order: Order): void => {
|
||||
if (order.isValid()) {
|
||||
for (const item of order.items) {
|
||||
if (item.inStock) {
|
||||
inventory.reserve(item);
|
||||
// ... 20 more lines
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Good - small, delegating
|
||||
const processOrder = (order: Order): void => {
|
||||
if (order.isValid()) {
|
||||
reserveItems(order);
|
||||
calculateTotals(order);
|
||||
notifyCustomer(order);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Do One Thing
|
||||
|
||||
Functions should do one thing, do it well, and do it only.
|
||||
|
||||
- All steps should be one level of abstraction below the function name
|
||||
- If you can extract a meaningful function, it's doing too much
|
||||
- If it has sections, it's doing too much
|
||||
|
||||
### 3. One Level of Abstraction
|
||||
|
||||
All statements in a function should be at the same abstraction level.
|
||||
|
||||
- Don't mix `getUser()` with `str.toLowerCase()`
|
||||
- High-level calls with high-level, low-level with low-level
|
||||
|
||||
### 4. Minimize Arguments
|
||||
|
||||
Zero arguments is ideal. Three is the maximum.
|
||||
|
||||
- Zero (niladic): Best
|
||||
- One (monadic): Good for questions or transformations
|
||||
- Two (dyadic): Acceptable, but harder to understand
|
||||
- Three (triadic): Avoid - very hard to understand
|
||||
- More: Never
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - too many arguments
|
||||
const createUser = (
|
||||
name: string,
|
||||
email: string,
|
||||
age: number,
|
||||
address: string,
|
||||
phone: string
|
||||
): User => { /* ... */ };
|
||||
|
||||
// Good - use an object
|
||||
interface CreateUserParams {
|
||||
name: string;
|
||||
email: string;
|
||||
age: number;
|
||||
address: string;
|
||||
phone: string;
|
||||
}
|
||||
|
||||
const createUser = (params: CreateUserParams): User => { /* ... */ };
|
||||
```
|
||||
|
||||
### 5. No Flag Arguments
|
||||
|
||||
Never pass a boolean to control function behavior.
|
||||
|
||||
- Flag arguments mean the function does more than one thing
|
||||
- Split into two functions instead
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad
|
||||
const render = (data: Data, isSuite: boolean): string => { /* ... */ };
|
||||
|
||||
// Good
|
||||
const renderSuite = (data: Data): string => { /* ... */ };
|
||||
const renderSingleTest = (data: Data): string => { /* ... */ };
|
||||
```
|
||||
|
||||
### 6. No Side Effects
|
||||
|
||||
Functions should not have hidden behaviors beyond their stated purpose.
|
||||
|
||||
- Don't modify global state unexpectedly
|
||||
- Don't modify input parameters unexpectedly
|
||||
- If side effects are necessary, make them explicit in the name
|
||||
|
||||
### 7. Command Query Separation
|
||||
|
||||
Functions should either do something OR answer something, never both.
|
||||
|
||||
- Commands: Change state, return nothing (or void)
|
||||
- Queries: Return information, change nothing
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - does both
|
||||
const set = (attr: string, value: string): boolean => { /* ... */ };
|
||||
if (set("username", "bob")) { /* confusing! */ }
|
||||
|
||||
// Good - separated
|
||||
const attributeExists = (attr: string): boolean => { /* ... */ };
|
||||
const setAttribute = (attr: string, value: string): void => { /* ... */ };
|
||||
|
||||
if (attributeExists("username")) {
|
||||
setAttribute("username", "bob");
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Use Exceptions, Not Error Codes
|
||||
|
||||
Throw exceptions instead of returning error codes.
|
||||
|
||||
- Error codes force immediate handling, causing deep nesting
|
||||
- Exceptions separate happy path from error handling
|
||||
- Error code enums become dependency magnets
|
||||
|
||||
### 9. Extract Try/Catch Blocks
|
||||
|
||||
Error handling is one thing - functions that handle errors should do nothing else.
|
||||
|
||||
- If `try` exists, it should be the first word in the function
|
||||
- Nothing should come after `catch`/`finally` blocks
|
||||
- Extract the try body and catch body into separate functions
|
||||
|
||||
### 10. Use Descriptive Names
|
||||
|
||||
Long descriptive names are better than short cryptic ones.
|
||||
|
||||
- Names should say what the function does
|
||||
- Be consistent: use same phrases, nouns, verbs
|
||||
- Spend time choosing names - it clarifies design
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Use verb/noun pairs for monadic functions: `writeField(name)`
|
||||
- Encode argument names in function name: `assertExpectedEqualsActual(expected, actual)`
|
||||
- Avoid output arguments - use return values or `this`
|
||||
- Multiple `return`/`break`/`continue` are fine in small functions
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **Switch statements**: Acceptable if used once, to create polymorphic objects, hidden behind a factory
|
||||
- **Multiple returns**: Fine in small functions where it improves clarity
|
||||
- **Two arguments**: Acceptable for ordered pairs like `Point(x, y)` or natural pairs
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Guideline |
|
||||
|------|-----------|
|
||||
| Size | 2-5 lines ideal, max 20 |
|
||||
| Arguments | 0-2 preferred, max 3 |
|
||||
| Flag args | Never use |
|
||||
| Side effects | Make explicit or eliminate |
|
||||
| Command/Query | Separate state changes from returns |
|
||||
| Error handling | Use exceptions, extract try/catch |
|
||||
| Naming | Long and descriptive beats short |
|
||||
@@ -0,0 +1,188 @@
|
||||
# Naming Examples
|
||||
|
||||
Code examples demonstrating meaningful naming principles in TypeScript.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Non-Revealing Names
|
||||
|
||||
```typescript
|
||||
const d = 0; // elapsed time in days
|
||||
|
||||
function getThem(): number[][] {
|
||||
const list1: number[][] = [];
|
||||
for (const x of theList) {
|
||||
if (x[0] === 4) list1.push(x);
|
||||
}
|
||||
return list1;
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**: `d`, `getThem`, `list1`, `x` reveal nothing; magic number `4` unexplained
|
||||
|
||||
### Disinformative Names
|
||||
|
||||
```typescript
|
||||
const accountList = new Set<Account>(); // not actually a list!
|
||||
let a = l; // l looks like 1
|
||||
if (O === l) a = O1; // O looks like 0
|
||||
```
|
||||
|
||||
**Problems**: `accountList` lies about type; `l` and `O` are visually confusing
|
||||
|
||||
### Number-Series and Noise Words
|
||||
|
||||
```typescript
|
||||
function copyChars(a1: string[], a2: string[]): void {
|
||||
for (let i = 0; i < a1.length; i++) a2[i] = a1[i];
|
||||
}
|
||||
|
||||
class Product {}
|
||||
class ProductInfo {} // What's the difference?
|
||||
class ProductData {} // Info and Data add no meaning
|
||||
```
|
||||
|
||||
**Problems**: `a1`/`a2` are meaningless; noise words create false distinctions
|
||||
|
||||
### Unpronounceable and Unsearchable
|
||||
|
||||
```typescript
|
||||
class DtaRcrd102 {
|
||||
private genymdhms: Date; // "gen why emm dee aich emm ess"
|
||||
private readonly pszqint = "102";
|
||||
}
|
||||
|
||||
for (let j = 0; j < 34; j++) {
|
||||
s += (t[j] * 4) / 5; // Can't search for 4 or 5
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**: Can't discuss verbally; can't find magic numbers in codebase
|
||||
|
||||
### Encoded Names
|
||||
|
||||
```typescript
|
||||
class Part {
|
||||
private m_dsc: string; // m_ prefix is clutter
|
||||
setName(name: string): void { this.m_dsc = name; }
|
||||
}
|
||||
|
||||
let strName: string; // Hungarian notation duplicates type system
|
||||
let phoneString: PhoneNumber; // Encoding lies when type changes
|
||||
```
|
||||
|
||||
**Problems**: Prefixes add noise; encodings become lies when types change
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Intention-Revealing Names
|
||||
|
||||
```typescript
|
||||
const elapsedTimeInDays = 0;
|
||||
const daysSinceCreation = 0;
|
||||
|
||||
function getFlaggedCells(): Cell[] {
|
||||
const flaggedCells: Cell[] = [];
|
||||
for (const cell of gameBoard) {
|
||||
if (cell.isFlagged()) flaggedCells.push(cell);
|
||||
}
|
||||
return flaggedCells;
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: Names reveal units, purpose, and domain context
|
||||
|
||||
### Pronounceable and Searchable
|
||||
|
||||
```typescript
|
||||
class Customer {
|
||||
private generationTimestamp: Date;
|
||||
private modificationTimestamp: Date;
|
||||
}
|
||||
|
||||
const WORK_DAYS_PER_WEEK = 5;
|
||||
for (let j = 0; j < NUMBER_OF_TASKS; j++) {
|
||||
const realTaskWeeks = taskEstimate[j] / WORK_DAYS_PER_WEEK;
|
||||
sum += realTaskWeeks;
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: Can discuss verbally; constants are searchable
|
||||
|
||||
### Clean Class Without Encodings
|
||||
|
||||
```typescript
|
||||
class Part {
|
||||
private description: string;
|
||||
setDescription(description: string): void {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**: No prefixes; name matches concept; method matches field
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before: Variables Without Context
|
||||
|
||||
```typescript
|
||||
function printGuessStatistics(candidate: string, count: number): void {
|
||||
let number: string, verb: string, pluralModifier: string;
|
||||
if (count === 0) { number = "no"; verb = "are"; pluralModifier = "s"; }
|
||||
else if (count === 1) { number = "1"; verb = "is"; pluralModifier = ""; }
|
||||
else { number = count.toString(); verb = "are"; pluralModifier = "s"; }
|
||||
console.log(`There ${verb} ${number} ${candidate}${pluralModifier}`);
|
||||
}
|
||||
```
|
||||
|
||||
### After: Context Through Class
|
||||
|
||||
```typescript
|
||||
class GuessStatisticsMessage {
|
||||
private number: string;
|
||||
private verb: string;
|
||||
private pluralModifier: string;
|
||||
|
||||
make(candidate: string, count: number): string {
|
||||
this.createPluralDependentMessageParts(count);
|
||||
return `There ${this.verb} ${this.number} ${candidate}${this.pluralModifier}`;
|
||||
}
|
||||
|
||||
private createPluralDependentMessageParts(count: number): void {
|
||||
if (count === 0) this.thereAreNoLetters();
|
||||
else if (count === 1) this.thereIsOneLetter();
|
||||
else this.thereAreManyLetters(count);
|
||||
}
|
||||
|
||||
private thereAreManyLetters(count: number): void {
|
||||
this.number = count.toString(); this.verb = "are"; this.pluralModifier = "s";
|
||||
}
|
||||
private thereIsOneLetter(): void {
|
||||
this.number = "1"; this.verb = "is"; this.pluralModifier = "";
|
||||
}
|
||||
private thereAreNoLetters(): void {
|
||||
this.number = "no"; this.verb = "are"; this.pluralModifier = "s";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Changes**: Class provides context; method names reveal intent; each method does one thing
|
||||
|
||||
## Method Naming Patterns
|
||||
|
||||
```typescript
|
||||
// Accessors: get prefix
|
||||
const name = employee.getName();
|
||||
|
||||
// Mutators: set prefix
|
||||
customer.setName("Mike");
|
||||
|
||||
// Predicates: is/has prefix
|
||||
if (paycheck.isPosted()) { }
|
||||
if (user.hasPermission("admin")) { }
|
||||
|
||||
// Factory methods: describe what they create from
|
||||
const point = Complex.fromRealNumber(23.0);
|
||||
const user = User.createGuest();
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
# Naming Knowledge
|
||||
|
||||
Core concepts and foundational understanding for meaningful names in code.
|
||||
|
||||
## Overview
|
||||
|
||||
Names are everywhere in software: variables, functions, arguments, classes, modules, files, and directories. Good names make code readable without comments. The investment in choosing good names pays dividends in maintainability and comprehension.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Intention-Revealing Names
|
||||
|
||||
**Definition**: Names that answer why something exists, what it does, and how it's used.
|
||||
|
||||
A name should make comments unnecessary. If you need a comment to explain what a variable is, the name doesn't reveal its intent.
|
||||
|
||||
**Key points**:
|
||||
- Names should answer the "big questions" about the thing being named
|
||||
- Good names eliminate the need for explanatory comments
|
||||
- Take time choosing names; change them when you find better ones
|
||||
|
||||
### Meaningful Distinctions
|
||||
|
||||
**Definition**: Names that differ in ways that convey actual semantic differences.
|
||||
|
||||
When names must be different, they should mean something different. Number-series naming (a1, a2) and noise words (Info, Data) create distinctions without meaning.
|
||||
|
||||
**Key points**:
|
||||
- Avoid arbitrary differences just to satisfy the compiler
|
||||
- Noise words like Info, Data, Object add no meaning
|
||||
- If you can't tell the difference between two names, readers can't either
|
||||
|
||||
### Searchability
|
||||
|
||||
**Definition**: Names that can be easily located across a codebase.
|
||||
|
||||
Single-letter names and magic numbers are nearly impossible to search for. Longer, descriptive names enable effective code navigation.
|
||||
|
||||
**Key points**:
|
||||
- Name length should correspond to scope size
|
||||
- Constants should have searchable names
|
||||
- Single letters acceptable only in tiny local scopes (loop counters)
|
||||
|
||||
### Mental Mapping
|
||||
|
||||
**Definition**: The cognitive load required to translate a name into its actual meaning.
|
||||
|
||||
Readers shouldn't have to mentally map your names to concepts they already know. Professional programmers prioritize clarity over cleverness.
|
||||
|
||||
**Key points**:
|
||||
- Clarity is king
|
||||
- Avoid forcing readers to remember what abbreviations mean
|
||||
- Use domain-appropriate terminology directly
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Disinformation | Names that lie or mislead about what something is |
|
||||
| Noise words | Meaningless additions like Info, Data, Object, String |
|
||||
| Hungarian Notation | Encoding type information in name prefixes (obsolete) |
|
||||
| Solution domain | Technical/CS terms programmers already know |
|
||||
| Problem domain | Business/domain-specific terminology |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Readability**: Names are the primary vehicle for code comprehension
|
||||
- **Comments**: Good names reduce or eliminate need for comments
|
||||
- **Refactoring**: Renaming is a fundamental refactoring operation
|
||||
- **Communication**: Code is read more than written; names enable team communication
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Short names are always better for brevity
|
||||
**Reality**: Clarity trumps brevity; name length should match scope size
|
||||
|
||||
- **Myth**: Adding prefixes like `m_` or `I` adds useful information
|
||||
**Reality**: Modern IDEs make these encodings unnecessary clutter
|
||||
|
||||
- **Myth**: Consistent word choice means using the same word everywhere
|
||||
**Reality**: Different concepts deserve different words even if similar
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Intention-revealing | Name answers why, what, and how |
|
||||
| Meaningful distinctions | Different names = different meanings |
|
||||
| Searchability | Longer names for larger scopes |
|
||||
| No mental mapping | Reader shouldn't need to translate |
|
||||
| Solution domain | Use CS terms for technical concepts |
|
||||
| Problem domain | Use business terms for domain concepts |
|
||||
142
.agents/skills/typescript-clean-code/references/naming/rules.md
Normal file
142
.agents/skills/typescript-clean-code/references/naming/rules.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# Naming Rules
|
||||
|
||||
Specific guidelines for choosing meaningful names in code.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Use Intention-Revealing Names
|
||||
|
||||
Names should tell you why something exists, what it does, and how it's used.
|
||||
|
||||
- A name requiring a comment doesn't reveal its intent
|
||||
- Include units of measurement when relevant
|
||||
|
||||
```typescript
|
||||
// Bad
|
||||
const d = 0; // elapsed time in days
|
||||
|
||||
// Good
|
||||
const elapsedTimeInDays = 0;
|
||||
```
|
||||
|
||||
### 2. Avoid Disinformation
|
||||
|
||||
Don't use names that lie about what something is.
|
||||
|
||||
- Don't use `list` unless it's actually a List type
|
||||
- Avoid names that look too similar to each other
|
||||
- Never use `l` (lowercase L) or `O` as variable names
|
||||
|
||||
### 3. Make Meaningful Distinctions
|
||||
|
||||
If names must be different, they should mean something different.
|
||||
|
||||
- Avoid number-series naming (a1, a2, a3)
|
||||
- Avoid noise words (Info, Data, Object, String)
|
||||
|
||||
```typescript
|
||||
// Bad: a1, a2 mean nothing
|
||||
function copyChars(a1: string[], a2: string[]): void { }
|
||||
|
||||
// Good: source, destination are meaningful
|
||||
function copyChars(source: string[], destination: string[]): void { }
|
||||
```
|
||||
|
||||
### 4. Use Pronounceable Names
|
||||
|
||||
If you can't say it, you can't discuss it.
|
||||
|
||||
- Programming is a social activity
|
||||
- New developers shouldn't need names explained
|
||||
|
||||
### 5. Use Searchable Names
|
||||
|
||||
Single letters and magic numbers can't be found in a codebase.
|
||||
|
||||
- Name length should correspond to scope size
|
||||
- Single letters only in small local scopes
|
||||
- Constants must have descriptive names
|
||||
|
||||
```typescript
|
||||
// Bad: can't search for 5
|
||||
s += (t[j] * 4) / 5;
|
||||
|
||||
// Good: WORK_DAYS_PER_WEEK is searchable
|
||||
const realTaskWeeks = realTaskDays / WORK_DAYS_PER_WEEK;
|
||||
```
|
||||
|
||||
### 6. Avoid Encodings
|
||||
|
||||
Don't encode type or scope information into names.
|
||||
|
||||
- No Hungarian Notation (strName, intCount)
|
||||
- No member prefixes (m_description, _name)
|
||||
- Leave interfaces unadorned; encode implementations if needed (ShapeFactoryImpl)
|
||||
|
||||
### 7. Class Names Should Be Nouns
|
||||
|
||||
- Good: Customer, WikiPage, Account, AddressParser
|
||||
- Avoid: Manager, Processor, Data, Info
|
||||
- Never use verbs for class names
|
||||
|
||||
### 8. Method Names Should Be Verbs
|
||||
|
||||
- Accessors: `get` prefix (getName)
|
||||
- Mutators: `set` prefix (setName)
|
||||
- Predicates: `is`/`has` prefix (isPosted, hasPermission)
|
||||
- Use static factory methods over overloaded constructors
|
||||
|
||||
### 9. Pick One Word Per Concept
|
||||
|
||||
- Don't mix fetch, retrieve, and get for the same concept
|
||||
- Don't mix controller, manager, and driver arbitrarily
|
||||
- Consistent lexicon helps programmers navigate code
|
||||
|
||||
### 10. Don't Pun
|
||||
|
||||
Don't use the same word for different concepts.
|
||||
|
||||
- If `add` concatenates values, don't use it for collection insertion
|
||||
- Use `insert` or `append` when semantics differ
|
||||
|
||||
### 11. Add Meaningful Context
|
||||
|
||||
Place names in context through enclosing structures.
|
||||
|
||||
- Variables alone may be ambiguous (`state` could be anything)
|
||||
- Group related variables in classes
|
||||
- Use prefixes only as a last resort
|
||||
|
||||
### 12. Don't Add Gratuitous Context
|
||||
|
||||
Shorter names are better if they're clear.
|
||||
|
||||
- Don't prefix every class with application name
|
||||
- Add only necessary context
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Use solution domain names (CS terms) for technical concepts
|
||||
- Use problem domain names for business concepts
|
||||
- Rename fearlessly; modern tools make it safe
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **Loop counters**: `i`, `j`, `k` acceptable in small loops
|
||||
- **Lambda parameters**: Short names OK in tiny scopes
|
||||
- **Well-known abbreviations**: `id`, `url`, `html` are acceptable
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Intention-revealing | Names answer why, what, how |
|
||||
| No disinformation | Don't lie about types or purpose |
|
||||
| Meaningful distinctions | Different names = different meanings |
|
||||
| Pronounceable | You can discuss it out loud |
|
||||
| Searchable | Can be found with grep/search |
|
||||
| No encodings | No type prefixes or Hungarian Notation |
|
||||
| Nouns for classes | Customer, not Manage |
|
||||
| Verbs for methods | calculatePay, not payment |
|
||||
| One word per concept | Consistent vocabulary |
|
||||
| No puns | Same word = same concept |
|
||||
@@ -0,0 +1,201 @@
|
||||
# Practicing Examples
|
||||
|
||||
Practice exercises and kata examples for deliberate skill development.
|
||||
|
||||
## Classic Kata Examples
|
||||
|
||||
### The Bowling Game Kata
|
||||
|
||||
A 30-minute TDD exercise that calculates bowling scores.
|
||||
|
||||
**What you practice**:
|
||||
- TDD red-green-refactor cycle
|
||||
- Incremental design
|
||||
- Handling edge cases (strikes, spares)
|
||||
|
||||
```typescript
|
||||
// Start: Write a failing test
|
||||
describe('BowlingGame', () => {
|
||||
let game: BowlingGame;
|
||||
|
||||
beforeEach(() => {
|
||||
game = new BowlingGame();
|
||||
});
|
||||
|
||||
it('should score zero for gutter game', () => {
|
||||
rollMany(20, 0);
|
||||
expect(game.score()).toBe(0);
|
||||
});
|
||||
|
||||
it('should score 20 for all ones', () => {
|
||||
rollMany(20, 1);
|
||||
expect(game.score()).toBe(20);
|
||||
});
|
||||
|
||||
// Continue with spare, strike, perfect game...
|
||||
});
|
||||
```
|
||||
|
||||
### Prime Factors Kata
|
||||
|
||||
Decompose a number into its prime factors.
|
||||
|
||||
**What you practice**:
|
||||
- Algorithm development through TDD
|
||||
- Simple incremental steps
|
||||
- Refactoring as patterns emerge
|
||||
|
||||
```typescript
|
||||
// Tests drive the implementation
|
||||
describe('PrimeFactors', () => {
|
||||
it('returns empty for 1', () => {
|
||||
expect(primeFactors(1)).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns [2] for 2', () => {
|
||||
expect(primeFactors(2)).toEqual([2]);
|
||||
});
|
||||
|
||||
it('returns [3] for 3', () => {
|
||||
expect(primeFactors(3)).toEqual([3]);
|
||||
});
|
||||
|
||||
it('returns [2, 2] for 4', () => {
|
||||
expect(primeFactors(4)).toEqual([2, 2]);
|
||||
});
|
||||
|
||||
// Pattern emerges, refactor...
|
||||
});
|
||||
```
|
||||
|
||||
### Word Wrap Kata
|
||||
|
||||
Wrap text at a given column width.
|
||||
|
||||
**What you practice**:
|
||||
- String manipulation
|
||||
- Edge case handling
|
||||
- Clean function design
|
||||
|
||||
```typescript
|
||||
describe('WordWrap', () => {
|
||||
it('returns empty for null', () => {
|
||||
expect(wrap(null, 10)).toBe('');
|
||||
});
|
||||
|
||||
it('returns text unchanged if shorter than width', () => {
|
||||
expect(wrap('hello', 10)).toBe('hello');
|
||||
});
|
||||
|
||||
it('wraps at word boundary', () => {
|
||||
expect(wrap('hello world', 7)).toBe('hello\nworld');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Wasa (Ping-Pong) Session Example
|
||||
|
||||
### Setup
|
||||
|
||||
Two developers, one problem, alternating roles.
|
||||
|
||||
**Round 1**: Developer A writes test
|
||||
```typescript
|
||||
it('should return fizz for multiples of 3', () => {
|
||||
expect(fizzBuzz(3)).toBe('fizz');
|
||||
});
|
||||
```
|
||||
|
||||
**Round 2**: Developer B makes it pass, writes next test
|
||||
```typescript
|
||||
function fizzBuzz(n: number): string {
|
||||
if (n % 3 === 0) return 'fizz';
|
||||
return String(n);
|
||||
}
|
||||
|
||||
it('should return buzz for multiples of 5', () => {
|
||||
expect(fizzBuzz(5)).toBe('buzz');
|
||||
});
|
||||
```
|
||||
|
||||
**Round 3**: Developer A makes it pass, writes next test
|
||||
```typescript
|
||||
function fizzBuzz(n: number): string {
|
||||
if (n % 3 === 0) return 'fizz';
|
||||
if (n % 5 === 0) return 'buzz';
|
||||
return String(n);
|
||||
}
|
||||
|
||||
it('should return fizzbuzz for multiples of 15', () => {
|
||||
expect(fizzBuzz(15)).toBe('fizzbuzz');
|
||||
});
|
||||
```
|
||||
|
||||
## Practice Routines
|
||||
|
||||
### Daily Warm-up (15 minutes)
|
||||
|
||||
```
|
||||
1. Pick a familiar kata
|
||||
2. Set a timer for 15 minutes
|
||||
3. Focus on smooth keystrokes and minimal mouse use
|
||||
4. Track how far you get each day
|
||||
```
|
||||
|
||||
### Weekly Deep Practice (1-2 hours)
|
||||
|
||||
```
|
||||
1. Choose a kata you don't know well
|
||||
2. Work through it slowly, understanding each step
|
||||
3. Repeat 3-5 times
|
||||
4. By end of session, aim for fluent execution
|
||||
```
|
||||
|
||||
### Monthly Challenge
|
||||
|
||||
```
|
||||
1. Learn a new kata in a language you don't use at work
|
||||
2. Contribute to an open source project
|
||||
3. Attend or host a coding dojo session
|
||||
```
|
||||
|
||||
## Kata Resources
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| Kata Catalog | katas.softwarecraftsmanship.org |
|
||||
| Code Kata | codekata.pragprog.com |
|
||||
| Coding Dojo | codingdojo.org |
|
||||
|
||||
## Good Practice Habits
|
||||
|
||||
### Before Practice
|
||||
|
||||
- Clear workspace
|
||||
- Close distractions (email, chat)
|
||||
- Set a time limit
|
||||
- Choose a specific focus
|
||||
|
||||
### During Practice
|
||||
|
||||
- Focus on form, not just completion
|
||||
- Notice inefficiencies in keystrokes
|
||||
- Experiment with IDE shortcuts
|
||||
- Stay in flow state
|
||||
|
||||
### After Practice
|
||||
|
||||
- Reflect on what felt awkward
|
||||
- Note improvements to try next time
|
||||
- Track progress over weeks
|
||||
|
||||
## TypeScript-Friendly Kata Ideas
|
||||
|
||||
| Kata | Focus Area |
|
||||
|------|------------|
|
||||
| String Calculator | TDD basics, parsing |
|
||||
| Roman Numerals | Algorithm, edge cases |
|
||||
| Tennis Scoring | State management |
|
||||
| Bank Account | OOP, immutability |
|
||||
| Gilded Rose | Refactoring legacy code |
|
||||
| Mars Rover | Command pattern, state |
|
||||
@@ -0,0 +1,91 @@
|
||||
# Practicing Knowledge
|
||||
|
||||
Core concepts and foundational understanding for deliberate practice in software development.
|
||||
|
||||
## Overview
|
||||
|
||||
Professional programmers practice their craft just like musicians, athletes, and doctors. Practice involves skill-sharpening exercises done on your own time to build muscle memory and quick decision-making. The goal is to make common solutions automatic so your mind can focus on higher-level problems.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### The Coding Dojo
|
||||
|
||||
**Definition**: A practice space where programmers gather (or practice solo) to sharpen their skills through structured exercises.
|
||||
|
||||
The martial arts metaphor fits programming practice well - both require quick reactions, pattern recognition, and automatic responses built through repetition.
|
||||
|
||||
**Key points**:
|
||||
- Practice sessions can be solo or group-based
|
||||
- Focus is on perfecting movements/keystrokes, not just solving problems
|
||||
- Goal is to make solutions instinctive and automatic
|
||||
|
||||
### Kata
|
||||
|
||||
**Definition**: A precise set of choreographed keystrokes and movements that simulates solving a programming problem you already know the solution to.
|
||||
|
||||
You aren't solving the problem - you're practicing the movements and decisions involved. The asymptote of perfection is the goal: repeat until movements are automatic.
|
||||
|
||||
**Key points**:
|
||||
- Learn hot keys and navigation idioms
|
||||
- Practice disciplines like TDD and CI
|
||||
- Drive problem/solution pairs into your subconscious
|
||||
- Know several kata and practice regularly so they don't fade
|
||||
|
||||
### Wasa (Ping-Pong)
|
||||
|
||||
**Definition**: A two-person practice technique where partners alternate roles while working through a problem.
|
||||
|
||||
One programmer writes a unit test, the other makes it pass, then they swap roles. Can use a known kata (practicing form) or a new problem (competitive challenge).
|
||||
|
||||
**Key points**:
|
||||
- Critique each other's techniques
|
||||
- Test writer controls problem direction
|
||||
- Can add constraints to challenge partner
|
||||
|
||||
### Randori
|
||||
|
||||
**Definition**: Free-form group practice where multiple people take turns writing tests and making them pass.
|
||||
|
||||
Screen projected on wall, one person writes a test and sits down, next person makes it pass and writes another test. Rotates through the group.
|
||||
|
||||
**Key points**:
|
||||
- Learn how others solve problems
|
||||
- Broaden your own approach
|
||||
- Can be sequential or voluntary participation
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Kata | Choreographed solo exercise for a known problem |
|
||||
| Wasa | Two-person paired practice (ping-pong) |
|
||||
| Randori | Free-form group practice with rotating participants |
|
||||
| Dojo | Practice space/session for skill development |
|
||||
| TDD | Test-Driven Development - red/green/refactor loop |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **TDD**: Practice builds speed for the red-green-refactor loop
|
||||
- **Professionalism**: Practice is part of being a professional
|
||||
- **Time Management**: Practice happens on your own time, not employer's
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Practice means working on real projects more
|
||||
**Reality**: Practice is separate from production work - it's deliberate repetition of known problems
|
||||
|
||||
- **Myth**: Experienced developers don't need practice
|
||||
**Reality**: All professionals practice to maintain and sharpen skills
|
||||
|
||||
- **Myth**: Fast coding is just about typing speed
|
||||
**Reality**: Speed comes from pattern recognition and automatic responses built through practice
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Kata | Solo choreographed exercises for known problems |
|
||||
| Wasa | Paired ping-pong practice |
|
||||
| Randori | Group rotation practice |
|
||||
| Dojo | Practice session or space |
|
||||
| Purpose | Build muscle memory for automatic problem-solving |
|
||||
@@ -0,0 +1,97 @@
|
||||
# Practicing Rules
|
||||
|
||||
Rules for effective deliberate practice as a professional developer.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Practice on Your Own Time
|
||||
|
||||
Professional programmers practice on their own time. It is not your employer's job to keep your skills sharp.
|
||||
|
||||
- Patients don't pay doctors to practice sutures
|
||||
- Concert-goers don't pay to hear musicians play scales
|
||||
- Employers don't pay you for practice time
|
||||
- Your skills are your responsibility
|
||||
|
||||
### 2. Use Different Languages and Platforms
|
||||
|
||||
Since practice time is your own, you don't have to use the same technologies as your employer.
|
||||
|
||||
- Pick any language you like
|
||||
- Keep your polyglot skills sharp
|
||||
- If you work in .NET, practice Java or Ruby at lunch
|
||||
- Broadens your resume and mindset
|
||||
|
||||
### 3. Know Multiple Kata
|
||||
|
||||
A programmer should know several different kata and practice them regularly.
|
||||
|
||||
- Practice so they don't fade from memory
|
||||
- Each kata teaches different problem/solution pairs
|
||||
- Mix up your practice routine
|
||||
- Aim for variety in types of problems
|
||||
|
||||
### 4. Practice Until Automatic
|
||||
|
||||
The goal is to make movements automatic and instinctive.
|
||||
|
||||
- Repeat exercises until you know them cold
|
||||
- Your body should know what keys to hit
|
||||
- Mind free for higher-level strategy
|
||||
- Pattern recognition becomes instant
|
||||
|
||||
### 5. Contribute to Open Source
|
||||
|
||||
Take on pro-bono work to broaden your experience.
|
||||
|
||||
- Work on something someone else cares about
|
||||
- Best way to increase your repertoire
|
||||
- If you write Java, contribute to a Rails project
|
||||
- Prevents unhealthy narrowing of skills
|
||||
|
||||
## Guidelines
|
||||
|
||||
Less strict recommendations:
|
||||
|
||||
- Start with simple kata before complex ones
|
||||
- Record your practice to review technique
|
||||
- Set kata to music for an advanced challenge
|
||||
- Join or form a local coding dojo
|
||||
- Practice the same kata in different languages
|
||||
|
||||
## When to Practice
|
||||
|
||||
| Time | Activity |
|
||||
|------|----------|
|
||||
| Lunch break | Quick kata (15-20 min) |
|
||||
| Before work | Morning warm-up kata |
|
||||
| Evening | Longer practice sessions |
|
||||
| Weekends | Open source contributions |
|
||||
|
||||
## Exceptions
|
||||
|
||||
When these rules may be relaxed:
|
||||
|
||||
- **Learning new tech at work**: If employer is paying for training, that's different from practice
|
||||
- **Pair programming**: Work-time pairing has some practice benefits, but isn't deliberate practice
|
||||
- **Hackathons**: May be employer-sponsored but still provide practice value
|
||||
|
||||
## Practice Ethics Summary
|
||||
|
||||
| Do | Don't |
|
||||
|----|-------|
|
||||
| Practice on your own time | Expect employer to pay for skill sharpening |
|
||||
| Use different languages | Limit yourself to work technologies |
|
||||
| Contribute to open source | Only work on paid projects |
|
||||
| Practice regularly | Let skills fade from lack of use |
|
||||
| Take responsibility for your skills | Blame employer for skill gaps |
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Own time | Practice is your responsibility, not employer's |
|
||||
| Polyglot | Practice different languages than work |
|
||||
| Multiple kata | Know several, practice regularly |
|
||||
| Automatic | Repeat until movements are instinctive |
|
||||
| Open source | Contribute to broaden experience |
|
||||
@@ -0,0 +1,123 @@
|
||||
# Pressure Examples
|
||||
|
||||
Scenarios demonstrating professional and unprofessional behavior under pressure.
|
||||
|
||||
## Unprofessional Behavior Scenarios
|
||||
|
||||
### The Surgeon Analogy
|
||||
|
||||
Imagine you're on an operating table watching a surgeon perform open heart surgery on you under a deadline - a *literal* deadline.
|
||||
|
||||
**Unprofessional behavior**:
|
||||
- Sweating and swearing
|
||||
- Slamming and throwing instruments
|
||||
- Blaming management for unrealistic expectations
|
||||
- Continuously complaining about time
|
||||
- Behaving "like a typical developer"
|
||||
|
||||
**Why it fails**: This behavior doesn't help meet the deadline and creates chaos that makes outcomes worse.
|
||||
|
||||
### The Start-up Death Spiral
|
||||
|
||||
**Scenario**: Company burning through financing, product vision constantly changing, pressure to deliver for demos and customers.
|
||||
|
||||
**Unprofessional responses observed**:
|
||||
- Working 80-hour weeks as "heroism"
|
||||
- Writing 3,000-line functions at 2 AM
|
||||
- Arguments with shouting and name calling
|
||||
- Throwing pens, punching walls
|
||||
- "Hacking some mess together" for customer demos
|
||||
- Getting people fired who didn't "shape up"
|
||||
|
||||
**Results**:
|
||||
- Functions that were unmaintainable (3,000 lines of C)
|
||||
- Constant anger and stress
|
||||
- Personal life destruction
|
||||
- Never-ending cycle of crises
|
||||
|
||||
**The turning point**: Realizing this approach was "making life miserable for himself and others in the name of---what?"
|
||||
|
||||
### Discipline Abandonment
|
||||
|
||||
**Scenario**: Developer follows TDD in normal times but abandons it when deadline pressure hits.
|
||||
|
||||
**What this reveals**: They don't actually trust that TDD is helpful. If they believed it worked, they'd use it especially when it matters most.
|
||||
|
||||
**Similar patterns**:
|
||||
- Keeping code clean normally, making messes under pressure → Don't believe messes slow you down
|
||||
- Not pairing normally, but pairing in crisis → Believe pairing is actually more efficient
|
||||
|
||||
## Professional Behavior Scenarios
|
||||
|
||||
### The Calm Surgeon
|
||||
|
||||
**Scenario**: Surgeon under time pressure performing critical operation.
|
||||
|
||||
**Professional behavior**:
|
||||
- Appearing calm and collected
|
||||
- Issuing clear and precise orders to support staff
|
||||
- Following training and adhering to disciplines
|
||||
- No panic, no blaming, no complaining
|
||||
|
||||
**Why it works**: The trained disciplines exist precisely for these moments.
|
||||
|
||||
### Commitment Management
|
||||
|
||||
**Scenario**: Business makes promises to customers without consulting developers.
|
||||
|
||||
**Professional response**:
|
||||
- Help the business find a way to meet commitments
|
||||
- Do not accept responsibility for commitments you didn't make
|
||||
- Quantify and present risk so business can manage it
|
||||
- If no way is found, those who made promises accept responsibility
|
||||
|
||||
**Key distinction**: Professionals help the business achieve goals but don't accept commitments made for them.
|
||||
|
||||
### Pressure Response Protocol
|
||||
|
||||
**Scenario**: Project taking longer than expected, initial design is wrong and needs rework, commitment can't be kept.
|
||||
|
||||
**Professional response sequence**:
|
||||
|
||||
1. **Don't Panic**
|
||||
- Resist the urge to rush
|
||||
- Slow down and think the problem through
|
||||
- Plot course to best possible outcome
|
||||
- Drive toward it at reasonable, steady pace
|
||||
|
||||
2. **Communicate**
|
||||
- Tell team and superiors you're in trouble
|
||||
- Share your best plans for getting out
|
||||
- Ask for their input and guidance
|
||||
- Avoid creating surprises
|
||||
|
||||
3. **Rely on Disciplines**
|
||||
- Write even more tests than usual
|
||||
- Refactor even more carefully
|
||||
- Keep functions even smaller
|
||||
- Become more deliberate and dedicated
|
||||
|
||||
4. **Get Help**
|
||||
- Find someone to pair program with
|
||||
- Let them help you hold onto disciplines
|
||||
- Let them spot things you miss
|
||||
- Offer the same help to others under pressure
|
||||
|
||||
## Contrasting Approaches
|
||||
|
||||
| Situation | Unprofessional | Professional |
|
||||
|-----------|----------------|--------------|
|
||||
| Tight deadline | Rush and cut corners | Slow down, stay disciplined |
|
||||
| Unrealistic commitment | Accept and stress | Quantify risk, present options |
|
||||
| Code quality vs speed | Create messes | Stay clean (dirty = slow) |
|
||||
| Crisis hits | Abandon practices | Trust disciplines more |
|
||||
| Getting behind | Work alone, longer hours | Communicate, pair up |
|
||||
| Others make promises | Accept blame | Help but don't accept commitments |
|
||||
|
||||
## Key Insight
|
||||
|
||||
> "You know what you believe by observing yourself in a crisis. If in a crisis you follow your disciplines, then you truly believe in those disciplines."
|
||||
|
||||
The way to handle pressure:
|
||||
- **Avoid it** by managing commitments, following disciplines, keeping clean
|
||||
- **Weather it** by staying calm, communicating, following disciplines, getting help
|
||||
@@ -0,0 +1,84 @@
|
||||
# Pressure Knowledge
|
||||
|
||||
Core concepts and foundational understanding for handling pressure professionally.
|
||||
|
||||
## Overview
|
||||
|
||||
Pressure is inevitable in software development - deadlines, missed estimates, and crises occur. What separates professionals from amateurs is how they behave under that pressure. The key insight is "crisis discipline": your true beliefs are revealed by how you behave in a crisis, not in calm times.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Crisis Discipline
|
||||
|
||||
**Definition**: The principle that you should behave in a crisis the same way you wish you always behaved - following your established disciplines rather than abandoning them.
|
||||
|
||||
Your behavior in a crisis reveals your true beliefs about your practices. If you abandon TDD under pressure, you don't truly trust TDD. If you make messes when rushed, you don't truly believe that messes slow you down.
|
||||
|
||||
**Key points**:
|
||||
- Choose disciplines you feel comfortable following in a crisis
|
||||
- Then follow them all the time
|
||||
- Following disciplines prevents crises from occurring
|
||||
- Don't change behavior when the crunch comes
|
||||
|
||||
### Avoiding Pressure
|
||||
|
||||
**Definition**: Proactive strategies to minimize and shorten high-pressure periods before they occur.
|
||||
|
||||
The best way to stay calm under pressure is to avoid situations that cause it. Prevention is more effective than reaction.
|
||||
|
||||
**Key points**:
|
||||
- Avoid committing to deadlines you aren't sure you can meet
|
||||
- Keep systems, code, and design clean at all times
|
||||
- "Quick and dirty" is an oxymoron - dirty always means slow
|
||||
- Messes slow you down and cause missed dates
|
||||
|
||||
### Handling Pressure
|
||||
|
||||
**Definition**: Reactive strategies for when pressure occurs despite your best prevention efforts.
|
||||
|
||||
Sometimes projects take longer than expected, designs need rework, or commitments can't be kept. When this happens, professionals have specific responses.
|
||||
|
||||
**Key points**:
|
||||
- Don't panic - rushing drives you deeper into the hole
|
||||
- Communicate with team and superiors
|
||||
- Rely on your disciplines more, not less
|
||||
- Get help through pairing
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Crisis Discipline | Following your normal disciplines during high-pressure times |
|
||||
| Staying Clean | Not creating messes to move faster |
|
||||
| Forestalling Pressure | Preventing pressure through good practices |
|
||||
| Weathering Pressure | Managing pressure when it can't be avoided |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Commitment**: Managing commitments prevents unrealistic deadlines that cause pressure
|
||||
- **Estimation**: Proper estimation with risk quantification avoids pressure situations
|
||||
- **TDD**: A key discipline to maintain (not abandon) under pressure
|
||||
- **Professionalism**: Calm, decisive behavior under pressure defines a professional
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Working more hours under pressure shows dedication
|
||||
**Reality**: Sleepless nights and rushing won't help - they drive you deeper into trouble
|
||||
|
||||
- **Myth**: Quick and dirty code helps meet deadlines
|
||||
**Reality**: Dirty always means slow - messes slow you down and cause missed dates
|
||||
|
||||
- **Myth**: Abandoning practices like TDD saves time during crunch
|
||||
**Reality**: If you abandon disciplines under pressure, you don't truly believe in them
|
||||
|
||||
- **Myth**: Pressure requires changing how you work
|
||||
**Reality**: Your disciplines are the best way through - follow them even more carefully
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Crisis Discipline | Behave in crisis as you wish you always behaved |
|
||||
| Avoiding Pressure | Manage commitments and stay clean to prevent crises |
|
||||
| Handling Pressure | Don't panic, communicate, trust disciplines, get help |
|
||||
| Staying Clean | Never sacrifice code quality for speed |
|
||||
@@ -0,0 +1,88 @@
|
||||
# Pressure Rules
|
||||
|
||||
Rules for avoiding and handling pressure as a professional developer.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Never Change Your Behavior in a Crisis
|
||||
|
||||
Your disciplines exist to guide you through high-pressure times. If you abandon them under pressure, you don't truly believe in them.
|
||||
|
||||
- If you follow TDD normally but abandon it in crisis, you don't trust TDD
|
||||
- If you keep code clean normally but make messes in crisis, you don't believe messes slow you down
|
||||
- Choose disciplines you're comfortable following in a crisis, then follow them all the time
|
||||
|
||||
### 2. Never Commit to Deadlines You Can't Meet
|
||||
|
||||
The business wants commitments to eliminate risk. Your job is to quantify and present risk so they can manage it.
|
||||
|
||||
- Make sure risk is visible to the business
|
||||
- Accepting unrealistic commitments does a disservice to everyone
|
||||
- When commitments are made for you, help find a way to meet them but don't accept responsibility for promises you didn't make
|
||||
|
||||
### 3. Stay Clean to Go Fast
|
||||
|
||||
"Quick and dirty" is an oxymoron. Dirty always means slow.
|
||||
|
||||
- Don't tolerate messes - they cause missed dates
|
||||
- Don't spend endless hours polishing, but don't create messes either
|
||||
- Keep systems, code, and design as clean as possible
|
||||
|
||||
### 4. Don't Panic When Pressure Hits
|
||||
|
||||
Rushing drives you deeper into the hole. Manage your stress instead.
|
||||
|
||||
- Sleepless nights won't help you get done faster
|
||||
- Sitting and fretting won't help either
|
||||
- Slow down and think the problem through
|
||||
- Plot a course to the best outcome and drive toward it at steady pace
|
||||
|
||||
### 5. Communicate Early and Often
|
||||
|
||||
Nothing makes people more angry and less rational than surprises.
|
||||
|
||||
- Let team and superiors know when you're in trouble
|
||||
- Tell them your best plans for getting out of trouble
|
||||
- Ask for input and guidance
|
||||
- Surprises multiply pressure by ten
|
||||
|
||||
### 6. Rely More on Disciplines Under Pressure
|
||||
|
||||
These are the times to pay special attention to all your disciplines, not question or abandon them.
|
||||
|
||||
- Write even more tests than usual if you follow TDD
|
||||
- Refactor even more if you're a merciless refactorer
|
||||
- Keep functions even smaller if you normally keep them small
|
||||
- Become more deliberate and dedicated to your disciplines
|
||||
|
||||
### 7. Get Help Through Pairing
|
||||
|
||||
When the heat is on, pair programming helps you get done faster with fewer defects.
|
||||
|
||||
- Your partner helps you hold onto disciplines and keeps you from panicking
|
||||
- Your partner spots things you miss and has helpful ideas
|
||||
- Your partner picks up slack when you lose focus
|
||||
- Offer to pair with others when you see them under pressure
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Be calm and decisive under pressure - this defines professional behavior
|
||||
- Know that your disciplines are the best way to meet deadlines and commitments
|
||||
- Help the business find ways to meet its goals while maintaining professional standards
|
||||
- When you can't meet promises made by others, those who made the promises must accept responsibility
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **External commitments made without you**: You should help find solutions but are not honor bound to accept commitments made for you by the business
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Crisis Behavior | Don't change behavior under pressure |
|
||||
| Commitments | Never commit to what you can't deliver |
|
||||
| Stay Clean | Dirty always means slow |
|
||||
| Don't Panic | Slow down and think; rushing makes it worse |
|
||||
| Communicate | No surprises; ask for help early |
|
||||
| Trust Disciplines | Follow them more, not less, under pressure |
|
||||
| Pair Up | Get help when the heat is on |
|
||||
@@ -0,0 +1,199 @@
|
||||
# Professionalism Examples
|
||||
|
||||
Scenarios demonstrating professional vs unprofessional behavior.
|
||||
|
||||
## Unprofessional Behavior Scenarios
|
||||
|
||||
### Shipping Without Testing
|
||||
|
||||
**Scenario**: Developer ships software on the promised date without testing a critical feature because "the bug fixes weren't in that code."
|
||||
|
||||
**What happened**:
|
||||
- Software caused customer data loss
|
||||
- Multiple customers affected simultaneously
|
||||
- Took days to diagnose and fix
|
||||
- Field service manager fielded angry calls
|
||||
- Customer trust damaged
|
||||
|
||||
**Why it's unprofessional**:
|
||||
- Prioritized "saving face" over customer needs
|
||||
- Rationalized skipping tests to meet deadline
|
||||
- Passed consequences to others (customers, field service)
|
||||
|
||||
**Professional alternative**:
|
||||
- Communicate that testing isn't complete
|
||||
- Delay ship date if necessary
|
||||
- Take responsibility early, before damage occurs
|
||||
|
||||
---
|
||||
|
||||
### Using QA as Bug Catchers
|
||||
|
||||
**Scenario**: Developer sends code to QA knowing it hasn't been thoroughly checked, relying on QA to find and report bugs.
|
||||
|
||||
**Why it's unprofessional**:
|
||||
- Shifts responsibility to QA team
|
||||
- Damages schedules when bugs are found late
|
||||
- Undermines confidence in development team
|
||||
- Lazy and irresponsible behavior
|
||||
- Violates "do no harm" principle
|
||||
|
||||
**Professional alternative**:
|
||||
- Test thoroughly before release
|
||||
- Expect QA to find nothing
|
||||
- Be surprised and investigate when bugs escape
|
||||
|
||||
---
|
||||
|
||||
### Letting Employer Own Your Career
|
||||
|
||||
**Scenario**: Developer does only assigned work during work hours, expects employer to provide all training, and doesn't learn new technologies.
|
||||
|
||||
**Why it's unprofessional**:
|
||||
- Career becomes dependent on employer's priorities
|
||||
- Skills stagnate and become outdated
|
||||
- Industry passes them by
|
||||
- Others with current skills advance instead
|
||||
|
||||
**Professional alternative**:
|
||||
- Invest 20 hours/week in personal development
|
||||
- Read books, attend conferences, learn new languages
|
||||
- Take ownership of career trajectory
|
||||
|
||||
---
|
||||
|
||||
### Coding Without Domain Knowledge
|
||||
|
||||
**Scenario**: Developer implements accounting system features exactly as specified, without understanding why the spec makes business sense.
|
||||
|
||||
**Why it's unprofessional**:
|
||||
- Can't recognize or challenge specification errors
|
||||
- Builds features that may not solve real problems
|
||||
- Misses opportunities to suggest better solutions
|
||||
- Abdication of professional judgment
|
||||
|
||||
**Professional alternative**:
|
||||
- Read books on accounting
|
||||
- Interview users about fundamentals
|
||||
- Understand principles behind specifications
|
||||
- Challenge specs that don't make sense
|
||||
|
||||
---
|
||||
|
||||
### Refusing to Touch Working Code
|
||||
|
||||
**Scenario**: Developer avoids making structural improvements because "if it ain't broke, don't fix it."
|
||||
|
||||
**Why it's unprofessional**:
|
||||
- Code becomes rigid over time
|
||||
- Future changes become exorbitantly expensive
|
||||
- Technical debt accumulates
|
||||
- Eventually the project becomes mired in "tar pit"
|
||||
|
||||
**Professional alternative**:
|
||||
- Make small improvements with every touch
|
||||
- Follow Boy Scout Rule
|
||||
- Use tests to enable fearless refactoring
|
||||
- Treat code like clay to be continuously shaped
|
||||
|
||||
## Professional Behavior Scenarios
|
||||
|
||||
### Taking Responsibility Early
|
||||
|
||||
**Scenario**: Developer realizes tests aren't complete but deadline is tomorrow. They inform the manager immediately.
|
||||
|
||||
**Actions**:
|
||||
- Communicate honestly about status
|
||||
- Explain the risk of shipping untested code
|
||||
- Propose alternatives (delay, partial release)
|
||||
- Accept the difficult conversation
|
||||
|
||||
**Why it's professional**:
|
||||
- Protects customers from potential harm
|
||||
- Demonstrates accountability
|
||||
- Allows informed decision-making
|
||||
- Builds long-term trust
|
||||
|
||||
---
|
||||
|
||||
### Continuous Learning in Practice
|
||||
|
||||
**Scenario**: Developer structures their week to include professional development.
|
||||
|
||||
**Actions**:
|
||||
- Uses lunch hour to read technical books
|
||||
- Listens to podcasts during commute
|
||||
- Spends 90 minutes daily learning a new language
|
||||
- Does morning kata to warm up
|
||||
- Attends monthly user group meetings
|
||||
|
||||
**Why it's professional**:
|
||||
- Takes ownership of career
|
||||
- Maintains current skills
|
||||
- Builds expertise beyond current job
|
||||
- Prevents burnout through passion reinforcement
|
||||
|
||||
---
|
||||
|
||||
### Fearless Refactoring
|
||||
|
||||
**Scenario**: Developer reads through a module and notices a method that's getting long.
|
||||
|
||||
**Actions**:
|
||||
- Runs existing test suite (passes)
|
||||
- Repartitions the method into smaller pieces
|
||||
- Runs tests again (passes)
|
||||
- Commits the improvement
|
||||
- Continues with original task
|
||||
|
||||
**Why it's professional**:
|
||||
- Maintains code structure continuously
|
||||
- Tests enable confident changes
|
||||
- Small improvements compound over time
|
||||
- Treats code as malleable material
|
||||
|
||||
---
|
||||
|
||||
### Mentoring a Junior Developer
|
||||
|
||||
**Scenario**: New developer joins the team, unfamiliar with the codebase.
|
||||
|
||||
**Actions**:
|
||||
- Sits down with them personally
|
||||
- Walks through system architecture
|
||||
- Explains team conventions and practices
|
||||
- Pairs on initial tasks
|
||||
- Remains available for questions
|
||||
|
||||
**Why it's professional**:
|
||||
- Doesn't let junior "flail about unsupervised"
|
||||
- Teaching deepens own understanding
|
||||
- Accelerates team productivity
|
||||
- Fulfills responsibility to profession
|
||||
|
||||
---
|
||||
|
||||
### Humble Response to Failure
|
||||
|
||||
**Scenario**: Developer's estimate turns out wrong. The team jokes about it.
|
||||
|
||||
**Actions**:
|
||||
- Laughs at themselves first
|
||||
- Acknowledges the mistake openly
|
||||
- Adjusts approach for future estimates
|
||||
|
||||
**Why it's professional**:
|
||||
- Accepts failure is inevitable
|
||||
- Uses failure as learning opportunity
|
||||
|
||||
## Contrasts Summary
|
||||
|
||||
| Situation | Unprofessional | Professional |
|
||||
|-----------|---------------|--------------|
|
||||
| Deadline pressure | Skip tests to ship on time | Communicate risk, negotiate scope |
|
||||
| QA process | Send code hoping QA catches bugs | Expect QA to find nothing |
|
||||
| Career development | Wait for employer to provide training | Invest 20 hours/week personally |
|
||||
| Domain knowledge | Code blindly from spec | Learn domain, challenge errors |
|
||||
| Working code | Don't touch it | Improve it continuously |
|
||||
| New team member | Let them figure it out | Mentor them personally |
|
||||
| Personal failure | Deflect blame | Laugh, learn, improve |
|
||||
@@ -0,0 +1,84 @@
|
||||
# Professionalism Knowledge
|
||||
|
||||
Core concepts and foundational understanding for software professionalism.
|
||||
|
||||
## Overview
|
||||
|
||||
Professionalism is about taking responsibility for your work and its consequences. It combines pride and honor with accountability. A professional developer owns their mistakes, continuously improves their skills, and prioritizes the good of their code, customers, and employer.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Responsibility and Accountability
|
||||
|
||||
**Definition**: Taking ownership of your work outcomes, both successes and failures.
|
||||
|
||||
A professional doesn't pass the buck when things go wrong. When a bug escapes to production, a professional feels it as if the cost came from their own pocket.
|
||||
|
||||
**Key points**:
|
||||
- Professionals clean up their own messes
|
||||
- The weight of responsibility should be felt constantly
|
||||
- Apologies are necessary but insufficient - you must improve
|
||||
|
||||
### Do No Harm
|
||||
|
||||
**Definition**: The principle that a developer's first obligation is to avoid damaging the software or the business.
|
||||
|
||||
Borrowed from the Hippocratic oath, this principle establishes that professionals must not harm the function (correctness) or structure (maintainability) of the software.
|
||||
|
||||
**Key points**:
|
||||
- Applies to both function (bugs) and structure (design)
|
||||
- You are responsible for imperfections even though perfection is impossible
|
||||
- Error rate should asymptotically approach zero over your career
|
||||
|
||||
### Work Ethic
|
||||
|
||||
**Definition**: The commitment to continuous self-improvement outside of employer-paid time.
|
||||
|
||||
Your career is your responsibility, not your employer's. Professionals invest their own time in learning, practicing, and growing.
|
||||
|
||||
**Key points**:
|
||||
- 40 hours for employer, 20 hours for your own growth
|
||||
- Don't rely on employer for training, books, or conferences
|
||||
- This investment prevents burnout by reinforcing passion
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Professional | One who takes responsibility and is accountable for their work |
|
||||
| Merciless Refactoring | Continuously improving code structure with every touch |
|
||||
| Boy Scout Rule | Always leave code cleaner than you found it |
|
||||
| Kata | Repetitive coding exercises to sharpen skills |
|
||||
| Asymptote | A limit approached but never reached (e.g., zero bugs) |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Testing**: Tests enable fearless refactoring and prove code works
|
||||
- **Code Quality**: Structure must be maintained to preserve changeability
|
||||
- **Team Collaboration**: Learning accelerates through pairing and mentoring
|
||||
- **Domain Knowledge**: Professionals understand what they're building and why
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Software is too complex for zero-bug expectations
|
||||
**Reality**: You're still responsible for bugs; aim for the asymptote of zero
|
||||
|
||||
- **Myth**: Making continuous changes to working code is dangerous
|
||||
**Reality**: NOT changing code is dangerous - it becomes rigid
|
||||
|
||||
- **Myth**: QA exists to catch your bugs
|
||||
**Reality**: QA should find nothing; sending known-faulty code is unprofessional
|
||||
|
||||
- **Myth**: Employer should provide all training and learning time
|
||||
**Reality**: Career development is your responsibility, not theirs
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Responsibility | Own your mistakes and their consequences |
|
||||
| Do No Harm | Protect both function (bugs) and structure (design) |
|
||||
| Work Ethic | Invest 20 hours/week in your own growth |
|
||||
| Continuous Learning | Never stop acquiring new knowledge and skills |
|
||||
| Practice | Exercise skills deliberately, outside of work performance |
|
||||
| Humility | Accept that you will fail and be ready to laugh at yourself |
|
||||
@@ -0,0 +1,165 @@
|
||||
# Professionalism Rules
|
||||
|
||||
Guidelines for professional behavior in software development.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Do No Harm to Function
|
||||
|
||||
Create software that works correctly. You are responsible for bugs even though they are inevitable.
|
||||
|
||||
- Never release code you aren't certain about
|
||||
- Apologize for bugs, then prevent recurrence
|
||||
- Your error rate should decrease over your career toward zero
|
||||
|
||||
### 2. Do No Harm to Structure
|
||||
|
||||
Never sacrifice code structure for short-term function delivery.
|
||||
|
||||
- Flexible structure enables future changes
|
||||
- Compromising structure compromises the future
|
||||
- You must be able to make changes without exorbitant costs
|
||||
|
||||
### 3. QA Should Find Nothing
|
||||
|
||||
Release only code you expect to pass QA completely.
|
||||
|
||||
- Never use QA as bug catchers
|
||||
- Sending known-faulty code is unprofessional
|
||||
- Be surprised and chagrined when QA finds issues
|
||||
- Every escaped bug requires root cause analysis
|
||||
|
||||
### 4. Know It Works
|
||||
|
||||
Test thoroughly before release.
|
||||
|
||||
- Test it up, down, and seven ways to Sunday
|
||||
- Automate your tests for quick, repeatable execution
|
||||
- 100% test coverage is demanded, not suggested
|
||||
- Design code to be easy to test (write tests first)
|
||||
|
||||
### 5. Maintain Automated QA
|
||||
|
||||
Your test suite should be your release gate.
|
||||
|
||||
- If tests pass, you should be confident to ship
|
||||
- Tests should run quickly (minutes, not hours)
|
||||
- At minimum, tests should indicate high probability of passing QA
|
||||
|
||||
### 6. Practice Merciless Refactoring
|
||||
|
||||
Continuously improve code structure.
|
||||
|
||||
- Make small improvements every time you touch code
|
||||
- The Boy Scout Rule: leave code cleaner than you found it
|
||||
- Tests enable fearless refactoring
|
||||
- Treat code like clay - continuously shape it
|
||||
|
||||
### 7. Own Your Career
|
||||
|
||||
Your professional development is your responsibility.
|
||||
|
||||
- Don't rely on employer for training, books, or conferences
|
||||
- Invest ~20 hours/week in your own growth
|
||||
- 40 hours for employer's problems, 20 for your career
|
||||
- If employer provides learning opportunities, be grateful but don't expect them
|
||||
|
||||
### 8. Know Your Field
|
||||
|
||||
Maintain broad knowledge of software development history and techniques.
|
||||
|
||||
- Know design patterns (all 24 GOF patterns, POSA patterns)
|
||||
- Know design principles (SOLID, component principles)
|
||||
- Know methods (XP, Scrum, Lean, Kanban, Waterfall, Structured Analysis)
|
||||
- Know disciplines (TDD, OO design, CI, Pair Programming)
|
||||
- Know artifacts (UML, DFDs, State Diagrams, decision tables)
|
||||
|
||||
### 9. Never Stop Learning
|
||||
|
||||
Continuous learning is mandatory for professionals.
|
||||
|
||||
- Read books, articles, blogs
|
||||
- Attend conferences and user groups
|
||||
- Learn outside your comfort zone
|
||||
- Architects who stop coding become irrelevant
|
||||
|
||||
### 10. Practice Deliberately
|
||||
|
||||
Performance is not practice. Practice skills outside daily work.
|
||||
|
||||
- Use kata exercises for skill sharpening
|
||||
- 10-minute warm-up in morning, cool-down in evening
|
||||
- Practice in multiple languages
|
||||
- Train fingers and brain through repetition
|
||||
|
||||
### 11. Collaborate and Teach
|
||||
|
||||
Learning accelerates through interaction with others.
|
||||
|
||||
- Program together, practice together, design together
|
||||
- Mentoring juniors is a professional responsibility
|
||||
- Teaching drives knowledge deeper into your own understanding
|
||||
- Balance collaboration with necessary alone time
|
||||
|
||||
### 12. Know Your Domain
|
||||
|
||||
Understand the business context of your software.
|
||||
|
||||
- Read books on the domain
|
||||
- Interview customers and users
|
||||
- Challenge specification errors with domain knowledge
|
||||
- Never code blindly from specs
|
||||
|
||||
### 13. Identify with Employer/Customer
|
||||
|
||||
Their problems are your problems.
|
||||
|
||||
- Work toward solutions that address real needs
|
||||
- Avoid "us versus them" mentality
|
||||
- Put yourself in employer's shoes when developing features
|
||||
|
||||
### 14. Practice Humility
|
||||
|
||||
Balance confidence with awareness of your fallibility.
|
||||
|
||||
- Know your job and take pride in your work
|
||||
- Take bold, calculated risks based on confidence
|
||||
- Accept that you will sometimes fail
|
||||
- Be first to laugh when you're the butt of a joke
|
||||
- Never demean others for mistakes
|
||||
|
||||
## Guidelines
|
||||
|
||||
Less strict recommendations for professional behavior:
|
||||
|
||||
- Use lunch hours for reading
|
||||
- Listen to podcasts during commute
|
||||
- Spend 90 minutes daily learning something new
|
||||
- Do kata to maintain skills in multiple languages
|
||||
- Pair program regularly but preserve alone time
|
||||
- Sit with new team members to show them the ropes
|
||||
|
||||
## Exceptions
|
||||
|
||||
When these rules may be relaxed:
|
||||
|
||||
- **Time investment**: Life circumstances may require temporary adjustment, but this should be exception not norm
|
||||
- **Test coverage**: Some mission-critical systems may need additional QA beyond automated tests
|
||||
- **Domain expertise**: You needn't be a domain expert, but due diligence is required
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Do No Harm - Function | Don't create bugs; own the ones that escape |
|
||||
| Do No Harm - Structure | Never sacrifice design for features |
|
||||
| QA Finds Nothing | Release only code you're certain about |
|
||||
| Know It Works | Test everything, automate everything |
|
||||
| Merciless Refactoring | Improve code every time you touch it |
|
||||
| Own Your Career | Invest 20 hours/week in growth |
|
||||
| Know Your Field | Master patterns, principles, methods, disciplines |
|
||||
| Never Stop Learning | Read, attend, participate continuously |
|
||||
| Practice | Do kata daily to sharpen skills |
|
||||
| Collaborate | Program and learn together |
|
||||
| Know Domain | Understand the business you're coding for |
|
||||
| Stay Humble | Accept you will fail; laugh at yourself |
|
||||
@@ -0,0 +1,169 @@
|
||||
# Saying No Examples
|
||||
|
||||
Dialogue examples demonstrating professional ways to decline requests.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Avoiding Confrontation (Both Parties Fail)
|
||||
|
||||
> Paula: "Mike, I need the login page done by tomorrow."
|
||||
>
|
||||
> Mike: "Oh, wow! That soon? Well, OK, I'll try."
|
||||
>
|
||||
> Paula: "OK, that's great. Thanks!"
|
||||
|
||||
**Problems**:
|
||||
- Paula is lying - she knows it takes longer than a day
|
||||
- Mike accepts "I'll try" as "Yes" - a dumb mistake
|
||||
- Both avoid confrontation but create future disaster
|
||||
- Neither party searched for the best possible outcome
|
||||
|
||||
### Passive Acceptance (No Advocacy)
|
||||
|
||||
> Paula: "Mike, I need the login page done by tomorrow."
|
||||
>
|
||||
> Mike: "Oh, sorry Mike, but it's going to take me more time than that."
|
||||
>
|
||||
> Paula: "When do you think you can have it done?"
|
||||
>
|
||||
> Mike: "How about two weeks from now?"
|
||||
>
|
||||
> Paula: (scribbles in daytimer) "OK, thanks."
|
||||
|
||||
**Problems**:
|
||||
- Paula should state her timeline assertively, not ask permission
|
||||
- Mike accepts without question, as if his objectives don't matter
|
||||
- Likely to lead to passive-aggressive blame later
|
||||
- Neither defended their position
|
||||
|
||||
### Manager Commits Team Without Input
|
||||
|
||||
> Don: "OK Mike, the customer is coming in six weeks. They're expecting to see everything working."
|
||||
>
|
||||
> Mike: "Yes, and we'll be ready. My team is busting their butts and we're going to get it done. We'll have to work some overtime, but we'll make it happen!"
|
||||
>
|
||||
> Don: "It's great that you and your staff are such team players."
|
||||
|
||||
**Problems**:
|
||||
- Mike committed Paula's team to something she explicitly said was impossible
|
||||
- Mike is playing for himself, not the team
|
||||
- "Team player" label rewards dishonesty
|
||||
- Disaster is now scheduled
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Professional Negotiation
|
||||
|
||||
> Mike: "Paula, I need the login page done by tomorrow."
|
||||
>
|
||||
> Paula: "No, Mike, that's a two-week job."
|
||||
>
|
||||
> Mike: "Two weeks? The architects estimated it at three days!"
|
||||
>
|
||||
> Paula: "The architects were wrong. I've got at least ten more days of work. Didn't you see my updated estimate on the wiki?"
|
||||
>
|
||||
> Mike: "This isn't acceptable Paula. Customers are coming tomorrow, and I need the login page working."
|
||||
>
|
||||
> Paula: "What part of the login page do you need working by tomorrow?"
|
||||
>
|
||||
> Mike: "I need to be able to log in."
|
||||
>
|
||||
> Paula: "I can give you a mock-up that lets you log in. It won't check passwords, email forgotten passwords, or have the news banner. But you'll be able to log in. Will that do?"
|
||||
>
|
||||
> Mike: "I'll be able to log in?"
|
||||
>
|
||||
> Paula: "Yes."
|
||||
>
|
||||
> Mike: "That's great Paula, you're a life saver!"
|
||||
|
||||
**Why it works**:
|
||||
- Paula says no clearly and firmly
|
||||
- She offers what IS possible as an alternative
|
||||
- They negotiate to find the best possible outcome
|
||||
- Both parties get something valuable
|
||||
|
||||
### Refusing to "Try"
|
||||
|
||||
> Mike: "Come on Paula, can't you guys at least try?"
|
||||
>
|
||||
> Paula: "Mike, I could try to levitate. I could try to change lead into gold. I could try to swim across the Atlantic. Do you think I'd succeed?"
|
||||
>
|
||||
> Mike: "Now you're being unreasonable. I'm not asking for the impossible."
|
||||
>
|
||||
> Paula: "Yes, Mike, you are."
|
||||
|
||||
**Why it works**:
|
||||
- Paula refuses the "try" trap clearly
|
||||
- Uses absurdity to show the request is impossible
|
||||
- Doesn't leave any room for false hope
|
||||
- Maintains her position despite pressure
|
||||
|
||||
### Threatening Escalation Professionally
|
||||
|
||||
> Paula: "Mike, have you told Don about my estimates?"
|
||||
>
|
||||
> Mike: "Yeah, but you were going to try anyway, right?"
|
||||
>
|
||||
> Paula: "We've already had that discussion. Remember, gold and lead?"
|
||||
>
|
||||
> Mike: "You've just got to do it. Please do whatever it takes."
|
||||
>
|
||||
> Paula: "Mike. I don't have to make this happen for you. What I have to do, if you don't, is tell Don."
|
||||
>
|
||||
> Mike: "That'd be going over my head."
|
||||
>
|
||||
> Paula: "I don't want to, but I will if you force me."
|
||||
|
||||
**Why it works**:
|
||||
- Paula gives Mike the chance to act first
|
||||
- Clear deadline: "tomorrow I expect a meeting"
|
||||
- Documents in writing (memo)
|
||||
- Explains she's protecting both of them
|
||||
|
||||
### High Stakes Confrontation
|
||||
|
||||
> Charles: "Damn it Don! This was supposed to be done three weeks ago! You've got to do better."
|
||||
>
|
||||
> Don: "Chuck, I told you three months ago, after the layoffs, that we'd need four more months. You cut my staff twenty percent!"
|
||||
>
|
||||
> Charles: "Without Galitron, we're really hosed. Don, you've got to do better."
|
||||
>
|
||||
> Don: "There's nothing I can do Chuck. Galitron won't cut scope, won't accept interim releases. I cannot do that any faster. It's not going to happen."
|
||||
>
|
||||
> Charles: "I don't suppose it would matter if I told you your job was at stake."
|
||||
>
|
||||
> Don: "Firing me isn't going to change the estimate, Charles."
|
||||
|
||||
**Why it works**:
|
||||
- Don stands firm under extreme pressure
|
||||
- Provides facts, not excuses
|
||||
- Doesn't cave even when job is threatened
|
||||
- CEO can now make informed decisions
|
||||
|
||||
## Contrast: Same Situation, Different Approaches
|
||||
|
||||
### The Wrong Response to Pressure
|
||||
|
||||
> Bill and Jalil: "No, it's really got to be Friday. Can you at least try?"
|
||||
>
|
||||
> Team Lead: "OK, we'll try."
|
||||
|
||||
**Result**: Blazing disaster. System crashed repeatedly. Customer told them to shut it down. Everyone quit.
|
||||
|
||||
### The Right Response to Pressure
|
||||
|
||||
> Bill and Jalil: "It's got to be ready by Friday."
|
||||
>
|
||||
> Professional Response: "Look, we just barely got this system to sort-of work. We need to shake down the troubles. We need four weeks. Friday is not possible."
|
||||
|
||||
**Note**: The team lead should have maintained this position. Professionals speak truth to power and have the courage to say no to their managers.
|
||||
|
||||
## Key Patterns
|
||||
|
||||
| Situation | Bad Response | Good Response |
|
||||
|-----------|--------------|---------------|
|
||||
| Impossible deadline | "I'll try" | "No, that's a two-week job" |
|
||||
| Pressure to commit | Accept silently | "What part do you actually need?" |
|
||||
| Repeated manipulation | Eventually cave | "We've had this discussion" |
|
||||
| Boss ignores warnings | Passive aggression | "I will escalate tomorrow" |
|
||||
| Threatened with firing | Cave to pressure | "Firing me won't change the estimate" |
|
||||
@@ -0,0 +1,92 @@
|
||||
# Saying No Knowledge
|
||||
|
||||
Core concepts and foundational understanding for professional communication and the art of saying no.
|
||||
|
||||
## Overview
|
||||
|
||||
Saying no is a fundamental professional skill. Professionals are expected to say no when necessary - it's how teams reach the best possible outcomes. The ability to decline requests appropriately protects projects, teams, and organizations from disaster.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Adversarial Roles
|
||||
|
||||
**Definition**: The productive tension between managers and developers pursuing their respective objectives.
|
||||
|
||||
Managers pursue and defend their objectives aggressively - that's their job. Developers must do the same for their objectives. This adversarial dynamic leads to the best possible outcomes through negotiation.
|
||||
|
||||
**Key points**:
|
||||
- Both parties defending their positions leads to better solutions
|
||||
- Avoiding confrontation often leads to dysfunction
|
||||
- The goal is finding a mutually acceptable outcome, not winning
|
||||
|
||||
### High Stakes Situations
|
||||
|
||||
**Definition**: Scenarios where the cost of failure threatens the survival of a project, team, or company.
|
||||
|
||||
The most important time to say no is when stakes are highest. When company survival depends on accurate information, you must give managers the best information possible - even if that means saying no.
|
||||
|
||||
**Key points**:
|
||||
- Higher stakes make honest "no" more valuable
|
||||
- Hiding bad news makes disasters worse
|
||||
- Your job is to provide accurate information, not false hope
|
||||
|
||||
### Being a Team Player
|
||||
|
||||
**Definition**: Playing your position well and helping teammates - which includes saying no when necessary.
|
||||
|
||||
A team player is NOT someone who says yes all the time. True team players communicate frequently, defend their estimates, and represent what can and cannot be done accurately.
|
||||
|
||||
**Key points**:
|
||||
- Saying yes to impossible requests is NOT being a team player
|
||||
- Defending accurate estimates protects the whole team
|
||||
- Committing others to impossible deadlines is selfish, not collaborative
|
||||
|
||||
### The Cost of Saying Yes
|
||||
|
||||
**Definition**: The negative consequences that result from agreeing to unrealistic commitments.
|
||||
|
||||
Saying yes to impossible deadlines leads to: rushed code, dropped quality practices, family sacrifice, team burnout, and ultimately project failure. The "hero" who says yes to everything often causes the most damage.
|
||||
|
||||
**Key points**:
|
||||
- Rushing creates technical debt and bugs
|
||||
- Overtime makes teams slower, not faster
|
||||
- Dropped quality practices multiply problems
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Best Possible Outcome | The goal shared by managers and developers, found through negotiation |
|
||||
| Professional | Someone expected to say no and defend their objectives |
|
||||
| Passive Aggression | Letting someone fail rather than confronting the issue directly |
|
||||
| The Brass Ring | The temptation to be a hero by accepting impossible tasks |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Estimation**: Saying no protects your estimates from unrealistic pressure
|
||||
- **Commitment**: A "no" now prevents a broken commitment later
|
||||
- **Code Quality**: Saying no to rushed timelines protects code quality
|
||||
- **Team Dynamics**: Honest disagreement leads to better solutions
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Professionals always do what their boss says
|
||||
**Reality**: Professionals are expected to say no - good managers crave it
|
||||
|
||||
- **Myth**: Team players say yes to help the team
|
||||
**Reality**: Team players defend accurate estimates to protect the team
|
||||
|
||||
- **Myth**: Saying no is insubordination
|
||||
**Reality**: Slaves can't say no; laborers hesitate; professionals say no
|
||||
|
||||
- **Myth**: Adversarial relationships are bad for teams
|
||||
**Reality**: Constructive tension through defended positions yields best outcomes
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Adversarial Roles | Manager vs developer tension leads to best outcomes |
|
||||
| High Stakes | More valuable to say no when stakes are higher |
|
||||
| Team Player | Defends estimates, doesn't just agree to everything |
|
||||
| Cost of Yes | Agreeing to impossible tasks causes more harm than no |
|
||||
@@ -0,0 +1,94 @@
|
||||
# Saying No Rules
|
||||
|
||||
Guidelines for when and how to decline requests professionally.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Say No to Impossible Commitments
|
||||
|
||||
If you know something cannot be done by the requested deadline, say no clearly.
|
||||
|
||||
- Don't agree hoping things will work out
|
||||
- Don't cave to pressure or intimidation
|
||||
- State the fact plainly: "No, that's a two-week job"
|
||||
|
||||
### 2. Never Promise to "Try"
|
||||
|
||||
The promise to try implies you were holding back effort before.
|
||||
|
||||
- "Trying" is a commitment to succeed with extra effort
|
||||
- If you have no new plan, promising to try is dishonest
|
||||
- Either you can do it or you can't - "trying" is a lie
|
||||
|
||||
### 3. Avoid Passive Aggression
|
||||
|
||||
Don't let others walk off a cliff just to prove you were right.
|
||||
|
||||
- Escalate when someone won't communicate bad news
|
||||
- Warn teammates of impending disasters
|
||||
- Cover-your-ass memos are not enough - take action
|
||||
|
||||
### 4. Defend Your Estimates Under Pressure
|
||||
|
||||
Maintain your position through wheedling, cajoling, and manipulation.
|
||||
|
||||
- Keep stating the uncertainty: "eight or nine weeks"
|
||||
- Don't suggest extra effort could change the outcome
|
||||
- Document your estimates in writing (memos, email)
|
||||
|
||||
### 5. Negotiate Toward the Best Outcome
|
||||
|
||||
Say no, then work together to find what IS possible.
|
||||
|
||||
- Ask what parts are actually needed
|
||||
- Offer alternatives with reduced scope
|
||||
- Find the mutually acceptable solution
|
||||
|
||||
## Guidelines
|
||||
|
||||
Less strict recommendations:
|
||||
|
||||
- Explain the "why" only if it helps - facts matter more than reasons
|
||||
- Too much detail invites micro-management
|
||||
- When escalating, give the person a chance to act first
|
||||
- Set deadlines for action before going over someone's head
|
||||
|
||||
## When to Escalate
|
||||
|
||||
Circumstances requiring you to go above someone's head:
|
||||
|
||||
- **Ignored warnings**: They won't tell their boss about real problems
|
||||
- **Approaching disaster**: Customers will arrive expecting impossible deliverables
|
||||
- **Continued manipulation**: Repeated attempts to get you to "try"
|
||||
|
||||
Always give warning first: "If that meeting doesn't happen tomorrow, I will be forced to go to Don myself."
|
||||
|
||||
## Exceptions
|
||||
|
||||
When these rules may be relaxed:
|
||||
|
||||
- **Genuine new information**: If scope actually shrinks, revise estimates
|
||||
- **Real options emerge**: New resources or timeline extensions change facts
|
||||
- **Collaborative problem-solving**: Brainstorming alternatives is not caving
|
||||
|
||||
## What NOT to Do
|
||||
|
||||
Behaviors to avoid:
|
||||
|
||||
- Don't say "OK, I'll try" to avoid confrontation
|
||||
- Don't accept dates without defending your objectives
|
||||
- Don't let others commit you to impossible deadlines
|
||||
- Don't sacrifice quality practices to meet unrealistic dates
|
||||
- Don't work 80-hour weeks hoping to be a hero
|
||||
- Don't stay silent when you see a disaster coming
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| No to Impossible | State clearly when something cannot be done |
|
||||
| Never "Try" | Trying implies you were holding back |
|
||||
| No Passive Aggression | Warn people, don't just let them fail |
|
||||
| Defend Estimates | Maintain position through pressure |
|
||||
| Negotiate Solutions | Say no, then find what IS possible |
|
||||
| Escalate When Needed | Go up the chain if warnings are ignored |
|
||||
@@ -0,0 +1,437 @@
|
||||
# Code Smells Examples
|
||||
|
||||
TypeScript examples demonstrating key code smells and their fixes.
|
||||
|
||||
## G5: Duplication
|
||||
|
||||
### Bad Example
|
||||
|
||||
```typescript
|
||||
// Duplication - copy-pasted logic
|
||||
function calculateRegularPay(employee: Employee): number {
|
||||
const hoursWorked = employee.getHoursWorked();
|
||||
const hourlyRate = employee.getHourlyRate();
|
||||
const straightTime = Math.min(40, hoursWorked);
|
||||
return straightTime * hourlyRate;
|
||||
}
|
||||
|
||||
function calculateOvertimePay(employee: Employee): number {
|
||||
const hoursWorked = employee.getHoursWorked();
|
||||
const hourlyRate = employee.getHourlyRate();
|
||||
const straightTime = Math.min(40, hoursWorked);
|
||||
const overtime = Math.max(0, hoursWorked - straightTime);
|
||||
return straightTime * hourlyRate + overtime * hourlyRate * 1.5;
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Hours and rate retrieval duplicated
|
||||
- Straight time calculation duplicated
|
||||
- Changes require updating both functions
|
||||
|
||||
### Good Example
|
||||
|
||||
```typescript
|
||||
function calculatePay(employee: Employee): PayBreakdown {
|
||||
const hours = employee.getHoursWorked();
|
||||
const rate = employee.getHourlyRate();
|
||||
const straightHours = Math.min(40, hours);
|
||||
const overtimeHours = Math.max(0, hours - 40);
|
||||
|
||||
return {
|
||||
straight: straightHours * rate,
|
||||
overtime: overtimeHours * rate * 1.5,
|
||||
total: straightHours * rate + overtimeHours * rate * 1.5
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Single source of truth for pay calculation
|
||||
- Changes only need to be made once
|
||||
|
||||
---
|
||||
|
||||
## G6: Wrong Level of Abstraction
|
||||
|
||||
### Bad Example
|
||||
|
||||
```typescript
|
||||
interface Stack<T> {
|
||||
push(item: T): void;
|
||||
pop(): T | undefined;
|
||||
peek(): T | undefined;
|
||||
isEmpty(): boolean;
|
||||
// Wrong level - not all stacks have bounded capacity
|
||||
percentFull(): number;
|
||||
isFull(): boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- `percentFull()` assumes bounded capacity
|
||||
- Unbounded stacks can't implement this meaningfully
|
||||
- Forcing implementations to lie (return 0) or throw
|
||||
|
||||
### Good Example
|
||||
|
||||
```typescript
|
||||
interface Stack<T> {
|
||||
push(item: T): void;
|
||||
pop(): T | undefined;
|
||||
peek(): T | undefined;
|
||||
isEmpty(): boolean;
|
||||
}
|
||||
|
||||
interface BoundedStack<T> extends Stack<T> {
|
||||
readonly capacity: number;
|
||||
percentFull(): number;
|
||||
isFull(): boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Base interface has only universal operations
|
||||
- Capacity concepts isolated in appropriate derivative
|
||||
|
||||
---
|
||||
|
||||
## G14: Feature Envy
|
||||
|
||||
### Bad Example
|
||||
|
||||
```typescript
|
||||
class HourlyPayCalculator {
|
||||
calculateWeeklyPay(employee: HourlyEmployee): Money {
|
||||
// This method "envies" HourlyEmployee - uses all its data
|
||||
const tenthRate = employee.getTenthRate().getPennies();
|
||||
const tenthsWorked = employee.getTenthsWorked();
|
||||
const straightTime = Math.min(400, tenthsWorked);
|
||||
const overtime = Math.max(0, tenthsWorked - straightTime);
|
||||
const straightPay = straightTime * tenthRate;
|
||||
const overtimePay = Math.round(overtime * tenthRate * 1.5);
|
||||
return new Money(straightPay + overtimePay);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Calculator reaches deep into Employee for all data
|
||||
- Exposes Employee's internal structure
|
||||
- Logic belongs with the data it operates on
|
||||
|
||||
### Good Example
|
||||
|
||||
```typescript
|
||||
class HourlyEmployee {
|
||||
private tenthRate: Money;
|
||||
private tenthsWorked: number;
|
||||
|
||||
calculateWeeklyPay(): Money {
|
||||
const straightPay = this.calculateStraightPay();
|
||||
const overtimePay = this.calculateOvertimePay();
|
||||
return straightPay.add(overtimePay);
|
||||
}
|
||||
|
||||
private calculateStraightPay(): Money {
|
||||
const straightTenths = Math.min(400, this.tenthsWorked);
|
||||
return this.tenthRate.times(straightTenths);
|
||||
}
|
||||
|
||||
private calculateOvertimePay(): Money {
|
||||
const overtimeTenths = Math.max(0, this.tenthsWorked - 400);
|
||||
return this.tenthRate.times(overtimeTenths).times(1.5);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Calculation lives with its data
|
||||
- Employee encapsulates its own business logic
|
||||
- External callers don't need to know internal structure
|
||||
|
||||
### Acceptable Feature Envy
|
||||
|
||||
```typescript
|
||||
// Sometimes feature envy is acceptable - reporting shouldn't be in domain
|
||||
class HourlyEmployeeReport {
|
||||
constructor(private employee: HourlyEmployee) {}
|
||||
|
||||
formatHoursWorked(): string {
|
||||
// Envy is OK here - we don't want Employee coupled to reporting
|
||||
const hours = Math.floor(this.employee.getTenthsWorked() / 10);
|
||||
const tenths = this.employee.getTenthsWorked() % 10;
|
||||
return `${this.employee.getName()}: ${hours}.${tenths} hours`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## G23: Prefer Polymorphism to Switch
|
||||
|
||||
### Bad Example
|
||||
|
||||
```typescript
|
||||
// Multiple switches on same type - scattered throughout codebase
|
||||
function calculateArea(shape: Shape): number {
|
||||
switch (shape.type) {
|
||||
case 'circle':
|
||||
return Math.PI * shape.radius ** 2;
|
||||
case 'rectangle':
|
||||
return shape.width * shape.height;
|
||||
case 'triangle':
|
||||
return (shape.base * shape.height) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
function calculatePerimeter(shape: Shape): number {
|
||||
switch (shape.type) {
|
||||
case 'circle':
|
||||
return 2 * Math.PI * shape.radius;
|
||||
case 'rectangle':
|
||||
return 2 * (shape.width + shape.height);
|
||||
case 'triangle':
|
||||
return shape.sideA + shape.sideB + shape.sideC;
|
||||
}
|
||||
}
|
||||
|
||||
function draw(shape: Shape): void {
|
||||
switch (shape.type) {
|
||||
case 'circle':
|
||||
drawCircle(shape);
|
||||
break;
|
||||
case 'rectangle':
|
||||
drawRectangle(shape);
|
||||
break;
|
||||
case 'triangle':
|
||||
drawTriangle(shape);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Adding new shape requires changing multiple functions
|
||||
- Switch statements duplicated everywhere
|
||||
- Easy to miss a case when adding shapes
|
||||
|
||||
### Good Example
|
||||
|
||||
```typescript
|
||||
// ONE SWITCH creates objects, polymorphism handles the rest
|
||||
interface Shape {
|
||||
calculateArea(): number;
|
||||
calculatePerimeter(): number;
|
||||
draw(): void;
|
||||
}
|
||||
|
||||
class Circle implements Shape {
|
||||
constructor(private radius: number) {}
|
||||
|
||||
calculateArea(): number {
|
||||
return Math.PI * this.radius ** 2;
|
||||
}
|
||||
|
||||
calculatePerimeter(): number {
|
||||
return 2 * Math.PI * this.radius;
|
||||
}
|
||||
|
||||
draw(): void {
|
||||
// Circle-specific drawing
|
||||
}
|
||||
}
|
||||
|
||||
class Rectangle implements Shape {
|
||||
constructor(private width: number, private height: number) {}
|
||||
|
||||
calculateArea(): number {
|
||||
return this.width * this.height;
|
||||
}
|
||||
|
||||
calculatePerimeter(): number {
|
||||
return 2 * (this.width + this.height);
|
||||
}
|
||||
|
||||
draw(): void {
|
||||
// Rectangle-specific drawing
|
||||
}
|
||||
}
|
||||
|
||||
// Only one switch - in factory
|
||||
function createShape(config: ShapeConfig): Shape {
|
||||
switch (config.type) {
|
||||
case 'circle':
|
||||
return new Circle(config.radius);
|
||||
case 'rectangle':
|
||||
return new Rectangle(config.width, config.height);
|
||||
default:
|
||||
throw new Error(`Unknown shape: ${config.type}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- New shapes only require new class + factory update
|
||||
- No scattered switch statements to maintain
|
||||
- Compiler enforces interface implementation
|
||||
|
||||
---
|
||||
|
||||
## G30: Functions Should Do One Thing
|
||||
|
||||
### Bad Example
|
||||
|
||||
```typescript
|
||||
function pay(employees: Employee[]): void {
|
||||
for (const employee of employees) {
|
||||
if (employee.isPayday()) {
|
||||
const pay = employee.calculatePay();
|
||||
employee.deliverPay(pay);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Function does three things: iterates, checks, and pays
|
||||
- Hard to reuse individual operations
|
||||
- Single responsibility violated
|
||||
|
||||
### Good Example
|
||||
|
||||
```typescript
|
||||
function pay(employees: Employee[]): void {
|
||||
for (const employee of employees) {
|
||||
payIfNecessary(employee);
|
||||
}
|
||||
}
|
||||
|
||||
function payIfNecessary(employee: Employee): void {
|
||||
if (employee.isPayday()) {
|
||||
calculateAndDeliverPay(employee);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateAndDeliverPay(employee: Employee): void {
|
||||
const pay = employee.calculatePay();
|
||||
employee.deliverPay(pay);
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Each function does exactly one thing
|
||||
- Functions are composable and testable
|
||||
- Intent is clear from function names
|
||||
|
||||
---
|
||||
|
||||
## G31: Hidden Temporal Couplings
|
||||
|
||||
### Bad Example
|
||||
|
||||
```typescript
|
||||
class MoogDiver {
|
||||
private gradient: Gradient;
|
||||
private splines: Spline[];
|
||||
|
||||
dive(reason: string): void {
|
||||
// Order matters but nothing enforces it
|
||||
this.saturateGradient();
|
||||
this.reticulateSplines();
|
||||
this.diveForMoog(reason);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
- Call order is critical but not enforced
|
||||
- Easy to reorder incorrectly
|
||||
- No compile-time protection
|
||||
|
||||
### Good Example
|
||||
|
||||
```typescript
|
||||
class MoogDiver {
|
||||
dive(reason: string): void {
|
||||
// Each step produces input for next - order is enforced
|
||||
const gradient = this.saturateGradient();
|
||||
const splines = this.reticulateSplines(gradient);
|
||||
this.diveForMoog(splines, reason);
|
||||
}
|
||||
|
||||
private saturateGradient(): Gradient {
|
||||
// Returns gradient needed by next step
|
||||
}
|
||||
|
||||
private reticulateSplines(gradient: Gradient): Spline[] {
|
||||
// Requires gradient, returns splines
|
||||
}
|
||||
|
||||
private diveForMoog(splines: Spline[], reason: string): void {
|
||||
// Requires splines from previous step
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Dependencies are explicit in function signatures
|
||||
- Cannot call out of order - won't compile
|
||||
- Temporal coupling is now a physical coupling
|
||||
|
||||
---
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before
|
||||
|
||||
```typescript
|
||||
function renderHorizontalRule(size: number): string {
|
||||
let html = '<hr';
|
||||
if (size > 0) {
|
||||
html += ` size="${size + 1}"`;
|
||||
}
|
||||
html += '>';
|
||||
return html;
|
||||
}
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```typescript
|
||||
function renderHorizontalRule(extraDashes: number): string {
|
||||
const hr = new HtmlTag('hr');
|
||||
if (extraDashes > 0) {
|
||||
hr.addAttribute('size', formatHrSize(extraDashes));
|
||||
}
|
||||
return hr.toHtml();
|
||||
}
|
||||
|
||||
function formatHrSize(extraDashes: number): string {
|
||||
return String(extraDashes + 1);
|
||||
}
|
||||
|
||||
class HtmlTag {
|
||||
private attributes: Map<string, string> = new Map();
|
||||
|
||||
constructor(private tagName: string) {}
|
||||
|
||||
addAttribute(name: string, value: string): void {
|
||||
this.attributes.set(name, value);
|
||||
}
|
||||
|
||||
toHtml(): string {
|
||||
const attrs = Array.from(this.attributes)
|
||||
.map(([k, v]) => ` ${k}="${v}"`)
|
||||
.join('');
|
||||
return `<${this.tagName}${attrs} />`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. **Renamed parameter** - `size` to `extraDashes` reveals true meaning
|
||||
2. **Separated abstraction levels** - HTML syntax isolated in `HtmlTag` class
|
||||
3. **Extracted formatting** - Size calculation in dedicated function
|
||||
4. **Fixed bug** - Original missed XHTML closing slash; `HtmlTag` handles correctly
|
||||
@@ -0,0 +1,102 @@
|
||||
# Code Smells Knowledge
|
||||
|
||||
Core concepts and foundational understanding for identifying and addressing code smells.
|
||||
|
||||
## Overview
|
||||
|
||||
Code smells are indicators of deeper problems in code. They are not bugs - the code may work correctly - but they suggest weaknesses in design that may slow development or increase the risk of bugs or failures in the future. This chapter compiles heuristics for recognizing and eliminating common smells.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Code Smell
|
||||
|
||||
**Definition**: A surface indication that usually corresponds to a deeper problem in the system.
|
||||
|
||||
Code smells are warning signs, not definitive rules. They require judgment to evaluate - some smells may be acceptable in certain contexts while being problematic in others.
|
||||
|
||||
**Key points**:
|
||||
- Smells indicate potential problems, not guaranteed defects
|
||||
- Each smell suggests specific refactoring techniques
|
||||
- Multiple smells often compound each other
|
||||
|
||||
### Heuristic
|
||||
|
||||
**Definition**: A practical approach to problem-solving that may not be optimal but is sufficient for finding satisfactory solutions.
|
||||
|
||||
The smells in this chapter are heuristics - they guide decision-making but don't mandate specific actions. Use them to question your code, not as absolute rules.
|
||||
|
||||
### The DRY Principle
|
||||
|
||||
**Definition**: Don't Repeat Yourself - every piece of knowledge should have a single, unambiguous representation in a system.
|
||||
|
||||
Duplication is the root of many smells. When you find duplication, you've found an opportunity for abstraction.
|
||||
|
||||
## Smell Categories
|
||||
|
||||
| Category | Focus Area | Count |
|
||||
|----------|------------|-------|
|
||||
| Comments (C) | Comment quality and necessity | 5 |
|
||||
| Environment (E) | Build and test processes | 2 |
|
||||
| Functions (F) | Function design issues | 4 |
|
||||
| General (G) | Broad design heuristics | 36 |
|
||||
| Names (N) | Naming conventions | 7 |
|
||||
| Tests (T) | Testing practices | 9 |
|
||||
|
||||
## Category Overview
|
||||
|
||||
### Comments (C1-C5)
|
||||
Problems with comments including inappropriate information, obsolete content, redundancy, poor writing, and commented-out code. Comments should explain *why*, not *what*.
|
||||
|
||||
### Environment (E1-E2)
|
||||
Build and test execution should be simple one-step operations. Complexity here slows the entire development cycle.
|
||||
|
||||
### Functions (F1-F4)
|
||||
Function design issues: too many arguments, output arguments, flag arguments, and dead functions. Functions should be small, focused, and intuitive.
|
||||
|
||||
### General (G1-G36)
|
||||
The largest category covering design principles, abstraction levels, coupling, precision, and code organization. These are universal heuristics applicable to any codebase.
|
||||
|
||||
### Names (N1-N7)
|
||||
Naming should be descriptive, unambiguous, and follow conventions. Names are the primary way code communicates intent.
|
||||
|
||||
### Tests (T1-T9)
|
||||
Tests should be comprehensive, fast, and easy to run. They are the safety net that enables refactoring and continuous improvement.
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Refactoring | Restructuring code without changing external behavior |
|
||||
| Abstraction | Hiding implementation details behind an interface |
|
||||
| Coupling | Degree of interdependence between modules |
|
||||
| Cohesion | Degree to which elements of a module belong together |
|
||||
| Polymorphism | Ability to process objects differently based on type |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Clean Functions**: Many smells (F1-F4, G30) directly address function design
|
||||
- **Meaningful Names**: The naming smells (N1-N7) expand on naming principles
|
||||
- **Comments**: C1-C5 reinforce when comments are harmful vs. helpful
|
||||
- **Error Handling**: G3 (boundary conditions), G4 (overridden safeties) relate to robustness
|
||||
- **Testing**: T1-T9 connect to TDD and test-first development
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Eliminating all smells guarantees clean code
|
||||
**Reality**: Smells are heuristics, not absolute rules. Context matters.
|
||||
|
||||
- **Myth**: Code that works doesn't need smell checking
|
||||
**Reality**: Smells affect maintainability, not just correctness.
|
||||
|
||||
- **Myth**: Fixing smells always improves code
|
||||
**Reality**: Over-engineering to avoid smells can be worse than the smell itself.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Code Smell | Surface indicator of deeper design problems |
|
||||
| DRY | Don't Repeat Yourself - eliminate duplication |
|
||||
| Law of Demeter | Only talk to immediate collaborators |
|
||||
| Single Responsibility | Each module should have one reason to change |
|
||||
| Least Surprise | Code should behave as readers expect |
|
||||
514
.agents/skills/typescript-clean-code/references/smells/rules.md
Normal file
514
.agents/skills/typescript-clean-code/references/smells/rules.md
Normal file
@@ -0,0 +1,514 @@
|
||||
# Code Smells Reference
|
||||
|
||||
Complete catalog of code smells organized by category. Use for code review and refactoring.
|
||||
|
||||
---
|
||||
|
||||
## Comments (C1-C5)
|
||||
|
||||
### C1: Inappropriate Information
|
||||
|
||||
**What it is**: Comments containing metadata (change history, authors, dates, ticket numbers)
|
||||
|
||||
**How to fix**: Move to source control, issue tracker, or other record-keeping systems. Comments are for technical notes only.
|
||||
|
||||
---
|
||||
|
||||
### C2: Obsolete Comment
|
||||
|
||||
**What it is**: Comments that are old, irrelevant, or incorrect
|
||||
|
||||
**How to fix**: Update or delete immediately. Obsolete comments mislead readers.
|
||||
|
||||
---
|
||||
|
||||
### C3: Redundant Comment
|
||||
|
||||
**What it is**: Comments that describe what code already clearly shows
|
||||
|
||||
**How to fix**: Delete the comment. Let the code speak for itself.
|
||||
|
||||
```typescript
|
||||
// Bad
|
||||
i++; // increment i
|
||||
|
||||
// Good - no comment needed
|
||||
i++;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### C4: Poorly Written Comment
|
||||
|
||||
**What it is**: Comments with bad grammar, unclear wording, or rambling explanations
|
||||
|
||||
**How to fix**: Rewrite concisely with correct grammar. If worth writing, write well.
|
||||
|
||||
---
|
||||
|
||||
### C5: Commented-Out Code
|
||||
|
||||
**What it is**: Code blocks left commented out "just in case"
|
||||
|
||||
**How to fix**: Delete it. Source control remembers everything.
|
||||
|
||||
---
|
||||
|
||||
## Environment (E1-E2)
|
||||
|
||||
### E1: Build Requires More Than One Step
|
||||
|
||||
**What it is**: Complex build processes requiring multiple commands or manual steps
|
||||
|
||||
**How to fix**: Single command to check out and build:
|
||||
```bash
|
||||
git clone mySystem
|
||||
cd mySystem
|
||||
npm install && npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### E2: Tests Require More Than One Step
|
||||
|
||||
**What it is**: Running tests requires multiple commands or manual configuration
|
||||
|
||||
**How to fix**: Single command to run all tests: `npm test`
|
||||
|
||||
---
|
||||
|
||||
## Functions (F1-F4)
|
||||
|
||||
### F1: Too Many Arguments
|
||||
|
||||
**What it is**: Functions with more than three arguments
|
||||
|
||||
**How to fix**:
|
||||
- Group related arguments into objects
|
||||
- Split function into smaller functions
|
||||
- Zero arguments is best, then one, two, three
|
||||
|
||||
---
|
||||
|
||||
### F2: Output Arguments
|
||||
|
||||
**What it is**: Arguments used to return values instead of using return statements
|
||||
|
||||
**How to fix**: Return values directly. If state must change, change the owning object.
|
||||
|
||||
---
|
||||
|
||||
### F3: Flag Arguments
|
||||
|
||||
**What it is**: Boolean arguments that select between behaviors
|
||||
|
||||
**How to fix**: Split into separate functions - one for each behavior.
|
||||
|
||||
---
|
||||
|
||||
### F4: Dead Function
|
||||
|
||||
**What it is**: Functions that are never called
|
||||
|
||||
**How to fix**: Delete them. Source control remembers.
|
||||
|
||||
---
|
||||
|
||||
## General (G1-G36)
|
||||
|
||||
### G1: Multiple Languages in One Source File
|
||||
|
||||
**What it is**: Mixing languages (HTML in TS, SQL strings, embedded JSON)
|
||||
|
||||
**How to fix**: Minimize extra languages. Separate concerns into distinct files.
|
||||
|
||||
---
|
||||
|
||||
### G2: Obvious Behavior Is Unimplemented
|
||||
|
||||
**What it is**: Functions that don't do what their names suggest
|
||||
|
||||
**How to fix**: Implement expected behaviors. Follow Principle of Least Surprise.
|
||||
|
||||
---
|
||||
|
||||
### G3: Incorrect Behavior at the Boundaries
|
||||
|
||||
**What it is**: Missing edge case handling, untested corner cases
|
||||
|
||||
**How to fix**: Test every boundary condition explicitly. Don't trust intuition.
|
||||
|
||||
---
|
||||
|
||||
### G4: Overridden Safeties
|
||||
|
||||
**What it is**: Disabling warnings, skipping tests, bypassing validations
|
||||
|
||||
**How to fix**: Fix the underlying issues. Don't suppress symptoms.
|
||||
|
||||
---
|
||||
|
||||
### G5: Duplication
|
||||
|
||||
**What it is**: Repeated code, similar switch statements, parallel algorithms
|
||||
|
||||
**How to fix**:
|
||||
- Identical code: Extract to function
|
||||
- Similar conditionals: Use polymorphism
|
||||
- Similar algorithms: Template Method or Strategy pattern
|
||||
|
||||
---
|
||||
|
||||
### G6: Code at Wrong Level of Abstraction
|
||||
|
||||
**What it is**: Implementation details in base classes, generic code in specific classes
|
||||
|
||||
**How to fix**: Separate high-level concepts from low-level details completely.
|
||||
|
||||
---
|
||||
|
||||
### G7: Base Classes Depending on Their Derivatives
|
||||
|
||||
**What it is**: Base classes that reference or know about derived classes
|
||||
|
||||
**How to fix**: Base classes should be ignorant of derivatives. Invert dependencies.
|
||||
|
||||
---
|
||||
|
||||
### G8: Too Much Information
|
||||
|
||||
**What it is**: Classes with too many methods, variables, or public members
|
||||
|
||||
**How to fix**: Hide data, utility functions, constants. Minimize interfaces.
|
||||
|
||||
---
|
||||
|
||||
### G9: Dead Code
|
||||
|
||||
**What it is**: Unreachable code, unused variables, uncalled functions
|
||||
|
||||
**How to fix**: Delete it. Dead code rots and misleads.
|
||||
|
||||
---
|
||||
|
||||
### G10: Vertical Separation
|
||||
|
||||
**What it is**: Variables declared far from usage, functions far from callers
|
||||
|
||||
**How to fix**: Declare variables just before use. Define functions just below first call.
|
||||
|
||||
---
|
||||
|
||||
### G11: Inconsistency
|
||||
|
||||
**What it is**: Similar things done differently throughout the codebase
|
||||
|
||||
**How to fix**: Choose conventions and follow them consistently.
|
||||
|
||||
---
|
||||
|
||||
### G12: Clutter
|
||||
|
||||
**What it is**: Default constructors, unused variables, meaningless comments
|
||||
|
||||
**How to fix**: Remove anything that adds no value.
|
||||
|
||||
---
|
||||
|
||||
### G13: Artificial Coupling
|
||||
|
||||
**What it is**: Dependencies that serve no direct purpose (enums in unrelated classes)
|
||||
|
||||
**How to fix**: Place items where they logically belong, not where convenient.
|
||||
|
||||
---
|
||||
|
||||
### G14: Feature Envy
|
||||
|
||||
**What it is**: Methods that use more of another class than their own
|
||||
|
||||
**How to fix**: Move the method to the class it envies, or rethink responsibilities.
|
||||
|
||||
---
|
||||
|
||||
### G15: Selector Arguments
|
||||
|
||||
**What it is**: Arguments (boolean, enum) that select function behavior
|
||||
|
||||
**How to fix**: Split into multiple functions with descriptive names.
|
||||
|
||||
---
|
||||
|
||||
### G16: Obscured Intent
|
||||
|
||||
**What it is**: Dense expressions, magic numbers, cryptic abbreviations
|
||||
|
||||
**How to fix**: Use explanatory variables and meaningful names.
|
||||
|
||||
---
|
||||
|
||||
### G17: Misplaced Responsibility
|
||||
|
||||
**What it is**: Code placed where convenient rather than where expected
|
||||
|
||||
**How to fix**: Follow Principle of Least Surprise. Place code where readers expect it.
|
||||
|
||||
---
|
||||
|
||||
### G18: Inappropriate Static
|
||||
|
||||
**What it is**: Static methods that should be polymorphic instance methods
|
||||
|
||||
**How to fix**: Prefer non-static. Use static only when polymorphism is impossible.
|
||||
|
||||
---
|
||||
|
||||
### G19: Use Explanatory Variables
|
||||
|
||||
**What it is**: Complex expressions without intermediate named values
|
||||
|
||||
**How to fix**: Break calculations into well-named intermediate variables.
|
||||
|
||||
---
|
||||
|
||||
### G20: Function Names Should Say What They Do
|
||||
|
||||
**What it is**: Ambiguous names like `add()` that don't explain behavior
|
||||
|
||||
**How to fix**: Names should reveal intent: `addDaysTo()` or `daysLater()`.
|
||||
|
||||
---
|
||||
|
||||
### G21: Understand the Algorithm
|
||||
|
||||
**What it is**: Code that works by accident, with unclear logic
|
||||
|
||||
**How to fix**: Refactor until the algorithm is obvious. Know why it works.
|
||||
|
||||
---
|
||||
|
||||
### G22: Make Logical Dependencies Physical
|
||||
|
||||
**What it is**: Assumptions between modules not enforced in code
|
||||
|
||||
**How to fix**: Make dependencies explicit through parameters and interfaces.
|
||||
|
||||
---
|
||||
|
||||
### G23: Prefer Polymorphism to If/Else or Switch/Case
|
||||
|
||||
**What it is**: Type-checking conditionals repeated throughout code
|
||||
|
||||
**How to fix**: ONE SWITCH rule - one switch creates polymorphic objects, no others.
|
||||
|
||||
---
|
||||
|
||||
### G24: Follow Standard Conventions
|
||||
|
||||
**What it is**: Inconsistent style, non-standard patterns
|
||||
|
||||
**How to fix**: Follow team/industry conventions. Let code be the style guide.
|
||||
|
||||
---
|
||||
|
||||
### G25: Replace Magic Numbers with Named Constants
|
||||
|
||||
**What it is**: Raw numbers without context: `86400`, `55`, `7777`
|
||||
|
||||
**How to fix**: Use constants: `SECONDS_PER_DAY`, `LINES_PER_PAGE`.
|
||||
|
||||
---
|
||||
|
||||
### G26: Be Precise
|
||||
|
||||
**What it is**: Lazy decisions, unchecked nulls, ignored edge cases
|
||||
|
||||
**How to fix**: Handle all cases explicitly. Use appropriate types.
|
||||
|
||||
---
|
||||
|
||||
### G27: Structure over Convention
|
||||
|
||||
**What it is**: Relying on naming to enforce design instead of code structure
|
||||
|
||||
**How to fix**: Use abstract classes/interfaces to force compliance.
|
||||
|
||||
---
|
||||
|
||||
### G28: Encapsulate Conditionals
|
||||
|
||||
**What it is**: Complex boolean expressions inline in if statements
|
||||
|
||||
**How to fix**: Extract to well-named functions.
|
||||
|
||||
```typescript
|
||||
// Bad
|
||||
if (timer.hasExpired() && !timer.isRecurrent())
|
||||
|
||||
// Good
|
||||
if (shouldBeDeleted(timer))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### G29: Avoid Negative Conditionals
|
||||
|
||||
**What it is**: Negated conditions that are harder to understand
|
||||
|
||||
**How to fix**: Express positively: `shouldCompact()` not `!shouldNotCompact()`.
|
||||
|
||||
---
|
||||
|
||||
### G30: Functions Should Do One Thing
|
||||
|
||||
**What it is**: Functions with multiple distinct operations
|
||||
|
||||
**How to fix**: Extract each operation into its own function.
|
||||
|
||||
---
|
||||
|
||||
### G31: Hidden Temporal Couplings
|
||||
|
||||
**What it is**: Function call order requirements not visible in code
|
||||
|
||||
**How to fix**: Chain results - each function produces what the next needs.
|
||||
|
||||
---
|
||||
|
||||
### G32: Don't Be Arbitrary
|
||||
|
||||
**What it is**: Structure without clear purpose
|
||||
|
||||
**How to fix**: Have reasons for structure. Make them evident in code.
|
||||
|
||||
---
|
||||
|
||||
### G33: Encapsulate Boundary Conditions
|
||||
|
||||
**What it is**: `+1` and `-1` scattered throughout code
|
||||
|
||||
**How to fix**: Capture in named variables: `nextLevel = level + 1`.
|
||||
|
||||
---
|
||||
|
||||
### G34: Functions Should Descend Only One Level of Abstraction
|
||||
|
||||
**What it is**: Mixing high-level logic with low-level details
|
||||
|
||||
**How to fix**: Keep each function at one abstraction level.
|
||||
|
||||
---
|
||||
|
||||
### G35: Keep Configurable Data at High Levels
|
||||
|
||||
**What it is**: Configuration values buried in low-level code
|
||||
|
||||
**How to fix**: Define defaults at top level, pass down as parameters.
|
||||
|
||||
---
|
||||
|
||||
### G36: Avoid Transitive Navigation
|
||||
|
||||
**What it is**: Chain calls like `a.getB().getC().doSomething()`
|
||||
|
||||
**How to fix**: Law of Demeter - talk only to immediate collaborators.
|
||||
|
||||
---
|
||||
|
||||
## Names (N1-N7)
|
||||
|
||||
### N1: Choose Descriptive Names
|
||||
|
||||
**What it is**: Names that don't reveal intent
|
||||
|
||||
**How to fix**: Names should describe what and why, not how.
|
||||
|
||||
### N2: Choose Names at the Appropriate Level of Abstraction
|
||||
|
||||
**What it is**: Implementation details in interface names
|
||||
|
||||
**How to fix**: Name for concept level, not implementation.
|
||||
|
||||
### N3: Use Standard Nomenclature Where Possible
|
||||
|
||||
**What it is**: Custom names for well-known patterns
|
||||
|
||||
**How to fix**: Use recognized terms (Factory, Visitor, Decorator).
|
||||
|
||||
### N4: Unambiguous Names
|
||||
|
||||
**What it is**: Names that could mean multiple things
|
||||
|
||||
**How to fix**: Be specific. Disambiguate.
|
||||
|
||||
### N5: Use Long Names for Long Scopes
|
||||
|
||||
**What it is**: Short names used across large scopes
|
||||
|
||||
**How to fix**: Scope determines length. Longer scope = longer name.
|
||||
|
||||
### N6: Avoid Encodings
|
||||
|
||||
**What it is**: Type prefixes, Hungarian notation
|
||||
|
||||
**How to fix**: Let the type system handle types. Name for meaning.
|
||||
|
||||
### N7: Names Should Describe Side-Effects
|
||||
|
||||
**What it is**: Names hiding what functions actually do
|
||||
|
||||
**How to fix**: Reveal all effects: `createOrReturnOos()` not `getOos()`.
|
||||
|
||||
---
|
||||
|
||||
## Tests (T1-T9)
|
||||
|
||||
### T1: Insufficient Tests
|
||||
|
||||
**How to fix**: Test everything that could break. Use coverage tools.
|
||||
|
||||
### T2: Use a Coverage Tool
|
||||
|
||||
**How to fix**: Run coverage reports. Fill gaps in tested code.
|
||||
|
||||
### T3: Don't Skip Trivial Tests
|
||||
|
||||
**How to fix**: Trivial tests document behavior and catch regressions.
|
||||
|
||||
### T4: An Ignored Test Is a Question about an Ambiguity
|
||||
|
||||
**How to fix**: Resolve ambiguity, then enable or remove the test.
|
||||
|
||||
### T5: Test Boundary Conditions
|
||||
|
||||
**How to fix**: Explicitly test edges, corners, and limits.
|
||||
|
||||
### T6: Exhaustively Test Near Bugs
|
||||
|
||||
**How to fix**: Bugs cluster. When you find one, test the surrounding code.
|
||||
|
||||
### T7: Patterns of Failure Are Revealing
|
||||
|
||||
**How to fix**: Analyze which tests fail together to find root causes.
|
||||
|
||||
### T8: Test Coverage Patterns Can Be Revealing
|
||||
|
||||
**How to fix**: Look at what's untested - it often reveals design problems.
|
||||
|
||||
### T9: Tests Should Be Fast
|
||||
|
||||
**How to fix**: Slow tests don't get run. Keep them fast.
|
||||
|
||||
---
|
||||
|
||||
## Quick Detection Table
|
||||
|
||||
| ID | Smell | Key Indicator |
|
||||
|----|-------|---------------|
|
||||
| C5 | Commented-Out Code | `//` or `/* */` around functional code |
|
||||
| G5 | Duplication | Copy-pasted blocks, similar switches |
|
||||
| G9 | Dead Code | Unreachable branches, uncalled functions |
|
||||
| G14 | Feature Envy | Method uses other class more than own |
|
||||
| G23 | Overuse of Switch | Multiple switches on same type |
|
||||
| G30 | Does Too Much | Function has multiple sections |
|
||||
| G36 | Law of Demeter | Chain of getters: `a.b().c().d()` |
|
||||
174
.agents/skills/typescript-clean-code/references/tdd/examples.md
Normal file
174
.agents/skills/typescript-clean-code/references/tdd/examples.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# TDD Examples
|
||||
|
||||
Code examples demonstrating the TDD red-green-refactor cycle in TypeScript.
|
||||
|
||||
## The TDD Cycle in Practice
|
||||
|
||||
### Example: Building a Stack
|
||||
|
||||
We'll build a simple Stack class following the three laws of TDD.
|
||||
|
||||
#### Cycle 1: Empty Stack
|
||||
|
||||
**Red** - Write a failing test:
|
||||
|
||||
```typescript
|
||||
// stack.test.ts
|
||||
import { Stack } from './stack';
|
||||
|
||||
describe('Stack', () => {
|
||||
it('should be empty when created', () => {
|
||||
const stack = new Stack<number>();
|
||||
expect(stack.isEmpty()).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Green** - Write minimal code to pass:
|
||||
|
||||
```typescript
|
||||
// stack.ts
|
||||
export class Stack<T> {
|
||||
isEmpty(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Test fails first (Stack doesn't exist)
|
||||
- We write just enough to compile and pass
|
||||
- No unnecessary code
|
||||
|
||||
#### Cycle 2: Push Makes Non-Empty
|
||||
|
||||
**Red** - Write the next failing test:
|
||||
|
||||
```typescript
|
||||
it('should not be empty after push', () => {
|
||||
const stack = new Stack<number>();
|
||||
stack.push(1);
|
||||
expect(stack.isEmpty()).toBe(false);
|
||||
});
|
||||
```
|
||||
|
||||
**Green** - Make it pass:
|
||||
|
||||
```typescript
|
||||
export class Stack<T> {
|
||||
private items: T[] = [];
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.items.length === 0;
|
||||
}
|
||||
|
||||
push(item: T): void {
|
||||
this.items.push(item);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Cycle 3: Pop Returns Pushed Item
|
||||
|
||||
**Red**:
|
||||
|
||||
```typescript
|
||||
it('should pop the pushed item', () => {
|
||||
const stack = new Stack<number>();
|
||||
stack.push(42);
|
||||
expect(stack.pop()).toBe(42);
|
||||
});
|
||||
```
|
||||
|
||||
**Green**:
|
||||
|
||||
```typescript
|
||||
export class Stack<T> {
|
||||
private items: T[] = [];
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.items.length === 0;
|
||||
}
|
||||
|
||||
push(item: T): void {
|
||||
this.items.push(item);
|
||||
}
|
||||
|
||||
pop(): T | undefined {
|
||||
return this.items.pop();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Cycle 4: LIFO Order
|
||||
|
||||
**Red**:
|
||||
|
||||
```typescript
|
||||
it('should pop in LIFO order', () => {
|
||||
const stack = new Stack<number>();
|
||||
stack.push(1);
|
||||
stack.push(2);
|
||||
expect(stack.pop()).toBe(2);
|
||||
expect(stack.pop()).toBe(1);
|
||||
});
|
||||
```
|
||||
|
||||
**Green**: Already passes! Our implementation naturally handles this.
|
||||
|
||||
**Refactor**: Tests are clean, code is clean. Nothing to refactor yet.
|
||||
|
||||
---
|
||||
|
||||
## Refactoring Example
|
||||
|
||||
### Before Refactoring
|
||||
|
||||
After several TDD cycles, we might have:
|
||||
|
||||
```typescript
|
||||
export class UserService {
|
||||
createUser(name: string, email: string): User {
|
||||
if (!name || name.trim() === '') {
|
||||
throw new Error('Name required');
|
||||
}
|
||||
if (!email || !email.includes('@')) {
|
||||
throw new Error('Valid email required');
|
||||
}
|
||||
const user = { id: generateId(), name: name.trim(), email: email.toLowerCase() };
|
||||
saveToDatabase(user);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### After Refactoring
|
||||
|
||||
With tests protecting us, we safely extract validation methods:
|
||||
|
||||
```typescript
|
||||
export class UserService {
|
||||
createUser(name: string, email: string): User {
|
||||
const validatedName = this.validateName(name);
|
||||
const validatedEmail = this.validateEmail(email);
|
||||
return this.buildUser(validatedName, validatedEmail);
|
||||
}
|
||||
|
||||
private validateName(name: string): string { /* validation logic */ }
|
||||
private validateEmail(email: string): string { /* validation logic */ }
|
||||
private buildUser(name: string, email: string): User { /* create user */ }
|
||||
}
|
||||
```
|
||||
|
||||
**Key point**: Tests still pass - refactoring is safe
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Phase | Action | Duration |
|
||||
|-------|--------|----------|
|
||||
| Red | Write failing test | ~10 sec |
|
||||
| Green | Write minimal code | ~15 sec |
|
||||
| Refactor | Clean up | ~5 sec |
|
||||
| **Total** | One cycle | ~30 sec |
|
||||
118
.agents/skills/typescript-clean-code/references/tdd/knowledge.md
Normal file
118
.agents/skills/typescript-clean-code/references/tdd/knowledge.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# TDD Knowledge
|
||||
|
||||
Core concepts and foundational understanding for Test Driven Development.
|
||||
|
||||
## Overview
|
||||
|
||||
Test Driven Development (TDD) is a discipline where you write tests before writing production code, following a rapid cycle of test-code-refactor. It provides certainty that your code works, reduces defects, gives you courage to refactor, serves as documentation, and drives better design.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### The Jury Is In
|
||||
|
||||
**Definition**: TDD is a proven, settled practice - not an experimental technique.
|
||||
|
||||
The debate is over. Like GOTO being harmful, TDD's effectiveness is established fact. Studies from IBM, Microsoft, Sabre, and Symantec show defect reductions of 2X, 5X, and even 10X.
|
||||
|
||||
**Key points**:
|
||||
- Programmers should not have to defend TDD any more than surgeons defend hand-washing
|
||||
- The evidence is overwhelming across multiple companies and teams
|
||||
- Controversy at this point is just "rants," not serious critique
|
||||
|
||||
### Certainty
|
||||
|
||||
**Definition**: The confidence that your changes haven't broken anything.
|
||||
|
||||
With TDD, you write thousands of tests that run in seconds. After any change, run the tests - if they pass, you're nearly certain nothing broke.
|
||||
|
||||
**Key points**:
|
||||
- "Nearly certain" means certain enough to ship
|
||||
- Real example: FitNesse has 64,000 lines of code, 2,200 tests, 90% coverage, runs in 90 seconds
|
||||
- The QA process can become simply: build + run tests + ship
|
||||
|
||||
### Defect Injection Rate
|
||||
|
||||
**Definition**: The rate at which bugs are introduced into the codebase.
|
||||
|
||||
TDD dramatically reduces defect injection. Studies show 2X to 10X reduction in defects.
|
||||
|
||||
**Key points**:
|
||||
- FitNesse example: 20,000 new lines of code, only 17 bugs (many cosmetic)
|
||||
- These are numbers no professional should ignore
|
||||
- Multiple independent studies confirm the effect
|
||||
|
||||
### Courage
|
||||
|
||||
**Definition**: The willingness to improve code without fear of breaking it.
|
||||
|
||||
TDD eliminates the fear of touching messy code. When you trust your test suite, you clean code on the spot.
|
||||
|
||||
**Key points**:
|
||||
- Without tests: "This is a mess" -> "I'm not touching it!"
|
||||
- With tests: You can click a button and know in 90 seconds your changes broke nothing
|
||||
- Code becomes clay you can safely sculpt
|
||||
- Code base improves instead of rotting
|
||||
|
||||
### Documentation
|
||||
|
||||
**Definition**: Tests serve as executable, accurate documentation of how the system works.
|
||||
|
||||
Unit tests describe how to create every object and call every function in meaningful ways. They are unambiguous, accurate, and written in a language developers understand.
|
||||
|
||||
**Key points**:
|
||||
- Programmers go to code examples first, not prose documentation
|
||||
- Tests are "the best kind of low-level documentation that can exist"
|
||||
- Tests execute, so they can never be out of date
|
||||
|
||||
### Design Benefits
|
||||
|
||||
**Definition**: TDD forces you to think about design and decoupling before writing code.
|
||||
|
||||
Writing tests first forces you to isolate code, which drives better decoupled design.
|
||||
|
||||
**Key points**:
|
||||
- Testing code requires isolating it from dependencies
|
||||
- Without tests first, nothing prevents coupling into an "untestable mass"
|
||||
- Tests written first are "offense"; tests written after are "defense"
|
||||
- After-the-fact tests can't be as incisive as test-first tests
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| TDD | Test Driven Development - write tests before production code |
|
||||
| Red-Green-Refactor | The TDD cycle: failing test, make it pass, improve code |
|
||||
| Test First | Writing the test before the implementation |
|
||||
| Cycle Time | Time between running tests (aim for ~30 seconds) |
|
||||
| Coverage | Percentage of production code exercised by tests |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Unit Tests**: TDD produces comprehensive unit test suites as a byproduct
|
||||
- **Refactoring**: TDD provides the safety net that makes refactoring possible
|
||||
- **Clean Code**: TDD encourages cleaner, more decoupled designs
|
||||
- **Professionalism**: TDD is the professional option for software development
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: TDD is just about testing
|
||||
**Reality**: TDD is primarily about design and confidence, tests are a byproduct
|
||||
|
||||
- **Myth**: You can write tests later and get the same benefits
|
||||
**Reality**: Tests written after are "defense"; tests written first are "offense"
|
||||
|
||||
- **Myth**: TDD is a magic formula that guarantees good code
|
||||
**Reality**: You can still write bad code and bad tests with TDD
|
||||
|
||||
- **Myth**: TDD always applies
|
||||
**Reality**: There are rare situations where TDD is impractical or inappropriate
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Certainty | Run tests, know nothing broke, ship with confidence |
|
||||
| Defect Reduction | 2X-10X fewer bugs with TDD |
|
||||
| Courage | Trust tests, clean code fearlessly |
|
||||
| Documentation | Tests are executable, always-current docs |
|
||||
| Design | Test-first forces decoupled design |
|
||||
94
.agents/skills/typescript-clean-code/references/tdd/rules.md
Normal file
94
.agents/skills/typescript-clean-code/references/tdd/rules.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# TDD Rules
|
||||
|
||||
The three laws of TDD and guidelines for professional practice.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. First Law: No Production Code Without a Failing Test
|
||||
|
||||
You are not allowed to write any production code until you have first written a failing unit test.
|
||||
|
||||
- Start every feature or fix with a test
|
||||
- The test defines what you're about to build
|
||||
- Even a compile error counts as a failing test
|
||||
|
||||
### 2. Second Law: Minimal Failing Test
|
||||
|
||||
You are not allowed to write more of a unit test than is sufficient to fail - and not compiling is failing.
|
||||
|
||||
- Write just enough test to fail
|
||||
- A compilation failure is a valid failure
|
||||
- Stop writing test code as soon as it fails to compile or run
|
||||
|
||||
### 3. Third Law: Minimal Production Code
|
||||
|
||||
You are not allowed to write more production code than is sufficient to pass the currently failing unit test.
|
||||
|
||||
- Write only enough code to make the test pass
|
||||
- Don't anticipate future needs
|
||||
- Stop as soon as the test goes green
|
||||
|
||||
## The TDD Cycle
|
||||
|
||||
The three laws lock you into a ~30 second cycle:
|
||||
|
||||
1. **Red**: Write a small failing test
|
||||
2. **Green**: Write minimal code to pass
|
||||
3. **Refactor**: Clean up while tests stay green
|
||||
4. Repeat
|
||||
|
||||
**Key insight**: The test code and production code grow simultaneously into complementary components - "like an antibody fits an antigen."
|
||||
|
||||
## Guidelines
|
||||
|
||||
### The Professional Option
|
||||
|
||||
TDD enhances:
|
||||
- **Certainty** - Know your code works
|
||||
- **Courage** - Fearlessly improve code
|
||||
- **Defect reduction** - 2X-10X fewer bugs
|
||||
- **Documentation** - Tests describe the system
|
||||
- **Design** - Forces decoupling
|
||||
|
||||
> It could be considered *unprofessional* not to use TDD.
|
||||
|
||||
### Rapid Cycle Time
|
||||
|
||||
- Aim for ~30 second cycles
|
||||
- Run tests after every small change
|
||||
- Don't go more than a few minutes without green tests
|
||||
|
||||
### Test Coverage Through TDD
|
||||
|
||||
- Following the three laws naturally produces high coverage
|
||||
- Every object creation method gets tested
|
||||
- Every meaningful function call gets tested
|
||||
- No production code exists without a corresponding test
|
||||
|
||||
## Exceptions
|
||||
|
||||
When TDD may not apply:
|
||||
|
||||
- **Impractical situations**: Rare cases where the discipline does more harm than good
|
||||
- **Exploratory spikes**: Sometimes you need to explore before you test
|
||||
- **Legacy code**: May need different strategies for untested codebases
|
||||
|
||||
> No professional developer should ever follow a discipline when that discipline does more harm than good.
|
||||
|
||||
## What TDD Is Not
|
||||
|
||||
- **Not a religion**: It's a practical discipline, not dogma
|
||||
- **Not a magic formula**: Following the laws doesn't guarantee good code
|
||||
- **Not a guarantee**: You can still write bad code and bad tests
|
||||
- **Not always applicable**: Use judgment about when it applies
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| Law 1 | No production code without a failing test first |
|
||||
| Law 2 | Write only enough test to fail (compile failure counts) |
|
||||
| Law 3 | Write only enough code to pass the failing test |
|
||||
| Cycle Time | ~30 seconds between test runs |
|
||||
| Coverage | Emerges naturally from following the three laws |
|
||||
| Professional | TDD is the professional option, not an optional extra |
|
||||
@@ -0,0 +1,193 @@
|
||||
# Testing Strategies Examples
|
||||
|
||||
Code examples demonstrating the test automation pyramid in TypeScript.
|
||||
|
||||
## Unit Tests (Vitest/Jest)
|
||||
|
||||
### Business Logic Test
|
||||
|
||||
```typescript
|
||||
// src/services/pricing.ts
|
||||
export function calculateDiscount(price: number, quantity: number): number {
|
||||
if (quantity >= 100) return price * 0.20;
|
||||
if (quantity >= 50) return price * 0.10;
|
||||
if (quantity >= 10) return price * 0.05;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// src/services/pricing.test.ts
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { calculateDiscount } from './pricing';
|
||||
|
||||
describe('calculateDiscount', () => {
|
||||
it('applies 20% discount for 100+ items', () => {
|
||||
expect(calculateDiscount(100, 100)).toBe(20);
|
||||
});
|
||||
|
||||
it('applies 10% discount for 50-99 items', () => {
|
||||
expect(calculateDiscount(100, 50)).toBe(10);
|
||||
});
|
||||
|
||||
it('applies 5% discount for 10-49 items', () => {
|
||||
expect(calculateDiscount(100, 10)).toBe(5);
|
||||
});
|
||||
|
||||
it('applies no discount for fewer than 10 items', () => {
|
||||
expect(calculateDiscount(100, 9)).toBe(0);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Tests pure business logic in isolation
|
||||
- Covers all branches with explicit assertions
|
||||
- Fast execution, runs on every commit
|
||||
|
||||
## Component Tests (Supertest)
|
||||
|
||||
### API Endpoint Test
|
||||
|
||||
```typescript
|
||||
// src/routes/orders.test.ts
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import { app } from '../app';
|
||||
import { OrderService } from '../services/orderService';
|
||||
|
||||
// Mock the service layer
|
||||
vi.mock('../services/orderService');
|
||||
|
||||
describe('POST /api/orders', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('creates order and returns 201 with order details', async () => {
|
||||
const mockOrder = { id: '123', items: [], total: 99.99 };
|
||||
vi.mocked(OrderService.create).mockResolvedValue(mockOrder);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/orders')
|
||||
.send({ items: [{ productId: 'abc', quantity: 2 }] })
|
||||
.expect(201);
|
||||
|
||||
expect(response.body).toEqual(mockOrder);
|
||||
expect(OrderService.create).toHaveBeenCalledWith({
|
||||
items: [{ productId: 'abc', quantity: 2 }]
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 400 for invalid order data', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/orders')
|
||||
.send({ items: [] })
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.error).toBe('Order must have at least one item');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Tests component in isolation with mocked dependencies
|
||||
- Verifies input/output behavior
|
||||
- Business stakeholders can understand the test intent
|
||||
|
||||
## Integration Tests (Supertest + Database)
|
||||
|
||||
### Multi-Component Communication Test
|
||||
|
||||
```typescript
|
||||
// tests/integration/order-flow.test.ts
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import { app } from '../../src/app';
|
||||
import { db } from '../../src/database';
|
||||
import { setupTestDatabase, teardownTestDatabase } from '../helpers';
|
||||
|
||||
describe('Order Flow Integration', () => {
|
||||
beforeAll(async () => {
|
||||
await setupTestDatabase();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await teardownTestDatabase();
|
||||
});
|
||||
|
||||
it('order creation updates inventory and notifies warehouse', async () => {
|
||||
// Seed test data
|
||||
await db.products.create({ id: 'prod-1', stock: 10 });
|
||||
|
||||
// Create order through API
|
||||
const orderResponse = await request(app)
|
||||
.post('/api/orders')
|
||||
.send({ items: [{ productId: 'prod-1', quantity: 3 }] })
|
||||
.expect(201);
|
||||
|
||||
// Verify inventory was updated
|
||||
const product = await db.products.findById('prod-1');
|
||||
expect(product.stock).toBe(7);
|
||||
|
||||
// Verify warehouse notification was queued
|
||||
const notifications = await db.notifications.findByOrderId(
|
||||
orderResponse.body.id
|
||||
);
|
||||
expect(notifications).toHaveLength(1);
|
||||
expect(notifications[0].type).toBe('WAREHOUSE_PICK');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Tests choreography between components
|
||||
- Verifies real database interactions
|
||||
- Runs periodically, not on every commit
|
||||
|
||||
## System Tests (Playwright)
|
||||
|
||||
### End-to-End User Flow
|
||||
|
||||
```typescript
|
||||
// tests/e2e/checkout.spec.ts
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Checkout Flow', () => {
|
||||
test('user can complete purchase', async ({ page }) => {
|
||||
// Navigate and add item to cart
|
||||
await page.goto('/products');
|
||||
await page.click('[data-testid="product-widget-1"]');
|
||||
await page.click('[data-testid="add-to-cart"]');
|
||||
|
||||
// Go to checkout
|
||||
await page.click('[data-testid="cart-icon"]');
|
||||
await page.click('[data-testid="checkout-button"]');
|
||||
|
||||
// Fill payment details
|
||||
await page.fill('[name="cardNumber"]', '4111111111111111');
|
||||
await page.fill('[name="expiry"]', '12/25');
|
||||
await page.fill('[name="cvv"]', '123');
|
||||
|
||||
// Complete purchase
|
||||
await page.click('[data-testid="pay-button"]');
|
||||
|
||||
// Verify confirmation
|
||||
await expect(page.locator('[data-testid="confirmation"]'))
|
||||
.toContainText('Order confirmed');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**:
|
||||
- Tests entire system end-to-end
|
||||
- Verifies system construction and wiring
|
||||
- Runs infrequently due to longer execution time
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Level | Tool | Frequency | Coverage |
|
||||
|-------|------|-----------|----------|
|
||||
| Unit | Vitest/Jest | Every commit | ~90% |
|
||||
| Component | Supertest + Mocks | Every commit | ~50% |
|
||||
| Integration | Supertest + DB | Nightly | Architectural |
|
||||
| System | Playwright | Weekly | ~10% |
|
||||
| Exploratory | Human | Sprint end | Creative |
|
||||
@@ -0,0 +1,80 @@
|
||||
# Testing Strategies Knowledge
|
||||
|
||||
Core concepts and foundational understanding for professional testing strategies.
|
||||
|
||||
## Overview
|
||||
|
||||
Professional developers need more than unit tests and acceptance tests - they need a comprehensive testing strategy. The goal is that QA should find nothing wrong, achieved through a hierarchy of automated tests at different levels of the system.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### QA Should Find Nothing
|
||||
|
||||
**Definition**: The development team's goal should be that QA discovers zero defects.
|
||||
|
||||
Every time QA finds something, the development team should react with concern and take steps to prevent similar issues in the future. While this goal may not always be achieved, it should drive the team's testing practices.
|
||||
|
||||
**Key points**:
|
||||
- QA finding bugs indicates gaps in the testing strategy
|
||||
- Each discovered bug is an opportunity to improve prevention
|
||||
- Development owns quality, not QA
|
||||
|
||||
### QA as Part of the Team
|
||||
|
||||
**Definition**: QA and Development work together toward quality, not as adversaries.
|
||||
|
||||
QA serves two critical roles that complement development:
|
||||
|
||||
**Key points**:
|
||||
- QA acts as specifiers (creating acceptance tests from requirements)
|
||||
- QA acts as characterizers (exploring actual system behavior)
|
||||
- Business writes happy-path tests; QA writes edge cases
|
||||
|
||||
### The Test Automation Pyramid
|
||||
|
||||
**Definition**: A hierarchical structure of automated tests, with unit tests forming the large base and exploratory tests at the small top.
|
||||
|
||||
The pyramid represents both the quantity and granularity of tests needed at each level. More tests at the bottom (fast, focused), fewer at the top (slow, broad).
|
||||
|
||||
**Key points**:
|
||||
- Unit tests form the foundation (highest quantity)
|
||||
- Each level builds on the confidence of levels below
|
||||
- Higher levels test integration, not business rules
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Unit Test | Tests written by programmers to specify system at lowest level |
|
||||
| Component Test | Acceptance tests for individual components and business rules |
|
||||
| Integration Test | Tests how component assemblies communicate together |
|
||||
| System Test | Tests against entire integrated system |
|
||||
| Exploratory Test | Manual, unscripted testing to find unexpected behaviors |
|
||||
| Test Doubles | Mocks/stubs used to isolate components under test |
|
||||
| Choreography Test | Tests that verify components work together (integration) |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **TDD**: Unit tests from the pyramid's base come from Test-Driven Development
|
||||
- **Acceptance Testing**: Component tests are the acceptance tests for business rules
|
||||
- **Continuous Integration**: Lower-level tests run on every commit; higher levels run periodically
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: More tests at every level is always better
|
||||
**Reality**: The pyramid shape is intentional - more unit tests, fewer system tests
|
||||
|
||||
- **Myth**: QA is responsible for finding all bugs
|
||||
**Reality**: Development should prevent bugs; QA validates the prevention worked
|
||||
|
||||
- **Myth**: Manual testing can be replaced entirely by automation
|
||||
**Reality**: Exploratory testing requires human creativity and cannot be scripted
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| QA Finds Nothing | Development's goal is zero defects reaching QA |
|
||||
| QA as Specifiers | QA translates requirements into acceptance tests |
|
||||
| QA as Characterizers | QA identifies actual system behavior through exploration |
|
||||
| Test Pyramid | Unit (90%+) > Component (50%) > Integration > System (10%) > Exploratory |
|
||||
@@ -0,0 +1,86 @@
|
||||
# Testing Strategies Rules
|
||||
|
||||
Rules for implementing a comprehensive testing strategy across all levels of the test pyramid.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Unit Tests
|
||||
|
||||
Tests written by programmers, for programmers, in the system's language.
|
||||
|
||||
- Write tests BEFORE production code (TDD)
|
||||
- Execute as part of Continuous Integration
|
||||
- Target ~90%+ true coverage (not false tests that execute without asserting)
|
||||
|
||||
**Coverage**: As close to 100% as practical (typically 90s%)
|
||||
|
||||
### 2. Component Tests
|
||||
|
||||
Acceptance tests for individual components encapsulating business rules.
|
||||
|
||||
- Wrap individual components with input/output verification
|
||||
- Decouple from other components using mocks and test doubles
|
||||
- Business should be able to read and interpret these tests
|
||||
- Written by QA and Business with development assistance
|
||||
|
||||
**Coverage**: ~50% of system (happy paths + obvious edge cases)
|
||||
|
||||
### 3. Integration Tests
|
||||
|
||||
Tests that verify component assemblies communicate correctly.
|
||||
|
||||
- Test choreography, not business rules
|
||||
- Verify plumbing and connections between components
|
||||
- Written by system architects or lead designers
|
||||
- Run periodically (nightly/weekly), not on every CI build
|
||||
- Include performance and throughput tests at this level
|
||||
|
||||
**Coverage**: Architectural soundness verification
|
||||
|
||||
### 4. System Tests
|
||||
|
||||
Automated tests against the entire integrated system.
|
||||
|
||||
- Ultimate integration tests
|
||||
- Verify system is wired together correctly
|
||||
- Test that parts interoperate according to plan
|
||||
- Include throughput and performance tests
|
||||
- Written by architects and technical leads
|
||||
|
||||
**Coverage**: ~10% of system (construction, not behavior)
|
||||
|
||||
### 5. Manual Exploratory Tests
|
||||
|
||||
Human-driven, unscripted testing.
|
||||
|
||||
- NOT automated
|
||||
- NOT scripted (no written test plans)
|
||||
- Explore for unexpected behaviors
|
||||
- Confirm expected behaviors
|
||||
- Use human creativity to investigate the system
|
||||
|
||||
**Goal**: Find peculiarities, not prove coverage
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Run tests as frequently as possible for maximum feedback
|
||||
- Lower pyramid levels should run more frequently
|
||||
- Business writes happy-path tests; QA writes corner/boundary/unhappy-path tests
|
||||
- React to every QA-found bug with process improvement
|
||||
- Keep system continuously clean through continuous testing
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **Small systems**: May skip integration tests if few components exist
|
||||
- **Performance-critical paths**: May need additional system-level tests beyond 10%
|
||||
- **Rapidly changing UI**: May reduce component test coverage in favor of unit tests
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Test Level | Coverage | Frequency | Written By |
|
||||
|------------|----------|-----------|------------|
|
||||
| Unit | ~90%+ | Every CI build | Developers |
|
||||
| Component | ~50% | Every CI build | QA + Business + Dev |
|
||||
| Integration | Architectural | Periodic (nightly/weekly) | Architects |
|
||||
| System | ~10% | Periodic | Tech Leads |
|
||||
| Exploratory | N/A | As needed | Humans (anyone) |
|
||||
@@ -0,0 +1,191 @@
|
||||
# Time Management Examples
|
||||
|
||||
Scenarios demonstrating good vs bad time management decisions.
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Accepting All Meeting Invites
|
||||
|
||||
**Scenario**: Developer receives invite for cross-team architecture discussion. Topic is interesting but unrelated to current sprint work.
|
||||
|
||||
**Bad Decision**: Accepts because "it might be useful to know about" and "I don't want to seem unhelpful."
|
||||
|
||||
**Problems**:
|
||||
- 2-hour meeting costs focus-manna needed for actual deliverables
|
||||
- Sets precedent for future invites
|
||||
- No immediate benefit to current responsibilities
|
||||
- Interest is not the same as necessity
|
||||
|
||||
---
|
||||
|
||||
### Staying in Hijacked Meetings
|
||||
|
||||
**Scenario**: Joined a 30-minute meeting about API design. After 15 minutes, discussion shifts to someone's pet peeve about logging standards.
|
||||
|
||||
**Bad Decision**: Stays for remaining 45 minutes (meeting runs over) because "it would be rude to leave."
|
||||
|
||||
**Problems**:
|
||||
- Remaining is unprofessional - you're wasting employer's money
|
||||
- You had no value to add to the new topic
|
||||
- Lost an hour that could have been productive
|
||||
|
||||
---
|
||||
|
||||
### Avoiding the Hard Task
|
||||
|
||||
**Scenario**: Need to refactor authentication module (scary, might break things). Instead, spend morning on "urgent" task of reorganizing test folder structure.
|
||||
|
||||
**Bad Decision**: Convince yourself folder organization is more important right now.
|
||||
|
||||
**Problems**:
|
||||
- Classic priority inversion
|
||||
- Building defenses against future judgment ("I was being productive!")
|
||||
- Auth refactor remains undone and gets harder over time
|
||||
- The lie compounds - tomorrow you'll find another avoidance task
|
||||
|
||||
---
|
||||
|
||||
### Pushing Through a Blind Alley
|
||||
|
||||
**Scenario**: Chose microservices architecture. Two months in, realize the overhead is killing velocity for a team of three. But you championed this approach.
|
||||
|
||||
**Bad Decision**: Double down. "We just need better tooling. Once we get past this hump..."
|
||||
|
||||
**Problems**:
|
||||
- Professional reputation became more important than project success
|
||||
- Each day forward makes the return path longer
|
||||
- Team suffers while you protect ego
|
||||
|
||||
---
|
||||
|
||||
### Wading Through the Swamp
|
||||
|
||||
**Scenario**: Original design doesn't scale with new requirements. Refactoring back looks like 2 weeks. Pushing forward looks like 1 week.
|
||||
|
||||
**Bad Decision**: Push forward. "We're so close, we can clean it up later."
|
||||
|
||||
**Problems**:
|
||||
- Forward path is always deceptively shorter
|
||||
- "Later" never comes - next feature adds more mess
|
||||
- Team productivity enters death spiral
|
||||
- Moving forward through known swamp is worst priority inversion
|
||||
|
||||
---
|
||||
|
||||
### Coding Without Focus
|
||||
|
||||
**Scenario**: 3 PM, focus-manna depleted after morning meetings. Critical feature deadline tomorrow.
|
||||
|
||||
**Bad Decision**: Power through with coffee. "I'll just push harder."
|
||||
|
||||
**Problems**:
|
||||
- Code written without focus requires rewriting
|
||||
- Creates mess that slows future work
|
||||
- Jittery caffeine focus goes in wrong directions
|
||||
- Better to take 30-60 min break, return refreshed
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Declining with Professionalism
|
||||
|
||||
**Scenario**: Same cross-team architecture invite as above.
|
||||
|
||||
**Good Decision**: "Thanks for including me. My sprint commitments won't allow me to attend, but I'd appreciate the meeting notes. Let me know if there's a specific topic where my input is essential."
|
||||
|
||||
**Why it works**:
|
||||
- Protects your time for actual responsibilities
|
||||
- Leaves door open for truly necessary inclusion
|
||||
- Manager will support this decision
|
||||
|
||||
---
|
||||
|
||||
### Negotiating Meeting Exit
|
||||
|
||||
**Scenario**: Same hijacked API meeting.
|
||||
|
||||
**Good Decision**: At natural pause: "I need to get back to my sprint work. Is there anything else about the API design where you need my input before I go?"
|
||||
|
||||
**Why it works**:
|
||||
- Polite but clear
|
||||
- Offers final chance for relevant contribution
|
||||
- Models professional time management for others
|
||||
|
||||
---
|
||||
|
||||
### Facing the Hard Task
|
||||
|
||||
**Scenario**: Same scary auth refactor.
|
||||
|
||||
**Good Decision**: Block first two hours of day (highest focus-manna) for auth work. Write down fears specifically. Start with smallest piece.
|
||||
|
||||
**Why it works**:
|
||||
- Uses best focus time for hardest work
|
||||
- Naming fears reduces their power
|
||||
- Small start builds momentum
|
||||
- Priority order maintained despite discomfort
|
||||
|
||||
---
|
||||
|
||||
### Backing Out of Blind Alley
|
||||
|
||||
**Scenario**: Same microservices realization.
|
||||
|
||||
**Good Decision**: Call team meeting. "I advocated for microservices, but the data shows it's not working for our team size. I recommend we consolidate. Here's a migration plan."
|
||||
|
||||
**Why it works**:
|
||||
- Ego subordinated to project success
|
||||
- Demonstrates professional courage
|
||||
- Team respects honesty over stubbornness
|
||||
- Earlier exit = less rework
|
||||
|
||||
---
|
||||
|
||||
### Escaping the Swamp Early
|
||||
|
||||
**Scenario**: Same scaling problem.
|
||||
|
||||
**Good Decision**: "The 2-week refactor is actually cheaper than the 1-week push. Going forward will never be easier than now. Let's fix the design."
|
||||
|
||||
**Why it works**:
|
||||
- Recognizes the deception of "shorter path forward"
|
||||
- Understands inflection point
|
||||
- Prevents productivity death spiral
|
||||
- Shows fear of messes (appropriate professional fear)
|
||||
|
||||
## Pomodoro Technique in Practice
|
||||
|
||||
### Setup
|
||||
```
|
||||
25 minutes: Focused work (tomato time)
|
||||
5 minutes: Short break
|
||||
Repeat 4x
|
||||
30 minutes: Long break
|
||||
```
|
||||
|
||||
### Handling Interruptions
|
||||
|
||||
**During tomato**: "I'm in the middle of something - can I get back to you in 15 minutes?"
|
||||
|
||||
**What it achieves**:
|
||||
- Most interruptions can wait 25 minutes
|
||||
- Creates predictable availability windows
|
||||
- 12-14 tomatoes = excellent day
|
||||
- 2-3 tomatoes = day consumed by "stuff"
|
||||
- Tracking reveals time usage patterns
|
||||
|
||||
## Time Blocking Example
|
||||
|
||||
From the book - managing a 15-person department:
|
||||
|
||||
| Time | Activity |
|
||||
|------|----------|
|
||||
| 5:00 AM | Wake, bike to office |
|
||||
| 6:00-9:00 | Quiet focused work (fully scheduled) |
|
||||
| 9:00-12:00 | Scheduled work with 15-min gaps/hour for interruptions |
|
||||
| 12:00+ | Unscheduled - reactive mode for chaos |
|
||||
|
||||
**Why it works**:
|
||||
- Protects 3 hours before interruptions begin
|
||||
- Built-in slots for pushing interruptions into
|
||||
- Acknowledges reality of afternoon chaos
|
||||
- When chaos doesn't intrude, works on most important thing
|
||||
@@ -0,0 +1,105 @@
|
||||
# Time Management Knowledge
|
||||
|
||||
Core concepts and foundational understanding for developer time management.
|
||||
|
||||
## Overview
|
||||
|
||||
Professionals have only 480 minutes (8 hours) per day and must use them efficiently. Time management for developers involves protecting focused work time, minimizing meeting overhead, and maintaining the mental energy needed for programming.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Focus-Manna
|
||||
|
||||
**Definition**: A finite mental resource required for concentration and programming that depletes with use and must be recharged.
|
||||
|
||||
Programming requires extended periods of concentration. Focus-manna is expended during intellectual work and cannot be forced once depleted.
|
||||
|
||||
**Key points**:
|
||||
- You can *feel* when focus-manna is present or depleted
|
||||
- It decays if not used - wasting it in meetings means none left for coding
|
||||
- Worry and distractions consume focus-manna rapidly
|
||||
- Code written without focus-manna will likely need rewriting
|
||||
|
||||
### Meeting Cost
|
||||
|
||||
**Definition**: The true expense of meetings calculated as ~$200/hour per attendee (including salary, benefits, facilities).
|
||||
|
||||
Meetings have two truths: they are necessary AND they are huge time wasters. Often both apply to the same meeting - some attendees find value, others find it redundant.
|
||||
|
||||
**Key points**:
|
||||
- Calculate the actual cost of meetings you attend
|
||||
- Your presence costs your employer real money
|
||||
- Remaining in wasteful meetings is unprofessional
|
||||
|
||||
### Priority Inversion
|
||||
|
||||
**Definition**: Raising the priority of a less important task to avoid a task you find scary, uncomfortable, or boring.
|
||||
|
||||
This is a lie we tell ourselves - we know the avoided task is truly more important but we build defenses against the judgment of others.
|
||||
|
||||
**Key points**:
|
||||
- Often triggered by fear of confrontation or inescapable problems
|
||||
- Professionals execute tasks in true priority order regardless of personal discomfort
|
||||
|
||||
### Blind Alleys
|
||||
|
||||
**Definition**: Technical pathways that lead nowhere, often discovered only after significant investment.
|
||||
|
||||
The more vested you are in a decision, the longer you'll wander. Staking professional reputation leads to endless wandering.
|
||||
|
||||
**Key points**:
|
||||
- Unavoidable - even experience won't prevent all blind alleys
|
||||
- Key skill is recognizing them quickly and having courage to back out
|
||||
- "The Rule of Holes": When you are in one, stop digging
|
||||
|
||||
### Messes (Bogs/Swamps)
|
||||
|
||||
**Definition**: Technical debt that slows progress but doesn't stop it - you can always see a way forward that looks shorter than going back (but isn't).
|
||||
|
||||
Messes are worse than blind alleys because you can still make progress through brute force, deceiving yourself about the true cost.
|
||||
|
||||
**Key points**:
|
||||
- Nothing has more negative effect on team productivity than a mess
|
||||
- The progression is insidious - starts clean, then design choices don't scale
|
||||
- The inflection point is when going back will never be easier than now
|
||||
- Moving forward through a known swamp is the worst priority inversion
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| Focus-manna | Finite mental energy for concentration |
|
||||
| Pomodoro/Tomato | 25-minute focused work session |
|
||||
| Priority inversion | Avoiding important work by elevating lesser tasks |
|
||||
| Blind alley | Technical dead-end requiring backtracking |
|
||||
| Mess/Bog/Swamp | Technical debt that impedes but doesn't stop progress |
|
||||
|
||||
## Recharging Strategies
|
||||
|
||||
### Sleep
|
||||
Seven hours of sleep provides a full eight hours of focus-manna. Professionals manage sleep schedules to arrive at work with topped-up focus.
|
||||
|
||||
### Physical Activity (Muscle Focus)
|
||||
Physical disciplines (martial arts, yoga, cycling, carpentry) use a different kind of focus that helps recharge mental focus. Regular muscle focus *increases* capacity for mental focus.
|
||||
|
||||
### De-focusing Activities
|
||||
- Long walks
|
||||
- Conversations with friends
|
||||
- Looking out a window
|
||||
- Meditation or power naps
|
||||
- Podcasts or light reading
|
||||
|
||||
### Input for Output
|
||||
Creative work requires creative input. Reading fiction or exposure to others' creativity stimulates your own creative capacity.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| Focus-manna | Finite mental resource - protect and recharge it |
|
||||
| Meeting cost | ~$200/hour/person - calculate before attending |
|
||||
| Priority inversion | Lying to avoid scary work - fight it as honor |
|
||||
| Blind alleys | Recognize quickly, back out with courage |
|
||||
| Messes | Fear them more than blind alleys - escape early |
|
||||
| Sleep | 7 hours sleep = 8 hours focus capacity |
|
||||
| Muscle focus | Physical activity recharges mental focus |
|
||||
@@ -0,0 +1,134 @@
|
||||
# Time Management Rules
|
||||
|
||||
Specific guidelines for managing time, meetings, and focus as a professional developer.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Decline Meetings Without Clear Value
|
||||
|
||||
You do not have to attend every meeting. Going to too many meetings is unprofessional.
|
||||
|
||||
- Only accept if participation is immediately and significantly necessary to YOUR job
|
||||
- The inviter is not responsible for managing your time - only you can do that
|
||||
- Interest alone is not sufficient reason to attend
|
||||
- Your responsibility is to your projects first
|
||||
|
||||
**Ask before accepting**:
|
||||
- What discussions are on the table?
|
||||
- How much time is allotted for each topic?
|
||||
- What goal is to be achieved?
|
||||
- If no clear answers, politely decline
|
||||
|
||||
### 2. Leave Meetings That Waste Your Time
|
||||
|
||||
When the meeting gets boring, leave.
|
||||
|
||||
- Remaining in a wasteful meeting is unprofessional
|
||||
- You have an obligation to wisely spend your employer's time and money
|
||||
- Don't storm out - negotiate exit at an appropriate moment
|
||||
- Ask: "Is my presence still necessary?" or "Can we expedite the discussion?"
|
||||
|
||||
### 3. Require Agendas and Goals
|
||||
|
||||
Meetings must have clear structure to justify their cost.
|
||||
|
||||
- Clear agenda with times for each topic
|
||||
- Stated goal to achieve
|
||||
- If agenda is hijacked, request return to original topics
|
||||
- If agenda cannot be restored, leave when possible
|
||||
|
||||
### 4. Keep Stand-ups Under 10 Minutes
|
||||
|
||||
Three questions only, 20 seconds each maximum:
|
||||
|
||||
1. What did I do yesterday?
|
||||
2. What am I going to do today?
|
||||
3. What's in my way?
|
||||
|
||||
- Each person: ~1 minute maximum
|
||||
- 10 people: under 10 minutes total
|
||||
- Stand to discourage lingering
|
||||
|
||||
### 5. Limit Iteration Planning to 5% of Iteration Time
|
||||
|
||||
One-week iteration = 2 hours maximum for planning.
|
||||
|
||||
- Estimates should already be done for candidates
|
||||
- Business value assessment should already be done
|
||||
- 5-10 minutes maximum per backlog item
|
||||
- Longer discussions scheduled separately with subset of team
|
||||
|
||||
### 6. Schedule Retrospectives Before Quitting Time
|
||||
|
||||
Schedule 45 minutes before end of last day of iteration.
|
||||
|
||||
- 20 minutes for retrospective
|
||||
- 25 minutes for demo
|
||||
- Only a week or two of work - shouldn't need more time
|
||||
|
||||
### 7. Settle Arguments with Data, Not Force
|
||||
|
||||
> "Any argument that can't be settled in five minutes can't be settled by arguing." - Kent Beck
|
||||
|
||||
- Technical disagreements without data won't forge agreement
|
||||
- Force of character (yelling, condescension) doesn't settle anything long-term
|
||||
- If you agree, you MUST engage - passive-aggressive sabotage is the worst unprofessional behavior
|
||||
- Run experiments, simulations, or flip a coin and set criteria for abandonment
|
||||
|
||||
**For team disagreements**:
|
||||
- Each arguer presents case in 5 minutes or less
|
||||
- Team votes
|
||||
- Whole meeting under 15 minutes
|
||||
|
||||
### 8. Execute True Priorities Despite Discomfort
|
||||
|
||||
Professionals evaluate priority disregarding personal fears and desires.
|
||||
|
||||
- Don't elevate comfortable tasks over scary-but-important ones
|
||||
- Recognize when you're building defenses against others' judgment
|
||||
- Priority inversion is a lie to yourself and others
|
||||
|
||||
### 9. Recognize Blind Alleys and Back Out
|
||||
|
||||
Keep options open by keeping an open mind about alternatives.
|
||||
|
||||
- Never become so vested you can't abandon an idea
|
||||
- When in a hole, stop digging
|
||||
- Backing out is not failure - it's prudence
|
||||
|
||||
### 10. Fear Messes More Than Blind Alleys
|
||||
|
||||
The inflection point is when going back will never be easier than now.
|
||||
|
||||
- Always watch for messes growing without bound
|
||||
- Expend all necessary effort to escape early
|
||||
- Moving forward through a known swamp is the worst priority inversion
|
||||
- Clean messes as soon as they are recognized
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Use your manager to defend your meeting decline decisions
|
||||
- Block early morning hours for focused work before chaos begins
|
||||
- Schedule reactive time for afternoons when interruptions are highest
|
||||
- Use 15-minute scheduling increments with gaps for interruption handling
|
||||
- Protect your sleep - it's your primary focus-manna source
|
||||
- Take 30-60 minutes to de-focus when manna is depleted - forcing focus produces bad code
|
||||
|
||||
## Exceptions
|
||||
|
||||
- **Authority requests**: When someone senior requests attendance, weigh authority against work schedule with your team/manager
|
||||
- **Team helping team**: Sometimes worth the loss to your project to help another team - discuss with your team first
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Guideline |
|
||||
|------|-----------|
|
||||
| Meeting invites | Decline unless immediately necessary to your job |
|
||||
| Boring meetings | Leave politely at appropriate moment |
|
||||
| No agenda | Don't attend / request agenda first |
|
||||
| Stand-ups | 1 min/person, 10 min max total |
|
||||
| Iteration planning | 5% of iteration time |
|
||||
| Arguments > 5 min | Get data, don't keep arguing |
|
||||
| Uncomfortable tasks | Execute in true priority order |
|
||||
| Blind alleys | Recognize quickly, back out |
|
||||
| Messes | Fear them, escape early, never wade forward |
|
||||
@@ -0,0 +1,181 @@
|
||||
# Unit Tests Examples
|
||||
|
||||
Code examples demonstrating clean test principles in TypeScript (Jest/Vitest).
|
||||
|
||||
## Bad Examples
|
||||
|
||||
### Noisy Test with Too Much Detail
|
||||
|
||||
```typescript
|
||||
it('should get page hierarchy as XML', async () => {
|
||||
await crawler.addPage(root, PathParser.parse('PageOne'));
|
||||
await crawler.addPage(root, PathParser.parse('PageOne.ChildOne'));
|
||||
request.setResource('root');
|
||||
request.addInput('type', 'pages');
|
||||
const responder = new SerializedPageResponder();
|
||||
const response = await responder.makeResponse(new Context(root), request);
|
||||
expect(response.getContentType()).toBe('text/xml');
|
||||
expect(response.getContent()).toContain('<name>PageOne</name>');
|
||||
});
|
||||
```
|
||||
|
||||
**Problems**: PathParser calls are noise, setup details obscure intent, reader must understand implementation.
|
||||
|
||||
### Multiple Concepts in One Test
|
||||
|
||||
```typescript
|
||||
it('should add months correctly', () => {
|
||||
const d1 = SerialDate.createInstance(31, 5, 2004);
|
||||
const d2 = SerialDate.addMonths(1, d1);
|
||||
expect(d2.getDayOfMonth()).toBe(30); // Concept 1: cap to 30-day month
|
||||
const d3 = SerialDate.addMonths(2, d1);
|
||||
expect(d3.getDayOfMonth()).toBe(31); // Concept 2: preserve in 31-day month
|
||||
const d4 = SerialDate.addMonths(1, SerialDate.addMonths(1, d1));
|
||||
expect(d4.getDayOfMonth()).toBe(30); // Concept 3: chained addition
|
||||
});
|
||||
```
|
||||
|
||||
**Problems**: Three concepts in one test, hard to identify which failed, missing edge cases.
|
||||
|
||||
### Hard-to-Read State Assertions
|
||||
|
||||
```typescript
|
||||
it('should turn on low temp alarm at threshold', () => {
|
||||
hw.setTemp(WAY_TOO_COLD);
|
||||
controller.tic();
|
||||
expect(hw.heaterState()).toBe(true);
|
||||
expect(hw.blowerState()).toBe(true);
|
||||
expect(hw.coolerState()).toBe(false);
|
||||
expect(hw.hiTempAlarm()).toBe(false);
|
||||
expect(hw.loTempAlarm()).toBe(true);
|
||||
});
|
||||
```
|
||||
|
||||
**Problems**: Eyes bounce between state names and values, tedious to read.
|
||||
|
||||
## Good Examples
|
||||
|
||||
### Clean Test with Domain Language
|
||||
|
||||
```typescript
|
||||
it('should get page hierarchy as XML', async () => {
|
||||
await makePages('PageOne', 'PageOne.ChildOne', 'PageTwo');
|
||||
await submitRequest('root', 'type:pages');
|
||||
assertResponseIsXML();
|
||||
assertResponseContains('<name>PageOne</name>', '<name>PageTwo</name>');
|
||||
});
|
||||
|
||||
it('should exclude symbolic links from hierarchy', async () => {
|
||||
const page = await makePage('PageOne');
|
||||
await addLinkTo(page, 'PageTwo', 'SymPage');
|
||||
await submitRequest('root', 'type:pages');
|
||||
assertResponseDoesNotContain('SymPage');
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**: Clear BUILD-OPERATE-CHECK structure, details hidden in helpers.
|
||||
|
||||
### Compact State Representation
|
||||
|
||||
```typescript
|
||||
// State: Heater, Blower, Cooler, HiAlarm, LoAlarm (uppercase=ON)
|
||||
it('should turn on cooler and blower if too hot', () => {
|
||||
tooHot();
|
||||
expect(hw.getState()).toBe('hBChl');
|
||||
});
|
||||
|
||||
it('should turn on heater and blower if too cold', () => {
|
||||
tooCold();
|
||||
expect(hw.getState()).toBe('HBchl');
|
||||
});
|
||||
|
||||
it('should turn on lo-temp alarm at threshold', () => {
|
||||
wayTooCold();
|
||||
expect(hw.getState()).toBe('HBchL');
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**: Compact, consistent format; once learned, easy to scan.
|
||||
|
||||
### Single Concept per Test
|
||||
|
||||
```typescript
|
||||
describe('SerialDate.addMonths', () => {
|
||||
describe('given last day of 31-day month', () => {
|
||||
const may31 = SerialDate.createInstance(31, 5, 2004);
|
||||
|
||||
it('should cap to 30th when target month has 30 days', () => {
|
||||
const june = SerialDate.addMonths(1, may31);
|
||||
expect(june.getDayOfMonth()).toBe(30);
|
||||
});
|
||||
|
||||
it('should preserve 31st when target month has 31 days', () => {
|
||||
const july = SerialDate.addMonths(2, may31);
|
||||
expect(july.getDayOfMonth()).toBe(31);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Why it works**: One concept per test, easy to add edge cases, clear what failed.
|
||||
|
||||
## Refactoring Walkthrough
|
||||
|
||||
### Before
|
||||
|
||||
```typescript
|
||||
it('should handle page requests', async () => {
|
||||
await crawler.addPage(root, PathParser.parse('PageOne'));
|
||||
request.setResource('root');
|
||||
request.addInput('type', 'pages');
|
||||
const responder = new SerializedPageResponder();
|
||||
const response = await responder.makeResponse(ctx, request);
|
||||
expect(response.getContentType()).toBe('text/xml');
|
||||
expect(response.getContent()).toContain('<name>PageOne</name>');
|
||||
});
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```typescript
|
||||
it('should return XML containing requested pages', async () => {
|
||||
await makePages('PageOne');
|
||||
await submitRequest('root', 'type:pages');
|
||||
assertResponseIsXML();
|
||||
assertResponseContains('<name>PageOne</name>');
|
||||
});
|
||||
```
|
||||
|
||||
### Test Helpers (Domain-Specific Testing Language)
|
||||
|
||||
```typescript
|
||||
async function makePages(...names: string[]): Promise<void> {
|
||||
for (const name of names) {
|
||||
await crawler.addPage(root, PathParser.parse(name));
|
||||
}
|
||||
}
|
||||
|
||||
async function submitRequest(resource: string, params: string): Promise<void> {
|
||||
request.setResource(resource);
|
||||
const [key, value] = params.split(':');
|
||||
request.addInput(key, value);
|
||||
response = await new SerializedPageResponder().makeResponse(ctx, request);
|
||||
}
|
||||
|
||||
function assertResponseIsXML(): void {
|
||||
expect(response.getContentType()).toBe('text/xml');
|
||||
}
|
||||
|
||||
function assertResponseContains(...substrings: string[]): void {
|
||||
for (const s of substrings) {
|
||||
expect(response.getContent()).toContain(s);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. **Extracted helpers** - Hide implementation noise (PathParser, request setup)
|
||||
2. **Created assertion DSL** - Domain-specific language for responses
|
||||
3. **Improved test name** - Describes behavior, not implementation
|
||||
4. **Clear structure** - BUILD-OPERATE-CHECK pattern is visible
|
||||
@@ -0,0 +1,102 @@
|
||||
# Unit Tests Knowledge
|
||||
|
||||
Core concepts and foundational understanding for writing clean, maintainable unit tests.
|
||||
|
||||
## Overview
|
||||
|
||||
Unit tests are as important as production code. They enable fearless refactoring by providing a safety net that catches regressions. Clean tests must be readable, maintainable, and expressive to preserve their value over time.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Test-Driven Development (TDD)
|
||||
|
||||
**Definition**: A development practice where tests are written before production code, in short cycles of approximately 30 seconds.
|
||||
|
||||
Tests and production code are written together, with tests just seconds ahead of the code. This results in comprehensive test coverage that rivals the size of production code.
|
||||
|
||||
**Key points**:
|
||||
- Write failing test first, then minimal code to pass
|
||||
- Cycle time is approximately 30 seconds
|
||||
- Results in dozens of tests daily, thousands yearly
|
||||
|
||||
### Clean Tests
|
||||
|
||||
**Definition**: Tests that prioritize readability through clarity, simplicity, and density of expression.
|
||||
|
||||
A clean test says a lot with as few expressions as possible. It uses domain-specific language and hides implementation details behind well-named helper functions.
|
||||
|
||||
**Key points**:
|
||||
- Readability is paramount (even more than production code)
|
||||
- Use helper functions to hide noisy details
|
||||
- Follow BUILD-OPERATE-CHECK pattern
|
||||
|
||||
### The -ilities
|
||||
|
||||
**Definition**: The qualities that unit tests enable in production code: flexibility, maintainability, and reusability.
|
||||
|
||||
Without tests, every change is a possible bug. With tests, you can make changes with confidence and continuously improve architecture and design.
|
||||
|
||||
**Key points**:
|
||||
- Tests enable fearless refactoring
|
||||
- Higher coverage = less fear of change
|
||||
- Dirty tests lead to dirty code, then no tests, then rotting code
|
||||
|
||||
### Domain-Specific Testing Language (DSL)
|
||||
|
||||
**Definition**: A specialized API of functions and utilities built for writing and reading tests more easily.
|
||||
|
||||
This API evolves through refactoring test code, making tests more succinct and expressive while hiding implementation details.
|
||||
|
||||
**Key points**:
|
||||
- Not designed upfront; evolves through refactoring
|
||||
- Makes tests convenient to write and easy to read
|
||||
- Hides system APIs behind intention-revealing functions
|
||||
|
||||
### Dual Standard
|
||||
|
||||
**Definition**: The principle that test code has different engineering standards than production code regarding efficiency.
|
||||
|
||||
Test code must be simple, succinct, and expressive but need not be as efficient as production code since it runs in test environments, not production.
|
||||
|
||||
**Key points**:
|
||||
- Efficiency trade-offs acceptable in tests
|
||||
- Cleanliness is never compromised
|
||||
- Test environment has different constraints than production
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| BUILD-OPERATE-CHECK | Test pattern: setup data, perform action, verify results |
|
||||
| Given-When-Then | BDD convention for structuring test names and bodies |
|
||||
| Test Coverage | Percentage of production code exercised by tests |
|
||||
| Test Suite | Collection of all automated tests for a codebase |
|
||||
| Red-Green-Refactor | TDD cycle: failing test, passing code, clean up |
|
||||
|
||||
## How It Relates To
|
||||
|
||||
- **Refactoring**: Tests enable safe refactoring by catching regressions
|
||||
- **Code Quality**: Dirty tests lead to dirty production code over time
|
||||
- **Maintainability**: Clean tests preserve ability to change code
|
||||
- **Documentation**: Well-written tests serve as living documentation
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
- **Myth**: Test code can be "quick and dirty" since it's not production code
|
||||
**Reality**: Dirty tests become a liability and eventually get abandoned, leading to rotting production code
|
||||
|
||||
- **Myth**: Having any tests is better than no tests
|
||||
**Reality**: Dirty tests can be worse than no tests due to maintenance burden
|
||||
|
||||
- **Myth**: Tests only verify correctness
|
||||
**Reality**: Tests enable the -ilities (flexibility, maintainability, reusability)
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Concept | One-Line Summary |
|
||||
|---------|-----------------|
|
||||
| TDD | Write failing test, minimal code to pass, refactor |
|
||||
| Clean Tests | Readable, simple, expressive with domain language |
|
||||
| The -ilities | Tests enable flexibility, maintainability, reusability |
|
||||
| Dual Standard | Tests can trade efficiency for clarity, never cleanliness |
|
||||
| DSL | Build helper functions that make tests read like specs |
|
||||
@@ -0,0 +1,126 @@
|
||||
# Unit Tests Rules
|
||||
|
||||
Rules for writing clean, maintainable unit tests that enable confident refactoring.
|
||||
|
||||
## Core Rules
|
||||
|
||||
### 1. Three Laws of TDD
|
||||
|
||||
Follow these laws for test-driven development:
|
||||
|
||||
- **First Law**: Never write production code until you have a failing unit test
|
||||
- **Second Law**: Write only enough test to fail (not compiling counts as failing)
|
||||
- **Third Law**: Write only enough production code to pass the failing test
|
||||
|
||||
**Cycle time**: ~30 seconds per iteration
|
||||
|
||||
### 2. Keep Tests Clean
|
||||
|
||||
Test code is just as important as production code.
|
||||
|
||||
- Tests require thought, design, and care
|
||||
- Dirty tests become harder to change as production code evolves
|
||||
- Dirty tests eventually get abandoned, leading to rotting code
|
||||
- Maintain tests to the same quality standards as production code
|
||||
|
||||
### 3. Prioritize Readability
|
||||
|
||||
What makes tests readable: clarity, simplicity, density of expression.
|
||||
|
||||
- Say a lot with few expressions
|
||||
- Hide noisy details behind well-named helper functions
|
||||
- Use domain-specific testing language
|
||||
- Make test intent immediately clear
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// Bad - too much noise
|
||||
it('should get page hierarchy as XML', async () => {
|
||||
await crawler.addPage(root, PathParser.parse('PageOne'));
|
||||
await crawler.addPage(root, PathParser.parse('PageOne.ChildOne'));
|
||||
request.setResource('root');
|
||||
request.addInput('type', 'pages');
|
||||
const responder = new SerializedPageResponder();
|
||||
const response = await responder.makeResponse(new Context(root), request);
|
||||
expect(response.getContentType()).toBe('text/xml');
|
||||
expect(response.getContent()).toContain('<name>PageOne</name>');
|
||||
});
|
||||
|
||||
// Good - clear intent with helpers
|
||||
it('should get page hierarchy as XML', async () => {
|
||||
await makePages('PageOne', 'PageOne.ChildOne', 'PageTwo');
|
||||
await submitRequest('root', 'type:pages');
|
||||
assertResponseIsXML();
|
||||
assertResponseContains('<name>PageOne</name>', '<name>PageTwo</name>');
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Follow BUILD-OPERATE-CHECK Pattern
|
||||
|
||||
Structure each test in three clear parts:
|
||||
|
||||
- **Build**: Set up the test data
|
||||
- **Operate**: Perform the action being tested
|
||||
- **Check**: Verify the expected results
|
||||
|
||||
Also known as Arrange-Act-Assert (AAA) or Given-When-Then.
|
||||
|
||||
### 5. Single Concept per Test
|
||||
|
||||
Test one concept per test function.
|
||||
|
||||
- Don't test multiple unrelated things in one test
|
||||
- Each test should have a single reason to fail
|
||||
- Split tests that verify multiple independent behaviors
|
||||
- Helps identify what broke when a test fails
|
||||
|
||||
### 6. Minimize Asserts per Concept
|
||||
|
||||
Keep assertions focused on the concept being tested.
|
||||
|
||||
- One assert per test is a good guideline (not strict rule)
|
||||
- Multiple asserts are acceptable if testing same concept
|
||||
- Avoid unrelated assertions in same test
|
||||
- Create domain-specific assertion helpers
|
||||
|
||||
### 7. F.I.R.S.T. Principles
|
||||
|
||||
Clean tests follow these five principles:
|
||||
|
||||
| Principle | Description |
|
||||
|-----------|-------------|
|
||||
| **Fast** | Tests run quickly; slow tests won't be run frequently |
|
||||
| **Independent** | Tests don't depend on each other; run in any order |
|
||||
| **Repeatable** | Run in any environment without external dependencies |
|
||||
| **Self-Validating** | Boolean output - pass or fail, no manual checking |
|
||||
| **Timely** | Written just before the production code |
|
||||
|
||||
## Guidelines
|
||||
|
||||
Less strict recommendations:
|
||||
|
||||
- Build a domain-specific testing language over time
|
||||
- Refactor tests to be more expressive as patterns emerge
|
||||
- Efficiency trade-offs are acceptable in test code
|
||||
- Use Given-When-Then naming convention for clarity
|
||||
- Name tests to describe the behavior being verified
|
||||
|
||||
## Exceptions
|
||||
|
||||
When these rules may be relaxed:
|
||||
|
||||
- **Efficiency in tests**: Test code can be less efficient than production code (but never less clean)
|
||||
- **Multiple asserts**: Acceptable when testing a single logical concept
|
||||
- **Integration tests**: May require more setup and multiple verifications
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Rule | Summary |
|
||||
|------|---------|
|
||||
| TDD Laws | Failing test → minimal code → refactor |
|
||||
| Keep Clean | Test code = production code quality |
|
||||
| Readability | Clarity, simplicity, density of expression |
|
||||
| BUILD-OPERATE-CHECK | Setup → Action → Verify |
|
||||
| Single Concept | One behavior per test |
|
||||
| Minimize Asserts | Few assertions per concept |
|
||||
| F.I.R.S.T. | Fast, Independent, Repeatable, Self-Validating, Timely |
|
||||
303
.agents/skills/typescript-clean-code/workflows/bug-fix.md
Normal file
303
.agents/skills/typescript-clean-code/workflows/bug-fix.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# Bug Fix Workflow
|
||||
|
||||
Debugging and fixing bugs cleanly with test coverage.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Fixing reported bugs
|
||||
- Addressing production issues
|
||||
- Correcting unexpected behavior
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Bug report with reproduction steps
|
||||
- Access to the codebase
|
||||
- Test framework available
|
||||
|
||||
**Reference**: `tdd/rules.md`, `coding-practices/rules.md`, `refactoring.md`
|
||||
|
||||
---
|
||||
|
||||
## The Golden Rule
|
||||
|
||||
> **Write a failing test that reproduces the bug BEFORE fixing it.**
|
||||
|
||||
This ensures:
|
||||
1. You understand the bug
|
||||
2. You'll know when it's fixed
|
||||
3. It won't regress in the future
|
||||
|
||||
---
|
||||
|
||||
## Workflow Steps
|
||||
|
||||
### Step 1: Understand the Bug
|
||||
|
||||
**Goal**: Know exactly what's wrong before trying to fix it.
|
||||
|
||||
- [ ] Read the bug report completely
|
||||
- [ ] Understand expected vs. actual behavior
|
||||
- [ ] Identify reproduction steps
|
||||
- [ ] Determine severity and impact
|
||||
|
||||
**Gather Information**:
|
||||
```
|
||||
Bug: User export fails for users with special characters
|
||||
Expected: Export completes successfully
|
||||
Actual: Error "Invalid JSON" thrown
|
||||
Steps to reproduce:
|
||||
1. Create user with name "O'Brien"
|
||||
2. Call export endpoint
|
||||
3. Observe error
|
||||
```
|
||||
|
||||
**Ask**:
|
||||
- When did this start happening?
|
||||
- What changed recently?
|
||||
- Does it happen consistently or intermittently?
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Reproduce the Bug
|
||||
|
||||
**Goal**: Verify you can trigger the bug yourself.
|
||||
|
||||
- [ ] Follow the reproduction steps
|
||||
- [ ] Confirm you see the same behavior
|
||||
- [ ] Identify the minimal reproduction case
|
||||
- [ ] Document any additional findings
|
||||
|
||||
**If you can't reproduce**:
|
||||
- Ask for more details
|
||||
- Check environment differences
|
||||
- Look at logs/monitoring
|
||||
|
||||
**Minimal reproduction**:
|
||||
```typescript
|
||||
// Minimal case that triggers the bug
|
||||
const user = { name: "O'Brien", email: "obrien@test.com" };
|
||||
const result = exporter.export(user, 'json');
|
||||
// Throws: Error "Invalid JSON"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Write a Failing Test
|
||||
|
||||
**Goal**: Capture the bug as a failing test.
|
||||
|
||||
**Reference**: `tdd/rules.md`
|
||||
|
||||
```typescript
|
||||
describe('UserExporter', () => {
|
||||
it('should handle special characters in names', () => {
|
||||
// Arrange
|
||||
const user = { name: "O'Brien", email: "obrien@test.com" };
|
||||
|
||||
// Act
|
||||
const result = exporter.export(user, 'json');
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toContain("O'Brien");
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] Test reproduces the bug
|
||||
- [ ] Test fails (proving bug exists)
|
||||
- [ ] Test is specific to this bug
|
||||
- [ ] Test name describes the scenario
|
||||
|
||||
**Run the test**: It MUST fail.
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Locate the Bug
|
||||
|
||||
**Goal**: Find the root cause.
|
||||
|
||||
**Debugging Strategies**:
|
||||
|
||||
1. **Binary Search**: Comment out half the code, see if bug persists
|
||||
2. **Print Debugging**: Add logs at key points
|
||||
3. **Debugger**: Step through execution
|
||||
4. **Git Bisect**: Find the commit that introduced it
|
||||
|
||||
**Reference**: `coding-practices/rules.md` (debugging section)
|
||||
|
||||
**Questions to ask**:
|
||||
- Where does the data get corrupted?
|
||||
- What assumption is being violated?
|
||||
- What edge case wasn't handled?
|
||||
|
||||
**Example finding**:
|
||||
```typescript
|
||||
// Found the bug here:
|
||||
function formatJson(user: User): string {
|
||||
// BUG: Not escaping quotes in string values
|
||||
return `{"name":"${user.name}","email":"${user.email}"}`;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] Root cause identified
|
||||
- [ ] You understand WHY it's happening
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Fix the Bug
|
||||
|
||||
**Goal**: Make the test pass with minimal change.
|
||||
|
||||
**Write the simplest fix**:
|
||||
```typescript
|
||||
function formatJson(user: User): string {
|
||||
// FIX: Use JSON.stringify for proper escaping
|
||||
return JSON.stringify({ name: user.name, email: user.email });
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] Fix is minimal and focused
|
||||
- [ ] Fix addresses root cause (not symptoms)
|
||||
- [ ] No unrelated changes
|
||||
|
||||
**Run the test**: It MUST pass now.
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Check for Regressions
|
||||
|
||||
**Goal**: Ensure fix didn't break anything else.
|
||||
|
||||
```bash
|
||||
npm test # Run ALL tests
|
||||
```
|
||||
|
||||
- [ ] New test passes
|
||||
- [ ] All existing tests still pass
|
||||
- [ ] No regressions introduced
|
||||
|
||||
**If other tests fail**:
|
||||
- Understand why
|
||||
- Decide if those tests were correct
|
||||
- Fix or update as needed
|
||||
|
||||
---
|
||||
|
||||
### Step 7: Look for Similar Bugs
|
||||
|
||||
**Goal**: Find and fix related issues.
|
||||
|
||||
**Ask**:
|
||||
- Could this bug exist elsewhere?
|
||||
- Is this a pattern that's repeated?
|
||||
- Should we add more tests for similar cases?
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
// If formatJson had the bug, check formatCsv too
|
||||
it('should handle special characters in CSV export', () => {
|
||||
const user = { name: "O'Brien", email: "obrien@test.com" };
|
||||
const result = exporter.export(user, 'csv');
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] Checked for similar bugs
|
||||
- [ ] Added tests for related cases
|
||||
- [ ] Fixed any additional issues found
|
||||
|
||||
---
|
||||
|
||||
### Step 8: Refactor if Needed
|
||||
|
||||
**Goal**: Clean up while tests are green.
|
||||
|
||||
**Reference**: `refactoring.md` workflow
|
||||
|
||||
- [ ] Is the fix clean?
|
||||
- [ ] Could the code be clearer?
|
||||
- [ ] Is there duplication to remove?
|
||||
- [ ] All tests still pass after refactoring?
|
||||
|
||||
**Don't**:
|
||||
- Refactor unrelated code in the same commit
|
||||
- Make the fix commit harder to review
|
||||
|
||||
---
|
||||
|
||||
### Step 9: Document the Fix
|
||||
|
||||
**Goal**: Help future developers understand.
|
||||
|
||||
**Commit message**:
|
||||
```
|
||||
fix: handle special characters in user export
|
||||
|
||||
Users with apostrophes in their names (e.g., "O'Brien") caused
|
||||
JSON export to fail due to unescaped quotes.
|
||||
|
||||
Fixed by using JSON.stringify instead of string interpolation.
|
||||
|
||||
Fixes #123
|
||||
```
|
||||
|
||||
**Code comment (if needed)**:
|
||||
```typescript
|
||||
// Use JSON.stringify to properly escape special characters
|
||||
// See bug #123 for context
|
||||
```
|
||||
|
||||
- [ ] Commit message explains the bug and fix
|
||||
- [ ] References the bug ticket
|
||||
- [ ] Code comments added if fix isn't obvious
|
||||
|
||||
---
|
||||
|
||||
## Quick Checklist
|
||||
|
||||
```
|
||||
[ ] Bug understood and documented
|
||||
[ ] Bug reproduced locally
|
||||
[ ] Failing test written
|
||||
[ ] Root cause identified
|
||||
[ ] Fix implemented (minimal change)
|
||||
[ ] Original test passes
|
||||
[ ] All tests pass (no regressions)
|
||||
[ ] Similar bugs checked
|
||||
[ ] Refactored if needed
|
||||
[ ] Fix documented in commit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### When Stuck
|
||||
|
||||
- [ ] Take a break (15 min walk)
|
||||
- [ ] Explain the bug to someone (rubber duck)
|
||||
- [ ] Check recent changes (git log)
|
||||
- [ ] Look at similar working code
|
||||
- [ ] Simplify the reproduction case
|
||||
|
||||
### Red Flags
|
||||
|
||||
| Symptom | Possible Cause |
|
||||
|---------|---------------|
|
||||
| Works on my machine | Environment difference |
|
||||
| Intermittent failure | Race condition, timing |
|
||||
| Only in production | Config/data difference |
|
||||
| After deploy | Recent code change |
|
||||
|
||||
---
|
||||
|
||||
## Exit Criteria
|
||||
|
||||
Bug is fixed when:
|
||||
- [ ] Failing test written and passes
|
||||
- [ ] All tests pass
|
||||
- [ ] Root cause addressed (not just symptoms)
|
||||
- [ ] Similar issues checked
|
||||
- [ ] Fix committed with good message
|
||||
- [ ] Bug ticket updated/closed
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
name: 'step-01-init'
|
||||
description: 'Initialize code review workflow — set output path and detect continuation'
|
||||
nextStepFile: './step-02-function-quality.md'
|
||||
---
|
||||
|
||||
# Step 1: Initialize Code Review
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Set up the code review session: identify the target code, set the output path for the review report, and check for an existing report to resume.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### 1. Ask the User
|
||||
|
||||
Ask the user:
|
||||
- **What code to review?** (file path, module, or directory)
|
||||
- **Output path** for the review report (suggest a default: `./code-review-report-{{date}}.md`)
|
||||
- **Or provide path to an existing report** to resume a previous review
|
||||
|
||||
### 2. Check for Existing Report
|
||||
|
||||
If the user provides a path to an existing report file:
|
||||
- Read the file
|
||||
- Parse the YAML frontmatter
|
||||
- If `stepsCompleted` is non-empty → **STOP and load `step-01b-continue.md`**
|
||||
|
||||
### 3. Fresh Workflow Setup
|
||||
|
||||
If starting fresh:
|
||||
1. Copy the template from `templates/report-template.md`
|
||||
2. Fill in the frontmatter:
|
||||
- `targetCode`: the code path/scope provided by the user
|
||||
- `outputPath`: the chosen output path
|
||||
- `date`: current date
|
||||
3. Write the initialized report to the output path
|
||||
|
||||
### 4. Understand Context
|
||||
|
||||
**Goal**: Understand what the code is supposed to do before judging how it does it.
|
||||
|
||||
- Read the related ticket/story/requirement (if provided)
|
||||
- Understand the business purpose
|
||||
- Identify the scope of changes
|
||||
|
||||
**Ask**: "What problem is this code solving?"
|
||||
|
||||
### 5. Append Context to Report
|
||||
|
||||
Append to the output document:
|
||||
|
||||
```markdown
|
||||
## Step 1: Context
|
||||
|
||||
**Code Under Review**: {{targetCode}}
|
||||
**Purpose**: {{purpose described by user}}
|
||||
**Scope**: {{scope of the review}}
|
||||
```
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document frontmatter:
|
||||
- Add `1` to `stepsCompleted`
|
||||
|
||||
## PRESENT TO USER
|
||||
|
||||
Show the user:
|
||||
- Confirmation of the review target and output path
|
||||
- Context summary
|
||||
|
||||
Then ask: **[C] Continue to Step 2: Function Quality**
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-02-function-quality.md`.
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: 'step-01b-continue'
|
||||
description: 'Resume code review from last completed step'
|
||||
---
|
||||
|
||||
# Step 1b: Continue Previous Code Review
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Resume a previously started code review by reading the existing report, determining progress, and routing to the next incomplete step.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### 1. Read Existing Report
|
||||
|
||||
- Read the file at the output path provided by the user
|
||||
- Parse the YAML frontmatter
|
||||
- Extract `stepsCompleted` array
|
||||
|
||||
### 2. Show Progress Summary
|
||||
|
||||
Display to the user:
|
||||
|
||||
```
|
||||
Code Review Progress
|
||||
====================
|
||||
Target: {{targetCode}}
|
||||
Date Started: {{date}}
|
||||
Steps Completed: {{stepsCompleted}}
|
||||
|
||||
Step Map:
|
||||
[1] Initialize & Context {{done/pending}}
|
||||
[2] Function Quality {{done/pending}}
|
||||
[3] Naming {{done/pending}}
|
||||
[4] Class/Module Design {{done/pending}}
|
||||
[5] Error Handling {{done/pending}}
|
||||
[6] Tests {{done/pending}}
|
||||
[7] Comments {{done/pending}}
|
||||
[8] Smells {{done/pending}}
|
||||
[9] Feedback {{done/pending}}
|
||||
```
|
||||
|
||||
### 3. Offer Options
|
||||
|
||||
Present:
|
||||
- **[R] Resume** from the next incomplete step
|
||||
- **[O] Overview** — re-read the existing report content before resuming
|
||||
- **[X] Start over** — create a fresh report (confirm: this will overwrite)
|
||||
|
||||
### 4. Route to Next Step
|
||||
|
||||
On **[R]** or after **[O]**:
|
||||
|
||||
Determine the next step from `max(stepsCompleted) + 1` and load the corresponding file:
|
||||
|
||||
| Next Step | File |
|
||||
|-----------|------|
|
||||
| 2 | `step-02-function-quality.md` |
|
||||
| 3 | `step-03-naming.md` |
|
||||
| 4 | `step-04-class-design.md` |
|
||||
| 5 | `step-05-error-handling.md` |
|
||||
| 6 | `step-06-tests.md` |
|
||||
| 7 | `step-07-comments.md` |
|
||||
| 8 | `step-08-smells.md` |
|
||||
| 9 | `step-09-feedback.md` |
|
||||
|
||||
On **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
Load the step file determined above.
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
name: 'step-02-function-quality'
|
||||
description: 'Review function quality — size, SRP, arguments, side effects'
|
||||
nextStepFile: './step-03-naming.md'
|
||||
referenceFiles:
|
||||
- 'references/functions/rules.md'
|
||||
- 'references/functions/checklist.md'
|
||||
---
|
||||
|
||||
# Step 2: Check Function Quality
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Review every function in the target code for size, single responsibility, argument count, abstraction level consistency, and side effects.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/functions/rules.md` — function design rules
|
||||
- `references/functions/checklist.md` — function review checklist
|
||||
|
||||
Cite specific rules when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
For each function in the target code, verify:
|
||||
|
||||
1. **Size**: Is it small (5-20 lines)?
|
||||
2. **Single Responsibility**: Does it do ONE thing?
|
||||
3. **Abstraction Level**: Is it consistent throughout?
|
||||
4. **Arguments**: Are there 3 or fewer?
|
||||
5. **Side Effects**: Are there hidden side effects?
|
||||
6. **Name**: Does the name describe what it does?
|
||||
|
||||
### Red Flags
|
||||
|
||||
Watch for and report:
|
||||
- Function > 20 lines
|
||||
- More than 3 arguments
|
||||
- Boolean flag arguments
|
||||
- Output arguments
|
||||
- Mixed abstraction levels
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 2: Function Quality
|
||||
========================
|
||||
|
||||
[PASS/ISSUE] function_name (file:line)
|
||||
- Size: OK / TOO LARGE (N lines)
|
||||
- SRP: OK / MULTIPLE RESPONSIBILITIES
|
||||
- Arguments: OK / TOO MANY (N args)
|
||||
- Side Effects: NONE / FOUND: description
|
||||
Rule: functions/rules.md — Rule N
|
||||
|
||||
Summary: N functions reviewed, N issues found
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 3: Naming**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `2` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-03-naming.md`.
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
name: 'step-03-naming'
|
||||
description: 'Review naming conventions — intent, consistency, searchability'
|
||||
nextStepFile: './step-04-class-design.md'
|
||||
referenceFiles:
|
||||
- 'references/naming/rules.md'
|
||||
---
|
||||
|
||||
# Step 3: Check Naming
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Review all names (variables, functions, classes) in the target code for intent-revealing quality, consistency, and searchability.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/naming/rules.md` — naming rules and conventions
|
||||
|
||||
Cite specific rules when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
Check all names in the target code:
|
||||
|
||||
1. **Variables**: Do they reveal intent without comments?
|
||||
2. **Functions**: Are they verb phrases that describe the action?
|
||||
3. **Classes**: Are they noun phrases that describe responsibility?
|
||||
4. **No Encodings**: No Hungarian notation or prefixes?
|
||||
5. **Searchable**: Can you grep for important names?
|
||||
6. **Consistent**: Same concept = same word throughout?
|
||||
|
||||
### Red Flags
|
||||
|
||||
Watch for and report:
|
||||
- Single-letter variables (except loop counters)
|
||||
- Abbreviations that aren't universal
|
||||
- Names that require comments to explain
|
||||
- Different words for the same concept
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 3: Naming
|
||||
==============
|
||||
|
||||
[PASS/ISSUE] name (file:line)
|
||||
- Problem: description
|
||||
- Suggestion: better_name
|
||||
Rule: naming/rules.md — Rule N
|
||||
|
||||
Summary: N names reviewed, N issues found
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 4: Class/Module Design**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `3` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-04-class-design.md`.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
name: 'step-04-class-design'
|
||||
description: 'Review class/module design — SRP, cohesion, dependencies'
|
||||
nextStepFile: './step-05-error-handling.md'
|
||||
referenceFiles:
|
||||
- 'references/classes/rules.md'
|
||||
---
|
||||
|
||||
# Step 4: Check Class/Module Design
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Review classes and modules in the target code for single responsibility, cohesion, size, and proper dependency management.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/classes/rules.md` — class design rules
|
||||
|
||||
Cite specific rules when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
For each class/module in the target code, verify:
|
||||
|
||||
1. **Single Responsibility**: One reason to change?
|
||||
2. **Cohesion**: Methods use most instance variables?
|
||||
3. **Size**: Small and focused?
|
||||
4. **Dependencies**: Depends on abstractions, not concretions?
|
||||
|
||||
### Red Flags
|
||||
|
||||
Watch for and report:
|
||||
- God classes with many responsibilities
|
||||
- Low cohesion (methods don't use shared state)
|
||||
- Concrete dependencies that should be injected
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 4: Class/Module Design
|
||||
============================
|
||||
|
||||
[PASS/ISSUE] ClassName (file:line)
|
||||
- SRP: OK / MULTIPLE RESPONSIBILITIES: list
|
||||
- Cohesion: HIGH / LOW — reason
|
||||
- Dependencies: OK / CONCRETE: list
|
||||
Rule: classes/rules.md — Rule N
|
||||
|
||||
Summary: N classes reviewed, N issues found
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 5: Error Handling**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `4` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-05-error-handling.md`.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: 'step-05-error-handling'
|
||||
description: 'Review error handling — exceptions, null safety, context'
|
||||
nextStepFile: './step-06-tests.md'
|
||||
referenceFiles:
|
||||
- 'references/error-handling/rules.md'
|
||||
---
|
||||
|
||||
# Step 5: Check Error Handling
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Review error handling patterns in the target code for proper exception usage, null safety, and error context.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/error-handling/rules.md` — error handling rules
|
||||
|
||||
Cite specific rules when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
Check all error handling in the target code:
|
||||
|
||||
1. **Exceptions over codes**: Using exceptions, not error codes?
|
||||
2. **No null returns**: Returning empty collections or Optional instead?
|
||||
3. **No null passes**: Not passing null to functions?
|
||||
4. **Context**: Exceptions include enough context?
|
||||
5. **Normal flow**: Happy path is clear and uncluttered?
|
||||
|
||||
### Red Flags
|
||||
|
||||
Watch for and report:
|
||||
- Returning null
|
||||
- Swallowing exceptions silently
|
||||
- Error handling mixed with business logic
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 5: Error Handling
|
||||
======================
|
||||
|
||||
[PASS/ISSUE] location (file:line)
|
||||
- Problem: description
|
||||
- Pattern: null return / swallowed exception / mixed logic
|
||||
- Suggestion: fix description
|
||||
Rule: error-handling/rules.md — Rule N
|
||||
|
||||
Summary: N patterns reviewed, N issues found
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 6: Tests**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `5` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-06-tests.md`.
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
name: 'step-06-tests'
|
||||
description: 'Review test quality — coverage, readability, FIRST principles'
|
||||
nextStepFile: './step-07-comments.md'
|
||||
referenceFiles:
|
||||
- 'references/unit-tests/rules.md'
|
||||
---
|
||||
|
||||
# Step 6: Check Tests
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Review tests for the target code — coverage, readability, single concept per test, and F.I.R.S.T. principles.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/unit-tests/rules.md` — test quality rules
|
||||
|
||||
Cite specific rules when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
Check all tests related to the target code:
|
||||
|
||||
1. **Coverage**: Are the changes tested?
|
||||
2. **Readability**: Can you understand what's being tested?
|
||||
3. **Single Concept**: One concept per test?
|
||||
4. **F.I.R.S.T.**: Fast, Independent, Repeatable, Self-validating, Timely?
|
||||
5. **Naming**: Test names describe the scenario?
|
||||
|
||||
### Red Flags
|
||||
|
||||
Watch for and report:
|
||||
- No tests for new code
|
||||
- Tests that test multiple things
|
||||
- Tests that depend on each other
|
||||
- Slow tests
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 6: Tests
|
||||
=============
|
||||
|
||||
[PASS/ISSUE] test_name (file:line)
|
||||
- Coverage: OK / MISSING for: description
|
||||
- Readability: OK / UNCLEAR: reason
|
||||
- F.I.R.S.T.: OK / VIOLATION: which principle
|
||||
Rule: unit-tests/rules.md — Rule N
|
||||
|
||||
Summary: N tests reviewed, N issues found
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 7: Comments**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `6` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-07-comments.md`.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
name: 'step-07-comments'
|
||||
description: 'Review comments — necessity, accuracy, noise'
|
||||
nextStepFile: './step-08-smells.md'
|
||||
referenceFiles:
|
||||
- 'references/comments/rules.md'
|
||||
---
|
||||
|
||||
# Step 7: Check Comments
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Review all comments in the target code for necessity, accuracy, and noise.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/comments/rules.md` — comment rules
|
||||
|
||||
Cite specific rules when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
Check all comments in the target code:
|
||||
|
||||
1. **Necessary**: Could the code explain itself instead?
|
||||
2. **Accurate**: Do comments match the code?
|
||||
3. **No noise**: No redundant or obvious comments?
|
||||
4. **No commented-out code**: Old code removed, not commented?
|
||||
|
||||
### Red Flags
|
||||
|
||||
Watch for and report:
|
||||
- Comments explaining "what" instead of "why"
|
||||
- Commented-out code blocks
|
||||
- TODO comments that should be tickets
|
||||
- Outdated comments
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 7: Comments
|
||||
================
|
||||
|
||||
[PASS/ISSUE] comment (file:line)
|
||||
- Problem: unnecessary / inaccurate / noise / dead code
|
||||
- Suggestion: remove / rewrite / convert to ticket
|
||||
Rule: comments/rules.md — Rule N
|
||||
|
||||
Summary: N comments reviewed, N issues found
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 8: Code Smells**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `7` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-08-smells.md`.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: 'step-08-smells'
|
||||
description: 'Scan for code smells — duplication, feature envy, coupling'
|
||||
nextStepFile: './step-09-feedback.md'
|
||||
referenceFiles:
|
||||
- 'references/smells/rules.md'
|
||||
---
|
||||
|
||||
# Step 8: Check for Smells
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Scan the target code for common code smells using the smell catalog.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/smells/rules.md` — code smell catalog
|
||||
|
||||
Cite specific smell codes when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
Scan for common smells:
|
||||
|
||||
1. **G5 - Duplication**: Any copy-pasted code?
|
||||
2. **G14 - Feature Envy**: Methods using other class's data excessively?
|
||||
3. **G30 - Functions Do One Thing**: Any functions doing multiple things?
|
||||
4. **G31 - Hidden Temporal Coupling**: Hidden order dependencies?
|
||||
5. **C5 - Commented-Out Code**: Any dead code?
|
||||
|
||||
Also scan for any other smells from the catalog that apply to the target code.
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 8: Code Smells
|
||||
===================
|
||||
|
||||
[PASS/FOUND] smell_code - smell_name (file:line)
|
||||
- Evidence: description
|
||||
- Impact: why this is a problem
|
||||
- Suggestion: how to fix
|
||||
Rule: smells/rules.md — G5/G14/G30/etc.
|
||||
|
||||
Summary: N smells found
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 9: Feedback**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `8` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-09-feedback.md`.
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: 'step-09-feedback'
|
||||
description: 'Compile final feedback — prioritize, summarize, mark complete'
|
||||
referenceFiles:
|
||||
- 'references/collaboration/rules.md'
|
||||
---
|
||||
|
||||
# Step 9: Provide Feedback
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Compile all findings from steps 2-8 into a prioritized, actionable feedback summary. Mark the review as complete.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before compiling feedback, load and read:
|
||||
- `references/collaboration/rules.md` — feedback and communication rules
|
||||
|
||||
## COMPILATION PROCESS
|
||||
|
||||
### 1. Gather All Findings
|
||||
|
||||
Read through the output document and collect all issues found in steps 2-8.
|
||||
|
||||
### 2. Prioritize Issues
|
||||
|
||||
Categorize each issue:
|
||||
|
||||
| Priority | Category | Meaning |
|
||||
|----------|----------|---------|
|
||||
| 1 | `[BLOCKING]` | Must fix — correctness, security, major design flaw |
|
||||
| 2 | `[SUGGESTION]` | Should fix — improves quality significantly |
|
||||
| 3 | `[NIT]` | Nice to have — minor style or preference |
|
||||
|
||||
### 3. Format Feedback
|
||||
|
||||
For each issue, use this template:
|
||||
|
||||
```
|
||||
[BLOCKING/SUGGESTION/NIT] [File:Line]
|
||||
Issue: What's wrong
|
||||
Why: Reference to specific rule
|
||||
Suggestion: How to fix
|
||||
```
|
||||
|
||||
### 4. Write Summary
|
||||
|
||||
Append to the output document:
|
||||
|
||||
```markdown
|
||||
## Final Review Summary
|
||||
|
||||
### Blocking Issues (Must Fix)
|
||||
- [list or "None found"]
|
||||
|
||||
### Suggestions (Should Fix)
|
||||
- [list or "None"]
|
||||
|
||||
### Nits (Nice to Have)
|
||||
- [list or "None"]
|
||||
|
||||
### Quick Checklist
|
||||
- [ ] Functions: small, single purpose, few arguments
|
||||
- [ ] Names: reveal intent, consistent
|
||||
- [ ] Classes: SRP, cohesive
|
||||
- [ ] Errors: exceptions, no null, context
|
||||
- [ ] Tests: exist, readable, F.I.R.S.T.
|
||||
- [ ] Comments: necessary, accurate
|
||||
- [ ] Smells: none detected
|
||||
|
||||
### Verdict
|
||||
[Overall assessment — approve, needs work, or major concerns]
|
||||
```
|
||||
|
||||
## PRESENT TO USER
|
||||
|
||||
Show the final summary to the user. Highlight blocking issues first.
|
||||
|
||||
Feedback principles (from `collaboration/rules.md`):
|
||||
1. **Be specific**: Point to exact lines/functions
|
||||
2. **Explain why**: Reference principles, not just preferences
|
||||
3. **Suggest alternatives**: Don't just criticize, propose solutions
|
||||
4. **Prioritize**: Distinguish blocking issues from nice-to-haves
|
||||
5. **Be respectful**: Critique code, not the person
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document frontmatter:
|
||||
- Add `9` to `stepsCompleted`
|
||||
- Set `status` to `'complete'`
|
||||
|
||||
## WORKFLOW COMPLETE
|
||||
|
||||
The code review workflow is complete. The full report is saved at the output path.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
stepsCompleted: []
|
||||
inputDocuments: []
|
||||
workflowType: 'code-review'
|
||||
targetCode: ''
|
||||
outputPath: ''
|
||||
date: ''
|
||||
status: 'in-progress'
|
||||
---
|
||||
|
||||
# Code Review Report
|
||||
|
||||
**Date**: {{date}}
|
||||
**Target**: {{targetCode}}
|
||||
**Status**: {{status}}
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
name: 'code-review'
|
||||
description: 'Step-by-step process for reviewing code quality'
|
||||
firstStepFile: './steps/step-01-init.md'
|
||||
templateFile: './templates/report-template.md'
|
||||
---
|
||||
|
||||
# Code Review Workflow
|
||||
|
||||
Step-by-step process for reviewing code quality.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Reviewing your own code before committing
|
||||
- Reviewing a colleague's code
|
||||
- Performing a quality check on a module
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Have the code to review accessible and understand its purpose.
|
||||
|
||||
## Step-File Architecture
|
||||
|
||||
This workflow uses a **step-file architecture** for context-safe execution:
|
||||
|
||||
- Each step is a separate file loaded sequentially
|
||||
- Progress is tracked via `stepsCompleted` in the output document's YAML frontmatter
|
||||
- If context is compacted mid-workflow, step-01 detects existing output and resumes from the last completed step via step-01b
|
||||
|
||||
### Steps
|
||||
|
||||
| Step | File | Description |
|
||||
|------|------|-------------|
|
||||
| 1 | `step-01-init.md` | Initialize workflow, set output path, detect continuation |
|
||||
| 1b | `step-01b-continue.md` | Resume from last completed step |
|
||||
| 2 | `step-02-function-quality.md` | Check function quality |
|
||||
| 3 | `step-03-naming.md` | Check naming conventions |
|
||||
| 4 | `step-04-class-design.md` | Check class/module design |
|
||||
| 5 | `step-05-error-handling.md` | Check error handling |
|
||||
| 6 | `step-06-tests.md` | Check test quality |
|
||||
| 7 | `step-07-comments.md` | Check comments |
|
||||
| 8 | `step-08-smells.md` | Check for code smells |
|
||||
| 9 | `step-09-feedback.md` | Provide feedback, mark complete |
|
||||
|
||||
### Rules
|
||||
|
||||
1. **Load one step at a time** - Read the step file, execute it, then load the next
|
||||
2. **Update frontmatter after each step** - Add the step number to `stepsCompleted`
|
||||
3. **Wait for user confirmation** - Present findings and wait for `[C]` before proceeding
|
||||
4. **Load reference files** - Each step specifies which reference files to load before analysis
|
||||
5. **Cite specific rules** - When reporting findings, cite the specific rule from the reference file
|
||||
|
||||
## Begin
|
||||
|
||||
Load `steps/step-01-init.md` to start.
|
||||
@@ -0,0 +1,276 @@
|
||||
# Deadline Negotiation Workflow
|
||||
|
||||
Handling unrealistic deadline requests professionally.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Asked to commit to an impossible deadline
|
||||
- Deadline is set without your input
|
||||
- Scope exceeds available time
|
||||
- Pressure to "just make it work"
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Understanding of the work required
|
||||
- Estimate of realistic timeline (see `estimation.md`)
|
||||
- Knowledge of your capacity
|
||||
|
||||
**Reference**: `saying-no/rules.md`, `commitment/rules.md`, `estimation/rules.md`, `pressure/rules.md`
|
||||
|
||||
---
|
||||
|
||||
## The Golden Rules
|
||||
|
||||
1. **Never say "I'll try"** - It's dishonest and sets you up to fail
|
||||
2. **Provide data, not emotions** - Use estimates and facts
|
||||
3. **Offer alternatives** - Don't just say no, propose solutions
|
||||
4. **Escalate professionally** - Not as a threat, but as information
|
||||
|
||||
---
|
||||
|
||||
## Workflow Steps
|
||||
|
||||
### Step 1: Gather Information
|
||||
|
||||
**Goal**: Understand what's being asked before responding.
|
||||
|
||||
- [ ] What exactly is the deadline?
|
||||
- [ ] What is the full scope of work?
|
||||
- [ ] Why is this deadline set?
|
||||
- [ ] What are the consequences of missing it?
|
||||
- [ ] Who set the deadline?
|
||||
|
||||
**Ask clarifying questions**:
|
||||
```
|
||||
"Help me understand - what's driving the March 1st date?"
|
||||
"What's included in 'complete'? Does that include testing and documentation?"
|
||||
"What happens if we deliver on March 15th instead?"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Do Your Estimation
|
||||
|
||||
**Goal**: Know the realistic timeline before negotiating.
|
||||
|
||||
**Reference**: `estimation.md` workflow
|
||||
|
||||
- [ ] Break down the work into tasks
|
||||
- [ ] Apply PERT estimation
|
||||
- [ ] Calculate expected time and uncertainty
|
||||
- [ ] Document assumptions
|
||||
|
||||
**Example**:
|
||||
```
|
||||
Feature: User Export
|
||||
|
||||
Estimated: 25-32 hours (95% confidence)
|
||||
Calendar time: 4-5 days (accounting for meetings, etc.)
|
||||
Realistic delivery: 1.5 weeks from start
|
||||
|
||||
Requested deadline: End of this week (3 days)
|
||||
Gap: 2-4 days short
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Identify Options
|
||||
|
||||
**Goal**: Prepare alternatives before the conversation.
|
||||
|
||||
**Option types**:
|
||||
|
||||
1. **Reduce scope**: What can be cut or deferred?
|
||||
2. **Extend deadline**: What's the realistic date?
|
||||
3. **Add resources**: Can anyone help? (Be realistic about ramp-up)
|
||||
4. **Change approach**: Is there a simpler solution?
|
||||
|
||||
**Example options**:
|
||||
```
|
||||
Option A: Reduce Scope
|
||||
- Deliver JSON export only (cut CSV)
|
||||
- Skip documentation
|
||||
- Delivery: End of week (meets deadline)
|
||||
- Trade-off: CSV comes in week 2
|
||||
|
||||
Option B: Extend Deadline
|
||||
- Full feature with JSON + CSV
|
||||
- Complete documentation
|
||||
- Delivery: End of next week
|
||||
- Trade-off: 1 week later
|
||||
|
||||
Option C: Partial Delivery
|
||||
- JSON export by end of week
|
||||
- CSV export by mid next week
|
||||
- Trade-off: Two releases needed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Have the Conversation
|
||||
|
||||
**Goal**: Communicate clearly and professionally.
|
||||
|
||||
**Structure**:
|
||||
1. Acknowledge the request
|
||||
2. Share your analysis
|
||||
3. Present the gap
|
||||
4. Offer alternatives
|
||||
5. Ask for decision
|
||||
|
||||
**Example conversation**:
|
||||
|
||||
**Bad approach** (don't do this):
|
||||
```
|
||||
"There's no way I can do this by Friday. It's impossible."
|
||||
```
|
||||
|
||||
**Good approach**:
|
||||
```
|
||||
"I understand the deadline is Friday. I've broken down the work,
|
||||
and with testing and documentation, I estimate 4-5 days of work.
|
||||
|
||||
Given we're starting today (Tuesday), that puts realistic
|
||||
completion at next Tuesday, not Friday.
|
||||
|
||||
I see three options:
|
||||
1. We can deliver JSON export only by Friday, and add CSV next week
|
||||
2. We can extend the deadline to next Tuesday for the full feature
|
||||
3. We can discuss if there's scope I'm not understanding correctly
|
||||
|
||||
Which option works best for the business?"
|
||||
```
|
||||
|
||||
**Key phrases**:
|
||||
- "Based on my analysis..."
|
||||
- "Here are the options I see..."
|
||||
- "What would you like to prioritize?"
|
||||
- "I want to give you a commitment I can keep"
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Handle Pushback
|
||||
|
||||
**Goal**: Stay professional when pressured.
|
||||
|
||||
**Common pushback and responses**:
|
||||
|
||||
**"Can't you just try?"**
|
||||
```
|
||||
Reference: commitment/rules.md - The "Try" problem
|
||||
|
||||
Response: "I want to be honest with you. If I say I'll try, I'm really
|
||||
saying I don't think I can do it but I don't want to say no.
|
||||
That's not fair to either of us. Here's what I CAN commit to..."
|
||||
```
|
||||
|
||||
**"We promised the client"**
|
||||
```
|
||||
Reference: saying-no/rules.md - High stakes
|
||||
|
||||
Response: "I understand there's a commitment to the client. That makes
|
||||
it even more important that we're realistic. Would you rather tell
|
||||
them now about a 3-day delay, or on Friday that we missed the deadline?
|
||||
Let's figure out the best path forward together."
|
||||
```
|
||||
|
||||
**"Everyone else manages to deliver on time"**
|
||||
```
|
||||
Response: "I want to deliver on time too. Can we look at the scope
|
||||
together? Maybe I'm including work that isn't needed, or maybe
|
||||
there's something I can learn from how others approach this."
|
||||
```
|
||||
|
||||
**"This is critical for the business"**
|
||||
```
|
||||
Response: "I understand this is important. That's exactly why I want
|
||||
to give you accurate information. Committing to Friday and delivering
|
||||
broken code or missing on Tuesday would hurt the business more than
|
||||
being clear now about realistic timing."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Document the Agreement
|
||||
|
||||
**Goal**: Have a clear record of what was agreed.
|
||||
|
||||
After the conversation:
|
||||
- [ ] Send a follow-up email/message summarizing:
|
||||
- What was agreed
|
||||
- What's in scope / out of scope
|
||||
- The delivery date
|
||||
- Any assumptions or dependencies
|
||||
|
||||
**Example follow-up**:
|
||||
```
|
||||
Hi [Manager],
|
||||
|
||||
Thanks for the discussion. To confirm our agreement:
|
||||
|
||||
Scope: User export feature - JSON format only
|
||||
Deadline: Friday, March 1st
|
||||
Out of scope: CSV format (scheduled for March 8th)
|
||||
|
||||
Assumptions:
|
||||
- No other high-priority work assigned this week
|
||||
- API contract is finalized
|
||||
- Test environment is available
|
||||
|
||||
Let me know if I've missed anything.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 7: Handle Escalation (if needed)
|
||||
|
||||
**Goal**: Escalate professionally if pushback continues.
|
||||
|
||||
**When to escalate**:
|
||||
- You're being asked to commit to something impossible
|
||||
- Your concerns are being dismissed
|
||||
- The risk to the project/company is significant
|
||||
|
||||
**How to escalate**:
|
||||
```
|
||||
"I want to make sure we're making the right decision here. I'm
|
||||
not comfortable committing to Friday because [specific reasons].
|
||||
|
||||
I think we should involve [manager/stakeholder] to make sure we're
|
||||
aligned on the trade-offs. Would you like me to set up that meeting?"
|
||||
```
|
||||
|
||||
**Reference**: `saying-no/rules.md` - "Threaten professionally"
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### What NOT to Say
|
||||
|
||||
| Don't Say | Why | Say Instead |
|
||||
|-----------|-----|-------------|
|
||||
| "I'll try" | Dishonest, sets up failure | "I can commit to X by Y" |
|
||||
| "That's impossible" | Sounds defensive | "Based on my analysis, that would require..." |
|
||||
| "You don't understand" | Confrontational | "Help me understand the constraints..." |
|
||||
| "Fine, I'll do it" | Resentful commitment | "Here are the options..." |
|
||||
|
||||
### What TO Say
|
||||
|
||||
| Situation | Response |
|
||||
|-----------|----------|
|
||||
| Unrealistic deadline | "Based on my estimate, realistic delivery is [date]. Here are options..." |
|
||||
| Pressure to commit | "I want to give you a commitment I can keep. Can we discuss scope?" |
|
||||
| Scope creep | "If we add X, the deadline moves to Y. Which do you prefer?" |
|
||||
| Being dismissed | "I understand the pressure. Let's involve [stakeholder] to decide." |
|
||||
|
||||
---
|
||||
|
||||
## Exit Criteria
|
||||
|
||||
Negotiation is complete when:
|
||||
- [ ] Clear agreement on scope and deadline
|
||||
- [ ] Agreement documented in writing
|
||||
- [ ] You can honestly commit to the deadline
|
||||
- [ ] Trade-offs are understood by all parties
|
||||
- [ ] Escalation path is clear if things change
|
||||
264
.agents/skills/typescript-clean-code/workflows/estimation.md
Normal file
264
.agents/skills/typescript-clean-code/workflows/estimation.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# Estimation Workflow
|
||||
|
||||
PERT-based task estimation process for accurate estimates.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Sprint planning
|
||||
- Project scoping
|
||||
- When asked "how long will this take?"
|
||||
- Committing to deadlines
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Understanding of the task/feature
|
||||
- Knowledge of the codebase
|
||||
- Ability to break down work
|
||||
|
||||
**Reference**: `estimation/rules.md`, `estimation/examples.md`, `commitment/rules.md`
|
||||
|
||||
---
|
||||
|
||||
## The Golden Rules
|
||||
|
||||
1. **Estimates are NOT commitments** - They're probability distributions
|
||||
2. **Never give a single number** - Always provide a range
|
||||
3. **Break it down** - Large tasks have large uncertainty
|
||||
4. **Communicate uncertainty** - Be explicit about what you don't know
|
||||
|
||||
---
|
||||
|
||||
## Workflow Steps
|
||||
|
||||
### Step 1: Clarify the Request
|
||||
|
||||
**Goal**: Understand exactly what's being asked.
|
||||
|
||||
- [ ] What is the scope of the work?
|
||||
- [ ] What are the acceptance criteria?
|
||||
- [ ] What's NOT included?
|
||||
- [ ] Are there dependencies on others?
|
||||
|
||||
**Ask**:
|
||||
- "Can you clarify what 'done' means for this?"
|
||||
- "Does this include testing/documentation/deployment?"
|
||||
- "Are there any constraints I should know about?"
|
||||
|
||||
**Reference**: `acceptance-testing/knowledge.md`
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Break Down the Task
|
||||
|
||||
**Goal**: Decompose into estimable pieces.
|
||||
|
||||
Break the task into subtasks that are:
|
||||
- Independent (can be done separately)
|
||||
- Small (< 1 day ideal, < 3 days max)
|
||||
- Clear (you know what "done" means)
|
||||
|
||||
**Example**:
|
||||
```
|
||||
Feature: User Export
|
||||
|
||||
Subtasks:
|
||||
1. Create UserExporter interface
|
||||
2. Implement JSON formatter
|
||||
3. Implement CSV formatter
|
||||
4. Add error handling
|
||||
5. Write unit tests
|
||||
6. Write integration tests
|
||||
7. Update API documentation
|
||||
```
|
||||
|
||||
- [ ] Each subtask is small enough to estimate
|
||||
- [ ] No subtask > 3 days
|
||||
- [ ] Dependencies identified
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Apply PERT to Each Subtask
|
||||
|
||||
**Goal**: Get trivariate estimates for each subtask.
|
||||
|
||||
For each subtask, estimate three values:
|
||||
|
||||
| Value | Symbol | Meaning |
|
||||
|-------|--------|---------|
|
||||
| Optimistic | O | Best case (everything goes right) |
|
||||
| Nominal | N | Most likely (normal conditions) |
|
||||
| Pessimistic | P | Worst case (things go wrong) |
|
||||
|
||||
**Example**:
|
||||
```
|
||||
Subtask: Implement JSON formatter
|
||||
- Optimistic (O): 2 hours (straightforward, no surprises)
|
||||
- Nominal (N): 4 hours (normal development)
|
||||
- Pessimistic (P): 12 hours (unexpected complexity, bugs)
|
||||
```
|
||||
|
||||
**Tips for each value**:
|
||||
- **Optimistic**: "If everything goes perfectly"
|
||||
- **Nominal**: "Most likely scenario"
|
||||
- **Pessimistic**: "If I hit problems but still finish"
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Calculate PERT Values
|
||||
|
||||
**Goal**: Compute expected time and uncertainty.
|
||||
|
||||
**PERT Formulas**:
|
||||
```
|
||||
Expected Time (μ) = (O + 4N + P) / 6
|
||||
Standard Deviation (σ) = (P - O) / 6
|
||||
```
|
||||
|
||||
**Example Calculation**:
|
||||
```
|
||||
O = 2, N = 4, P = 12
|
||||
|
||||
μ = (2 + 4×4 + 12) / 6 = (2 + 16 + 12) / 6 = 30/6 = 5 hours
|
||||
σ = (12 - 2) / 6 = 10/6 = 1.67 hours
|
||||
```
|
||||
|
||||
**Calculate for each subtask**:
|
||||
```
|
||||
| Subtask | O | N | P | μ | σ |
|
||||
|---------------------|----|----|-----|-------|------|
|
||||
| UserExporter interface| 1 | 2 | 4 | 2.2 | 0.5 |
|
||||
| JSON formatter | 2 | 4 | 12 | 5.0 | 1.7 |
|
||||
| CSV formatter | 2 | 4 | 10 | 4.7 | 1.3 |
|
||||
| Error handling | 1 | 2 | 6 | 2.5 | 0.8 |
|
||||
| Unit tests | 2 | 4 | 8 | 4.3 | 1.0 |
|
||||
| Integration tests | 2 | 4 | 10 | 4.7 | 1.3 |
|
||||
| Documentation | 1 | 2 | 4 | 2.2 | 0.5 |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Sum and Calculate Total
|
||||
|
||||
**Goal**: Combine subtask estimates.
|
||||
|
||||
**Total Expected Time**:
|
||||
```
|
||||
μ_total = Σ μ_i (sum of all expected times)
|
||||
```
|
||||
|
||||
**Total Standard Deviation**:
|
||||
```
|
||||
σ_total = √(Σ σ_i²) (square root of sum of squares)
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```
|
||||
μ_total = 2.2 + 5.0 + 4.7 + 2.5 + 4.3 + 4.7 + 2.2 = 25.6 hours
|
||||
|
||||
σ_total = √(0.5² + 1.7² + 1.3² + 0.8² + 1.0² + 1.3² + 0.5²)
|
||||
= √(0.25 + 2.89 + 1.69 + 0.64 + 1.0 + 1.69 + 0.25)
|
||||
= √8.41
|
||||
= 2.9 hours
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Communicate the Estimate
|
||||
|
||||
**Goal**: Present estimate with appropriate uncertainty.
|
||||
|
||||
**Never say**: "It will take 26 hours"
|
||||
|
||||
**Instead say**:
|
||||
```
|
||||
"I estimate this will take about 26 hours, give or take 3 hours.
|
||||
There's about a 95% chance it will be done within 32 hours."
|
||||
```
|
||||
|
||||
**Confidence Intervals**:
|
||||
| Confidence | Formula | Example |
|
||||
|------------|---------|---------|
|
||||
| 68% | μ ± 1σ | 23-29 hours |
|
||||
| 95% | μ ± 2σ | 20-32 hours |
|
||||
| 99.7% | μ ± 3σ | 17-35 hours |
|
||||
|
||||
**Communicate**:
|
||||
- [ ] Expected time (μ)
|
||||
- [ ] Range (μ ± σ or μ ± 2σ)
|
||||
- [ ] Key assumptions
|
||||
- [ ] What could change the estimate
|
||||
|
||||
---
|
||||
|
||||
### Step 7: Document Assumptions
|
||||
|
||||
**Goal**: Make uncertainty explicit.
|
||||
|
||||
List what you assumed:
|
||||
```
|
||||
Assumptions:
|
||||
- API contract is already defined
|
||||
- No major refactoring needed
|
||||
- Test environment is available
|
||||
- No dependencies on other teams
|
||||
|
||||
Risks that could increase time:
|
||||
- If we need to refactor the data layer: +8-16 hours
|
||||
- If API contract changes: +4-8 hours
|
||||
- If test environment issues: +2-4 hours
|
||||
```
|
||||
|
||||
- [ ] Assumptions documented
|
||||
- [ ] Risks identified
|
||||
- [ ] Impact of risks quantified
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ PERT ESTIMATION │
|
||||
│ │
|
||||
│ For each task, estimate: │
|
||||
│ O = Optimistic (best case) │
|
||||
│ N = Nominal (most likely) │
|
||||
│ P = Pessimistic (worst case) │
|
||||
│ │
|
||||
│ Calculate: │
|
||||
│ Expected: μ = (O + 4N + P) / 6 │
|
||||
│ Std Dev: σ = (P - O) / 6 │
|
||||
│ │
|
||||
│ For multiple tasks: │
|
||||
│ μ_total = sum of all μ │
|
||||
│ σ_total = √(sum of all σ²) │
|
||||
│ │
|
||||
│ Report as range: │
|
||||
│ "About X hours, give or take Y hours" │
|
||||
│ or "Between X and Z hours (95% confidence)" │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
| Mistake | Why It's Bad | Do Instead |
|
||||
|---------|--------------|------------|
|
||||
| Single number | Creates false precision | Give a range |
|
||||
| Optimistic only | You'll always be "late" | Include pessimistic |
|
||||
| Padding | Dishonest, still often wrong | Use PERT honestly |
|
||||
| Commitment language | "I'll have it done by..." | "I estimate..." |
|
||||
| Skipping breakdown | Large tasks = large error | Break it down |
|
||||
|
||||
---
|
||||
|
||||
## Exit Criteria
|
||||
|
||||
Estimation is complete when:
|
||||
- [ ] Task broken into subtasks (< 3 days each)
|
||||
- [ ] Each subtask has O, N, P values
|
||||
- [ ] Total μ and σ calculated
|
||||
- [ ] Estimate communicated as a range
|
||||
- [ ] Assumptions documented
|
||||
- [ ] Risks identified
|
||||
269
.agents/skills/typescript-clean-code/workflows/new-feature.md
Normal file
269
.agents/skills/typescript-clean-code/workflows/new-feature.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# New Feature Workflow
|
||||
|
||||
Writing new code following clean code principles from the start.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Implementing a new feature or user story
|
||||
- Adding new functionality to an existing system
|
||||
- Creating a new module or service
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Clear understanding of requirements
|
||||
- Access to the codebase
|
||||
- Test framework set up
|
||||
|
||||
**Reference**: `functions/rules.md`, `naming/rules.md`, `classes/rules.md`, `tdd/rules.md`
|
||||
|
||||
---
|
||||
|
||||
## Workflow Steps
|
||||
|
||||
### Step 1: Understand Requirements
|
||||
|
||||
**Goal**: Know exactly what you're building before writing code.
|
||||
|
||||
- [ ] Read the ticket/story/requirement completely
|
||||
- [ ] Identify acceptance criteria
|
||||
- [ ] Clarify any ambiguities with stakeholders
|
||||
- [ ] Understand the "why" behind the feature
|
||||
|
||||
**Ask**:
|
||||
- What problem does this solve?
|
||||
- What are the edge cases?
|
||||
- What should happen on errors?
|
||||
- How will this be tested?
|
||||
|
||||
**Reference**: `acceptance-testing/knowledge.md`
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Plan the Approach
|
||||
|
||||
**Goal**: Design before coding.
|
||||
|
||||
- [ ] Identify the components/classes needed
|
||||
- [ ] Define the interfaces/contracts
|
||||
- [ ] Consider dependencies
|
||||
- [ ] Plan for testability
|
||||
|
||||
**Design Questions**:
|
||||
- What are the nouns? (potential classes)
|
||||
- What are the verbs? (potential methods)
|
||||
- What are the dependencies?
|
||||
- How will I test this?
|
||||
|
||||
**Sketch the structure**:
|
||||
```typescript
|
||||
// Example: Feature to export user data
|
||||
|
||||
// Nouns: UserExporter, ExportFormat, ExportResult
|
||||
// Verbs: export, format, validate
|
||||
|
||||
interface UserExporter {
|
||||
export(userId: string, format: ExportFormat): Promise<ExportResult>;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Write Acceptance Test(s)
|
||||
|
||||
**Goal**: Define "done" with executable tests.
|
||||
|
||||
**Reference**: `acceptance-testing/rules.md`
|
||||
|
||||
```typescript
|
||||
describe('User Export Feature', () => {
|
||||
it('should export user data as JSON', async () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
await createTestUser(userId, { name: 'Alice', email: 'alice@example.com' });
|
||||
|
||||
// Act
|
||||
const result = await userExporter.export(userId, 'json');
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toContain('"name":"Alice"');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] Tests describe the acceptance criteria
|
||||
- [ ] Tests are readable by non-developers
|
||||
- [ ] Tests fail (feature doesn't exist yet)
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Build with TDD
|
||||
|
||||
**Goal**: Implement using red-green-refactor cycles.
|
||||
|
||||
**Reference**: `tdd.md` workflow
|
||||
|
||||
For each piece of functionality:
|
||||
|
||||
1. **RED**: Write a small failing unit test
|
||||
2. **GREEN**: Write minimum code to pass
|
||||
3. **REFACTOR**: Clean up while green
|
||||
|
||||
**Start with the simplest case**:
|
||||
```typescript
|
||||
describe('UserExporter', () => {
|
||||
it('should throw if user not found', async () => {
|
||||
const exporter = new UserExporter(mockRepo);
|
||||
await expect(exporter.export('nonexistent', 'json'))
|
||||
.rejects.toThrow('User not found');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Build up complexity**:
|
||||
```
|
||||
1. Error case: user not found
|
||||
2. Simple case: export one field
|
||||
3. Full case: export all fields
|
||||
4. Format: JSON format
|
||||
5. Format: CSV format
|
||||
6. Edge: special characters
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Apply Clean Code Principles
|
||||
|
||||
**Goal**: Write clean code from the start.
|
||||
|
||||
While building, continuously apply:
|
||||
|
||||
**Functions** (`functions/rules.md`):
|
||||
- [ ] Small (5-20 lines)
|
||||
- [ ] Do one thing
|
||||
- [ ] Descriptive names
|
||||
- [ ] Few arguments (≤3)
|
||||
|
||||
**Naming** (`naming/rules.md`):
|
||||
- [ ] Intention-revealing names
|
||||
- [ ] No abbreviations
|
||||
- [ ] Consistent terminology
|
||||
|
||||
**Error Handling** (`error-handling/rules.md`):
|
||||
- [ ] Use exceptions, not error codes
|
||||
- [ ] Don't return null
|
||||
- [ ] Provide context in errors
|
||||
|
||||
**Example of clean implementation**:
|
||||
```typescript
|
||||
class UserExporter {
|
||||
constructor(private readonly userRepository: UserRepository) {}
|
||||
|
||||
async export(userId: string, format: ExportFormat): Promise<ExportResult> {
|
||||
const user = await this.findUserOrThrow(userId);
|
||||
const formatter = this.getFormatter(format);
|
||||
const data = formatter.format(user);
|
||||
|
||||
return { success: true, data };
|
||||
}
|
||||
|
||||
private async findUserOrThrow(userId: string): Promise<User> {
|
||||
const user = await this.userRepository.findById(userId);
|
||||
if (!user) {
|
||||
throw new UserNotFoundError(userId);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private getFormatter(format: ExportFormat): Formatter {
|
||||
const formatters: Record<ExportFormat, Formatter> = {
|
||||
json: new JsonFormatter(),
|
||||
csv: new CsvFormatter(),
|
||||
};
|
||||
return formatters[format] ?? throw new UnsupportedFormatError(format);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Review Against Checklist
|
||||
|
||||
**Goal**: Self-review before considering "done".
|
||||
|
||||
**Reference**: `functions/checklist.md`, `code-review.md` workflow
|
||||
|
||||
- [ ] All acceptance tests pass
|
||||
- [ ] Unit test coverage is adequate
|
||||
- [ ] Functions are small and focused
|
||||
- [ ] Names reveal intent
|
||||
- [ ] No code smells
|
||||
- [ ] Error handling is clean
|
||||
- [ ] No commented-out code
|
||||
- [ ] Code is formatted consistently
|
||||
|
||||
---
|
||||
|
||||
### Step 7: Refactor if Needed
|
||||
|
||||
**Goal**: Polish the implementation.
|
||||
|
||||
**Reference**: `refactoring.md` workflow
|
||||
|
||||
- [ ] Remove any duplication
|
||||
- [ ] Improve any unclear names
|
||||
- [ ] Extract any long methods
|
||||
- [ ] Simplify any complex logic
|
||||
- [ ] All tests still pass
|
||||
|
||||
---
|
||||
|
||||
### Step 8: Document if Necessary
|
||||
|
||||
**Goal**: Add only necessary documentation.
|
||||
|
||||
**Reference**: `comments/rules.md`
|
||||
|
||||
- [ ] Public APIs have doc comments
|
||||
- [ ] Complex "why" decisions are explained
|
||||
- [ ] No redundant comments
|
||||
- [ ] README updated if needed
|
||||
|
||||
**Good documentation**:
|
||||
```typescript
|
||||
/**
|
||||
* Exports user data in the specified format.
|
||||
*
|
||||
* @throws UserNotFoundError if user doesn't exist
|
||||
* @throws UnsupportedFormatError if format is not supported
|
||||
*/
|
||||
export(userId: string, format: ExportFormat): Promise<ExportResult>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Checklist
|
||||
|
||||
```
|
||||
[ ] Requirements understood
|
||||
[ ] Approach planned
|
||||
[ ] Acceptance tests written
|
||||
[ ] Built with TDD (red-green-refactor)
|
||||
[ ] Clean code principles applied
|
||||
[ ] Self-reviewed against checklist
|
||||
[ ] Refactored if needed
|
||||
[ ] Documented if necessary
|
||||
[ ] All tests pass
|
||||
[ ] Ready for code review
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Exit Criteria
|
||||
|
||||
Feature is complete when:
|
||||
- [ ] All acceptance criteria are met
|
||||
- [ ] All tests pass
|
||||
- [ ] Code follows clean code principles
|
||||
- [ ] Self-review checklist passes
|
||||
- [ ] Ready for peer code review
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
name: 'step-01-init'
|
||||
description: 'Initialize PR review workflow — set output path, understand context, detect continuation'
|
||||
nextStepFile: './step-02-tests-first.md'
|
||||
---
|
||||
|
||||
# Step 1: Initialize PR Review
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Set up the PR review session: identify the target PR, set the output path for the review report, understand context, and check for an existing report to resume.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### 1. Ask the User
|
||||
|
||||
Ask the user:
|
||||
- **Which PR to review?** (PR URL, branch name, or diff)
|
||||
- **Output path** for the review report (suggest a default: `./pr-review-report-{{date}}.md`)
|
||||
- **Or provide path to an existing report** to resume a previous review
|
||||
|
||||
### 2. Check for Existing Report
|
||||
|
||||
If the user provides a path to an existing report file:
|
||||
- Read the file
|
||||
- Parse the YAML frontmatter
|
||||
- If `stepsCompleted` is non-empty → **STOP and load `step-01b-continue.md`**
|
||||
|
||||
### 3. Fresh Workflow Setup
|
||||
|
||||
If starting fresh:
|
||||
1. Copy the template from `templates/report-template.md`
|
||||
2. Fill in the frontmatter:
|
||||
- `targetPR`: the PR identifier provided by the user
|
||||
- `outputPath`: the chosen output path
|
||||
- `date`: current date
|
||||
3. Write the initialized report to the output path
|
||||
|
||||
### 4. Understand the Context
|
||||
|
||||
**Goal**: Know what the PR is trying to accomplish.
|
||||
|
||||
- Read the PR title and description
|
||||
- Read the linked ticket/issue
|
||||
- Understand the acceptance criteria
|
||||
- Check the scope (how big is this change?)
|
||||
|
||||
**Questions to answer**:
|
||||
- What problem does this solve?
|
||||
- Is this a feature, bug fix, or refactor?
|
||||
- What should I focus on?
|
||||
|
||||
**Time estimate**:
|
||||
```
|
||||
Small PR (< 100 lines): 15-30 min
|
||||
Medium PR (100-400 lines): 30-60 min
|
||||
Large PR (> 400 lines): Consider requesting split
|
||||
```
|
||||
|
||||
### 5. Append Context to Report
|
||||
|
||||
Append to the output document:
|
||||
|
||||
```markdown
|
||||
## Step 1: Context
|
||||
|
||||
**PR**: {{targetPR}}
|
||||
**Type**: {{feature/bugfix/refactor}}
|
||||
**Scope**: {{prSize}} ({{line count}} lines)
|
||||
**Purpose**: {{purpose}}
|
||||
**Focus Areas**: {{what to focus on}}
|
||||
```
|
||||
|
||||
Update frontmatter:
|
||||
- Set `prSize` to the estimated size
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document frontmatter:
|
||||
- Add `1` to `stepsCompleted`
|
||||
|
||||
## PRESENT TO USER
|
||||
|
||||
Show the user:
|
||||
- PR context summary
|
||||
- Estimated review time
|
||||
- Confirmation of output path
|
||||
|
||||
Then ask: **[C] Continue to Step 2: Review Tests First**
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-02-tests-first.md`.
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
name: 'step-01b-continue'
|
||||
description: 'Resume PR review from last completed step'
|
||||
---
|
||||
|
||||
# Step 1b: Continue Previous PR Review
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Resume a previously started PR review by reading the existing report, determining progress, and routing to the next incomplete step.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### 1. Read Existing Report
|
||||
|
||||
- Read the file at the output path provided by the user
|
||||
- Parse the YAML frontmatter
|
||||
- Extract `stepsCompleted` array
|
||||
|
||||
### 2. Show Progress Summary
|
||||
|
||||
Display to the user:
|
||||
|
||||
```
|
||||
PR Review Progress
|
||||
==================
|
||||
PR: {{targetPR}}
|
||||
Date Started: {{date}}
|
||||
Size: {{prSize}}
|
||||
Steps Completed: {{stepsCompleted}}
|
||||
|
||||
Step Map:
|
||||
[1] Context & Setup {{done/pending}}
|
||||
[2] Tests First {{done/pending}}
|
||||
[3] High-Level Review {{done/pending}}
|
||||
[4] Code Details {{done/pending}}
|
||||
[5] Security {{done/pending}}
|
||||
[6] Performance {{done/pending}}
|
||||
[7] Run Code {{done/pending}}
|
||||
[8] Feedback {{done/pending}}
|
||||
[9] Decision {{done/pending}}
|
||||
```
|
||||
|
||||
### 3. Offer Options
|
||||
|
||||
Present:
|
||||
- **[R] Resume** from the next incomplete step
|
||||
- **[O] Overview** — re-read the existing report content before resuming
|
||||
- **[X] Start over** — create a fresh report (confirm: this will overwrite)
|
||||
|
||||
### 4. Route to Next Step
|
||||
|
||||
On **[R]** or after **[O]**:
|
||||
|
||||
Determine the next step from `max(stepsCompleted) + 1` and load the corresponding file:
|
||||
|
||||
| Next Step | File |
|
||||
|-----------|------|
|
||||
| 2 | `step-02-tests-first.md` |
|
||||
| 3 | `step-03-high-level.md` |
|
||||
| 4 | `step-04-code-details.md` |
|
||||
| 5 | `step-05-security.md` |
|
||||
| 6 | `step-06-performance.md` |
|
||||
| 7 | `step-07-run-code.md` |
|
||||
| 8 | `step-08-feedback.md` |
|
||||
| 9 | `step-09-decision.md` |
|
||||
|
||||
On **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
Load the step file determined above.
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
name: 'step-02-tests-first'
|
||||
description: 'Review tests first — coverage, meaningfulness, edge cases'
|
||||
nextStepFile: './step-03-high-level.md'
|
||||
referenceFiles:
|
||||
- 'references/unit-tests/rules.md'
|
||||
- 'references/tdd/rules.md'
|
||||
---
|
||||
|
||||
# Step 2: Review the Tests First
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Understand what the code should do by reading the tests first. Verify test coverage, meaningfulness, and edge case handling.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/unit-tests/rules.md` — test quality rules
|
||||
- `references/tdd/rules.md` — TDD rules
|
||||
|
||||
Cite specific rules when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
1. **Check test coverage** for new/changed code
|
||||
2. **Read test names** to understand expected behavior
|
||||
3. **Verify tests are meaningful** (not just coverage padding)
|
||||
4. **Look for missing test cases**
|
||||
|
||||
### Questions to Answer
|
||||
|
||||
- Do tests cover the acceptance criteria?
|
||||
- Are edge cases tested?
|
||||
- Are error cases handled?
|
||||
- Would the tests catch regressions?
|
||||
|
||||
### Red Flags
|
||||
|
||||
Watch for and report:
|
||||
- No tests for new functionality
|
||||
- Tests that don't actually assert anything
|
||||
- Tests that test implementation, not behavior
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 2: Tests First
|
||||
===================
|
||||
|
||||
Test Coverage:
|
||||
- New code covered: YES/NO/PARTIAL
|
||||
- Edge cases: covered/missing: [list]
|
||||
- Error cases: covered/missing: [list]
|
||||
|
||||
[PASS/ISSUE] test_name (file:line)
|
||||
- Problem: description
|
||||
Rule: unit-tests/rules.md — Rule N
|
||||
|
||||
Summary: N tests reviewed, N issues found, N missing test cases
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 3: High-Level Review**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `2` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-03-high-level.md`.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: 'step-03-high-level'
|
||||
description: 'High-level code review — architecture, approach, structure'
|
||||
nextStepFile: './step-04-code-details.md'
|
||||
---
|
||||
|
||||
# Step 3: Review the Code - High Level
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Understand the overall approach, architecture decisions, and whether the solution fits the problem.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
1. **Look at the file structure** changes
|
||||
2. **Understand the architecture** decisions
|
||||
3. **Check if the approach** makes sense
|
||||
4. **Identify any major concerns**
|
||||
|
||||
### Questions to Answer
|
||||
|
||||
- Does the solution fit the problem?
|
||||
- Is the architecture appropriate?
|
||||
- Are there simpler alternatives?
|
||||
|
||||
### What to Look At
|
||||
|
||||
- New files created
|
||||
- Files with significant changes
|
||||
- Changes to shared/core code
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 3: High-Level Review
|
||||
=========================
|
||||
|
||||
File Structure:
|
||||
- New files: [list]
|
||||
- Modified files: [list]
|
||||
- Deleted files: [list]
|
||||
|
||||
Architecture Assessment:
|
||||
- Approach: [description]
|
||||
- Fits problem: YES/PARTIALLY/NO — reason
|
||||
- Simpler alternative: [if applicable]
|
||||
|
||||
Concerns:
|
||||
- [list or "None"]
|
||||
|
||||
Summary: [overall structural assessment]
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 4: Code Details**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `3` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-04-code-details.md`.
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: 'step-04-code-details'
|
||||
description: 'Detailed code review — functions, naming, error handling, comments, smells'
|
||||
nextStepFile: './step-05-security.md'
|
||||
referenceFiles:
|
||||
- 'references/functions/rules.md'
|
||||
- 'references/naming/rules.md'
|
||||
- 'references/error-handling/rules.md'
|
||||
- 'references/comments/rules.md'
|
||||
- 'references/smells/rules.md'
|
||||
---
|
||||
|
||||
# Step 4: Review the Code - Details
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Check code quality line by line across all changed files, applying the full code review checklist.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/functions/rules.md` — function design rules
|
||||
- `references/naming/rules.md` — naming conventions
|
||||
- `references/error-handling/rules.md` — error handling rules
|
||||
- `references/comments/rules.md` — comment rules
|
||||
- `references/smells/rules.md` — code smell catalog
|
||||
|
||||
Cite specific rules when reporting findings.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
For each changed file, check:
|
||||
|
||||
### Functions
|
||||
- Small (5-20 lines)
|
||||
- Do one thing
|
||||
- Good names
|
||||
- Few arguments
|
||||
|
||||
### Naming
|
||||
- Intention-revealing
|
||||
- Consistent terminology
|
||||
- No abbreviations
|
||||
|
||||
### Error Handling
|
||||
- Exceptions used properly
|
||||
- No null returns
|
||||
- Proper error context
|
||||
|
||||
### Comments
|
||||
- Only necessary comments
|
||||
- No commented-out code
|
||||
- Comments are accurate
|
||||
|
||||
### Smells
|
||||
- No duplication
|
||||
- No feature envy
|
||||
- No god classes
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 4: Code Details
|
||||
====================
|
||||
|
||||
File: {{filename}}
|
||||
[PASS/ISSUE] (line N) category: description
|
||||
Rule: {{category}}/rules.md — Rule N
|
||||
Suggestion: fix
|
||||
|
||||
File: {{filename}}
|
||||
...
|
||||
|
||||
Summary: N files reviewed, N issues found
|
||||
- Functions: N issues
|
||||
- Naming: N issues
|
||||
- Error Handling: N issues
|
||||
- Comments: N issues
|
||||
- Smells: N issues
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 5: Security**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `4` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-05-security.md`.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
name: 'step-05-security'
|
||||
description: 'Check for security vulnerabilities — injection, XSS, auth, secrets'
|
||||
nextStepFile: './step-06-performance.md'
|
||||
---
|
||||
|
||||
# Step 5: Check for Security Issues
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Identify potential security vulnerabilities in the PR changes.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
Check all changed code for:
|
||||
|
||||
1. **Input validation** present?
|
||||
2. **SQL injection** possible?
|
||||
3. **XSS vulnerabilities**?
|
||||
4. **Sensitive data** exposed?
|
||||
5. **Authentication/authorization** checked?
|
||||
6. **Secrets in code**?
|
||||
|
||||
### Common Issues
|
||||
|
||||
```typescript
|
||||
// BAD: SQL injection
|
||||
const query = `SELECT * FROM users WHERE id = ${userId}`;
|
||||
|
||||
// GOOD: Parameterized query
|
||||
const query = 'SELECT * FROM users WHERE id = ?';
|
||||
db.query(query, [userId]);
|
||||
```
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 5: Security
|
||||
================
|
||||
|
||||
[PASS/ISSUE] vulnerability_type (file:line)
|
||||
- Risk: HIGH/MEDIUM/LOW
|
||||
- Description: what the vulnerability is
|
||||
- Attack vector: how it could be exploited
|
||||
- Fix: how to remediate
|
||||
|
||||
Summary: N security issues found
|
||||
- HIGH: N
|
||||
- MEDIUM: N
|
||||
- LOW: N
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 6: Performance**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `5` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-06-performance.md`.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: 'step-06-performance'
|
||||
description: 'Check for performance issues — N+1, memory, blocking'
|
||||
nextStepFile: './step-07-run-code.md'
|
||||
---
|
||||
|
||||
# Step 6: Check for Performance Issues
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Identify obvious performance problems in the PR changes.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
Check all changed code for:
|
||||
|
||||
1. **N+1 query problems**?
|
||||
2. **Large data sets in memory**?
|
||||
3. **Unnecessary database calls**?
|
||||
4. **Missing indexes for queries**?
|
||||
5. **Blocking operations in async code**?
|
||||
|
||||
### Common Issues
|
||||
|
||||
```typescript
|
||||
// BAD: N+1 query
|
||||
for (const user of users) {
|
||||
const orders = await db.getOrdersForUser(user.id);
|
||||
}
|
||||
|
||||
// GOOD: Single query
|
||||
const orders = await db.getOrdersForUsers(userIds);
|
||||
```
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 6: Performance
|
||||
===================
|
||||
|
||||
[PASS/ISSUE] issue_type (file:line)
|
||||
- Impact: HIGH/MEDIUM/LOW
|
||||
- Description: what the performance issue is
|
||||
- Suggestion: how to optimize
|
||||
|
||||
Summary: N performance issues found
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 7: Run Code**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `6` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-07-run-code.md`.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
name: 'step-07-run-code'
|
||||
description: 'Run the code if needed — tests, manual verification'
|
||||
nextStepFile: './step-08-feedback.md'
|
||||
---
|
||||
|
||||
# Step 7: Run the Code (if needed)
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Verify the code actually works by running tests and optionally testing manually.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### 1. Determine if Running is Needed
|
||||
|
||||
Running locally is recommended for:
|
||||
- Complex logic changes
|
||||
- UI changes
|
||||
- Integration changes
|
||||
- When you're unsure about correctness
|
||||
|
||||
### 2. Run Tests
|
||||
|
||||
If applicable:
|
||||
- Pull the branch locally
|
||||
- Run the test suite
|
||||
- Note any failures
|
||||
|
||||
### 3. Manual Testing
|
||||
|
||||
If applicable:
|
||||
- Test the happy path
|
||||
- Test edge cases
|
||||
- Verify the fix/feature works as described
|
||||
|
||||
### 4. Record Results
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present findings to the user in this format:
|
||||
|
||||
```
|
||||
Step 7: Run Code
|
||||
================
|
||||
|
||||
Tests:
|
||||
- Ran: YES/NO/SKIPPED
|
||||
- Result: ALL PASS / N FAILURES
|
||||
- Failures: [list if any]
|
||||
|
||||
Manual Testing:
|
||||
- Performed: YES/NO/SKIPPED
|
||||
- Result: WORKS / ISSUES FOUND
|
||||
- Issues: [list if any]
|
||||
|
||||
Recommendation: [proceed / investigate failures]
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 8: Feedback**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `7` to `stepsCompleted`
|
||||
- Append the findings section to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-08-feedback.md`.
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: 'step-08-feedback'
|
||||
description: 'Compile feedback — categorize, format, prepare for decision'
|
||||
nextStepFile: './step-09-decision.md'
|
||||
referenceFiles:
|
||||
- 'references/collaboration/rules.md'
|
||||
---
|
||||
|
||||
# Step 8: Provide Feedback
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Compile all findings from steps 2-7 into categorized, constructive, actionable feedback.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before compiling feedback, load and read:
|
||||
- `references/collaboration/rules.md` — feedback and communication rules
|
||||
|
||||
## COMPILATION PROCESS
|
||||
|
||||
### 1. Gather All Findings
|
||||
|
||||
Read through the output document and collect all issues found in steps 2-7.
|
||||
|
||||
### 2. Categorize Feedback
|
||||
|
||||
Use these categories:
|
||||
|
||||
| Prefix | Meaning | Action Required |
|
||||
|--------|---------|-----------------|
|
||||
| `[BLOCKING]` | Must fix before merge | Yes |
|
||||
| `[SUGGESTION]` | Nice to have | No |
|
||||
| `[QUESTION]` | Need clarification | Depends |
|
||||
| `[NIT]` | Minor style issue | No |
|
||||
| `[PRAISE]` | Something good | No |
|
||||
|
||||
### 3. Format Each Item
|
||||
|
||||
```
|
||||
[BLOCKING] src/services/userExporter.ts:45
|
||||
|
||||
This function is doing too many things. It validates, fetches, formats,
|
||||
and saves in one place.
|
||||
|
||||
Reference: functions/rules.md - "Do One Thing"
|
||||
|
||||
Suggestion: Extract into separate functions:
|
||||
- validateExportRequest()
|
||||
- fetchUser()
|
||||
- formatUser()
|
||||
- saveExport()
|
||||
```
|
||||
|
||||
### 4. Include Praise
|
||||
|
||||
Don't forget to call out what's done well — clean patterns, good test coverage, clever solutions.
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
Present the categorized feedback to the user:
|
||||
|
||||
```
|
||||
Step 8: Feedback Summary
|
||||
========================
|
||||
|
||||
[BLOCKING] (N items)
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
[SUGGESTION] (N items)
|
||||
1. ...
|
||||
|
||||
[QUESTION] (N items)
|
||||
1. ...
|
||||
|
||||
[NIT] (N items)
|
||||
1. ...
|
||||
|
||||
[PRAISE] (N items)
|
||||
1. ...
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 9: Decision**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `8` to `stepsCompleted`
|
||||
- Append the categorized feedback to the report
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-09-decision.md`.
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
name: 'step-09-decision'
|
||||
description: 'Make the final PR decision — approve, request changes, or comment'
|
||||
---
|
||||
|
||||
# Step 9: Make the Decision
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Make the final review decision: approve, request changes, or comment. Complete the review report.
|
||||
|
||||
## DECISION CRITERIA
|
||||
|
||||
### Approve when:
|
||||
- All blocking issues resolved
|
||||
- Tests are adequate
|
||||
- Code quality is acceptable
|
||||
- You'd be comfortable maintaining this code
|
||||
|
||||
### Request Changes when:
|
||||
- Blocking issues exist
|
||||
- Tests are missing for critical paths
|
||||
- Security vulnerabilities found
|
||||
- Major design problems
|
||||
|
||||
### Comment when:
|
||||
- You have questions but no blockers
|
||||
- You want to discuss approaches
|
||||
- You're not the final approver
|
||||
|
||||
## COMPILATION
|
||||
|
||||
### 1. Write Final Decision
|
||||
|
||||
Append to the output document:
|
||||
|
||||
```markdown
|
||||
## Final Decision
|
||||
|
||||
### PR Review Checklist
|
||||
|
||||
Context:
|
||||
- [ ] PR description read
|
||||
- [ ] Ticket/issue understood
|
||||
- [ ] Scope is appropriate
|
||||
|
||||
Tests:
|
||||
- [ ] New code has tests
|
||||
- [ ] Tests are meaningful
|
||||
- [ ] Edge cases covered
|
||||
|
||||
Code Quality:
|
||||
- [ ] Functions are small
|
||||
- [ ] Names are clear
|
||||
- [ ] No code smells
|
||||
- [ ] Error handling is proper
|
||||
|
||||
Security:
|
||||
- [ ] No vulnerabilities
|
||||
- [ ] Input validated
|
||||
- [ ] No secrets in code
|
||||
|
||||
Performance:
|
||||
- [ ] No obvious issues
|
||||
- [ ] No N+1 queries
|
||||
|
||||
Feedback:
|
||||
- [ ] Constructive and specific
|
||||
- [ ] Clear blocking vs suggestions
|
||||
- [ ] Actionable recommendations
|
||||
|
||||
### Decision: [APPROVE / REQUEST CHANGES / COMMENT]
|
||||
|
||||
**Reason**: [summary]
|
||||
|
||||
### Common Issues Found
|
||||
|
||||
| Issue | Location | Category |
|
||||
|-------|----------|----------|
|
||||
| ... | file:line | BLOCKING/SUGGESTION/NIT |
|
||||
```
|
||||
|
||||
## PRESENT TO USER
|
||||
|
||||
Show the final decision and checklist to the user.
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document frontmatter:
|
||||
- Add `9` to `stepsCompleted`
|
||||
- Set `status` to `'complete'`
|
||||
- Set `decision` to the chosen decision
|
||||
|
||||
## WORKFLOW COMPLETE
|
||||
|
||||
The PR review workflow is complete. The full report is saved at the output path.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
stepsCompleted: []
|
||||
inputDocuments: []
|
||||
workflowType: 'pr-review'
|
||||
targetPR: ''
|
||||
outputPath: ''
|
||||
date: ''
|
||||
status: 'in-progress'
|
||||
prSize: ''
|
||||
decision: ''
|
||||
---
|
||||
|
||||
# PR Review Report
|
||||
|
||||
**Date**: {{date}}
|
||||
**PR**: {{targetPR}}
|
||||
**Size**: {{prSize}}
|
||||
**Status**: {{status}}
|
||||
**Decision**: {{decision}}
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: 'pr-review'
|
||||
description: 'Comprehensive process for reviewing pull requests'
|
||||
firstStepFile: './steps/step-01-init.md'
|
||||
templateFile: './templates/report-template.md'
|
||||
---
|
||||
|
||||
# PR Review Workflow
|
||||
|
||||
Comprehensive process for reviewing pull requests.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Reviewing a teammate's PR
|
||||
- Doing a final check before merge
|
||||
- Conducting formal code review
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Access to the PR and codebase
|
||||
- Understanding of the feature/fix context
|
||||
- Time to do a thorough review
|
||||
|
||||
## Step-File Architecture
|
||||
|
||||
This workflow uses a **step-file architecture** for context-safe execution:
|
||||
|
||||
- Each step is a separate file loaded sequentially
|
||||
- Progress is tracked via `stepsCompleted` in the output document's YAML frontmatter
|
||||
- If context is compacted mid-workflow, step-01 detects existing output and resumes from the last completed step via step-01b
|
||||
|
||||
### Steps
|
||||
|
||||
| Step | File | Description |
|
||||
|------|------|-------------|
|
||||
| 1 | `step-01-init.md` | Initialize workflow, set output path, detect continuation |
|
||||
| 1b | `step-01b-continue.md` | Resume from last completed step |
|
||||
| 2 | `step-02-tests-first.md` | Review tests first |
|
||||
| 3 | `step-03-high-level.md` | Review code at high level |
|
||||
| 4 | `step-04-code-details.md` | Review code details |
|
||||
| 5 | `step-05-security.md` | Check for security issues |
|
||||
| 6 | `step-06-performance.md` | Check for performance issues |
|
||||
| 7 | `step-07-run-code.md` | Run code if needed |
|
||||
| 8 | `step-08-feedback.md` | Provide constructive feedback |
|
||||
| 9 | `step-09-decision.md` | Make approve/reject decision, mark complete |
|
||||
|
||||
### Rules
|
||||
|
||||
1. **Load one step at a time** - Read the step file, execute it, then load the next
|
||||
2. **Update frontmatter after each step** - Add the step number to `stepsCompleted`
|
||||
3. **Wait for user confirmation** - Present findings and wait for `[C]` before proceeding
|
||||
4. **Load reference files** - Each step specifies which reference files to load before analysis
|
||||
5. **Cite specific rules** - When reporting findings, cite the specific rule from the reference file
|
||||
|
||||
## Begin
|
||||
|
||||
Load `steps/step-01-init.md` to start.
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
name: 'step-01-init'
|
||||
description: 'Initialize refactoring workflow — verify tests, set output path, detect continuation'
|
||||
nextStepFile: './step-02-identify-smell.md'
|
||||
---
|
||||
|
||||
# Step 1: Initialize Refactoring
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Set up the refactoring session: identify the target code, verify tests pass, set the output path for the refactoring log, and check for an existing log to resume.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### 1. Ask the User
|
||||
|
||||
Ask the user:
|
||||
- **What code to refactor?** (file path, module, or class)
|
||||
- **What's the improvement goal?** (what smell to fix, what to clean up)
|
||||
- **Output path** for the refactoring log (suggest a default: `./refactoring-log-{{date}}.md`)
|
||||
- **Or provide path to an existing log** to resume a previous session
|
||||
|
||||
### 2. Check for Existing Log
|
||||
|
||||
If the user provides a path to an existing log file:
|
||||
- Read the file
|
||||
- Parse the YAML frontmatter
|
||||
- If `stepsCompleted` is non-empty → **STOP and load `step-01b-continue.md`**
|
||||
|
||||
### 3. Fresh Workflow Setup
|
||||
|
||||
If starting fresh:
|
||||
1. Copy the template from `templates/log-template.md`
|
||||
2. Fill in the frontmatter:
|
||||
- `targetCode`: the code path provided by the user
|
||||
- `outputPath`: the chosen output path
|
||||
- `date`: current date
|
||||
3. Write the initialized log to the output path
|
||||
|
||||
### 4. Verify Tests Pass
|
||||
|
||||
**Goal**: Establish a green baseline.
|
||||
|
||||
```bash
|
||||
npm test # All tests must pass
|
||||
```
|
||||
|
||||
- All existing tests pass
|
||||
- Coverage is adequate for the code you'll change
|
||||
- You understand what the tests verify
|
||||
|
||||
**If tests fail**: Fix them first. Don't refactor broken code.
|
||||
|
||||
**If no tests exist**: Write characterization tests first. See `test-strategy.md` workflow.
|
||||
|
||||
### 5. Append to Log
|
||||
|
||||
Append to the output document:
|
||||
|
||||
```markdown
|
||||
## Step 1: Initialization
|
||||
|
||||
**Target**: {{targetCode}}
|
||||
**Goal**: {{improvement goal}}
|
||||
**Tests**: {{PASS / FAIL — details}}
|
||||
**Coverage**: {{adequate / needs improvement}}
|
||||
```
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document frontmatter:
|
||||
- Add `1` to `stepsCompleted`
|
||||
- Set `testsGreen` to `true` (if tests passed)
|
||||
|
||||
## PRESENT TO USER
|
||||
|
||||
Show the user:
|
||||
- Confirmation of the refactoring target and goal
|
||||
- Test results
|
||||
- Output path
|
||||
|
||||
Then ask: **[C] Continue to Step 2: Identify the Smell**
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-02-identify-smell.md`.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
name: 'step-01b-continue'
|
||||
description: 'Resume refactoring from last completed step'
|
||||
---
|
||||
|
||||
# Step 1b: Continue Previous Refactoring
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Resume a previously started refactoring session by reading the existing log, determining progress, and routing to the next incomplete step.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### 1. Read Existing Log
|
||||
|
||||
- Read the file at the output path provided by the user
|
||||
- Parse the YAML frontmatter
|
||||
- Extract `stepsCompleted` array and `iterations` array
|
||||
|
||||
### 2. Show Progress Summary
|
||||
|
||||
Display to the user:
|
||||
|
||||
```
|
||||
Refactoring Progress
|
||||
====================
|
||||
Target: {{targetCode}}
|
||||
Smell: {{smell}}
|
||||
Date Started: {{date}}
|
||||
Steps Completed: {{stepsCompleted}}
|
||||
Iterations: {{iterations.length}} change-test-commit cycles
|
||||
|
||||
Step Map:
|
||||
[1] Initialize & Verify Tests {{done/pending}}
|
||||
[2] Identify Smell {{done/pending}}
|
||||
[3] Plan Steps {{done/pending}}
|
||||
[4] Make ONE Change {{done/pending}}
|
||||
[5] Run Tests {{done/pending}}
|
||||
[6] Commit {{done/pending}}
|
||||
[7] Repeat / Complete {{done/pending}}
|
||||
|
||||
Note: Steps 4-7 may repeat multiple times (loop).
|
||||
Last iteration: {{last iteration details if any}}
|
||||
```
|
||||
|
||||
### 3. Offer Options
|
||||
|
||||
Present:
|
||||
- **[R] Resume** from the next incomplete step
|
||||
- **[O] Overview** — re-read the existing log content before resuming
|
||||
- **[X] Start over** — create a fresh log (confirm: this will overwrite)
|
||||
|
||||
### 4. Route to Next Step
|
||||
|
||||
On **[R]** or after **[O]**:
|
||||
|
||||
Determine the next step. For the refactoring workflow, steps 4-7 loop, so:
|
||||
|
||||
- If `stepsCompleted` contains `7` (repeat step was reached and decided to loop):
|
||||
- Check the last entry — if it says "loop back", load `step-04-make-change.md`
|
||||
- If it says "complete", the workflow is done
|
||||
- Otherwise, determine from `max(stepsCompleted) + 1`:
|
||||
|
||||
| Next Step | File |
|
||||
|-----------|------|
|
||||
| 2 | `step-02-identify-smell.md` |
|
||||
| 3 | `step-03-plan-steps.md` |
|
||||
| 4 | `step-04-make-change.md` |
|
||||
| 5 | `step-05-run-tests.md` |
|
||||
| 6 | `step-06-commit.md` |
|
||||
| 7 | `step-07-repeat.md` |
|
||||
|
||||
On **[X]**: Go back to `step-01-init.md` fresh workflow setup (section 3).
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
Load the step file determined above.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
name: 'step-02-identify-smell'
|
||||
description: 'Identify the specific code smell to fix'
|
||||
nextStepFile: './step-03-plan-steps.md'
|
||||
referenceFiles:
|
||||
- 'references/smells/rules.md'
|
||||
---
|
||||
|
||||
# Step 2: Identify the Smell
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Know exactly what you're fixing. Identify the specific code smell, understand why it's a problem, and define the target state.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before starting analysis, load and read:
|
||||
- `references/smells/rules.md` — code smell catalog
|
||||
|
||||
Cite specific smell codes when identifying the smell.
|
||||
|
||||
## ANALYSIS PROCESS
|
||||
|
||||
### 1. Scan for Smells
|
||||
|
||||
Common smells to look for:
|
||||
|
||||
| Smell | Symptom | Refactoring |
|
||||
|-------|---------|-------------|
|
||||
| G5: Duplication | Copy-pasted code | Extract Method/Class |
|
||||
| Long Function | > 20 lines | Extract Method |
|
||||
| Long Parameter List | > 3 params | Introduce Parameter Object |
|
||||
| Feature Envy | Uses other class's data | Move Method |
|
||||
| God Class | Too many responsibilities | Extract Class |
|
||||
| Primitive Obsession | Primitives instead of objects | Replace with Value Object |
|
||||
|
||||
### 2. Confirm with User
|
||||
|
||||
Present the identified smell and ask the user to confirm:
|
||||
- What the smell is
|
||||
- Why it's a problem
|
||||
- What the target state looks like
|
||||
|
||||
## PRESENT FINDINGS
|
||||
|
||||
```
|
||||
Step 2: Smell Identification
|
||||
============================
|
||||
|
||||
Identified Smell: {{smell code}} — {{smell name}}
|
||||
Location: {{file:line}}
|
||||
Evidence: {{what makes this a smell}}
|
||||
Impact: {{why it's a problem}}
|
||||
Target State: {{what it should look like after refactoring}}
|
||||
|
||||
Rule: smells/rules.md — {{smell code}}
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 3: Plan Steps**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `2` to `stepsCompleted`
|
||||
- Set `smell` to the identified smell name
|
||||
- Append the findings to the log
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-03-plan-steps.md`.
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: 'step-03-plan-steps'
|
||||
description: 'Plan small, safe refactoring steps'
|
||||
nextStepFile: './step-04-make-change.md'
|
||||
---
|
||||
|
||||
# Step 3: Plan Small Steps
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Break the refactoring into tiny, safe changes. Each step should take < 5 minutes and keep tests green.
|
||||
|
||||
## PLANNING PROCESS
|
||||
|
||||
### 1. Break Down the Refactoring
|
||||
|
||||
**Rule**: Each step should be:
|
||||
- Independently testable
|
||||
- Purely structural (no behavior change)
|
||||
- Describable in one sentence
|
||||
|
||||
### 2. Example Breakdown
|
||||
|
||||
For extracting a long function:
|
||||
```
|
||||
Step 1: Identify code block to extract
|
||||
Step 2: Create new function with extracted code
|
||||
Step 3: Replace original code with function call
|
||||
Step 4: Run tests
|
||||
Step 5: Rename function for clarity
|
||||
Step 6: Run tests
|
||||
Step 7: Move function if needed
|
||||
Step 8: Run tests
|
||||
```
|
||||
|
||||
### 3. Common Refactoring Patterns
|
||||
|
||||
**Extract Method**: Function is too long or does multiple things
|
||||
**Rename**: Name doesn't reveal intent
|
||||
**Introduce Parameter Object**: Too many parameters
|
||||
**Replace Conditional with Polymorphism**: Switch statements on type
|
||||
**Extract Class**: Class has multiple responsibilities
|
||||
|
||||
## PRESENT PLAN
|
||||
|
||||
```
|
||||
Step 3: Refactoring Plan
|
||||
========================
|
||||
|
||||
Target Smell: {{smell}}
|
||||
Planned Steps:
|
||||
1. {{step description}}
|
||||
2. {{step description}}
|
||||
3. {{step description}}
|
||||
...
|
||||
|
||||
Each step: < 5 min, tests stay green, one change only.
|
||||
Estimated total: {{N}} micro-changes
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 4: Make ONE Change**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `3` to `stepsCompleted`
|
||||
- Append the plan to the log
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-04-make-change.md`.
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: 'step-04-make-change'
|
||||
description: 'Make ONE structural change — no behavior change'
|
||||
nextStepFile: './step-05-run-tests.md'
|
||||
referenceFiles:
|
||||
- 'references/functions/rules.md'
|
||||
- 'references/classes/rules.md'
|
||||
---
|
||||
|
||||
# Step 4: Make ONE Change
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Execute one small refactoring step. The change must be purely structural with no behavior change.
|
||||
|
||||
## REFERENCE LOADING
|
||||
|
||||
Before making changes, load the relevant reference files:
|
||||
- `references/functions/rules.md` — if refactoring functions
|
||||
- `references/classes/rules.md` — if refactoring classes
|
||||
|
||||
Apply the rules to guide the structural change.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### Checklist
|
||||
|
||||
- Make exactly ONE change
|
||||
- The change is purely structural (no behavior change)
|
||||
- You can describe the change in one sentence
|
||||
|
||||
### Common Refactorings
|
||||
|
||||
**Extract Method**:
|
||||
```typescript
|
||||
// Before
|
||||
function processOrder(order: Order) {
|
||||
// validate
|
||||
if (!order.items.length) throw new Error('Empty order');
|
||||
if (!order.customer) throw new Error('No customer');
|
||||
|
||||
// calculate
|
||||
const subtotal = order.items.reduce((sum, i) => sum + i.price, 0);
|
||||
const tax = subtotal * 0.1;
|
||||
const total = subtotal + tax;
|
||||
|
||||
// save
|
||||
db.save({ ...order, total });
|
||||
}
|
||||
|
||||
// After (one extraction)
|
||||
function processOrder(order: Order) {
|
||||
validateOrder(order); // Extracted
|
||||
|
||||
const subtotal = order.items.reduce((sum, i) => sum + i.price, 0);
|
||||
const tax = subtotal * 0.1;
|
||||
const total = subtotal + tax;
|
||||
|
||||
db.save({ ...order, total });
|
||||
}
|
||||
|
||||
function validateOrder(order: Order) {
|
||||
if (!order.items.length) throw new Error('Empty order');
|
||||
if (!order.customer) throw new Error('No customer');
|
||||
}
|
||||
```
|
||||
|
||||
**Rename**: Change a name to better reveal intent
|
||||
**Introduce Parameter Object**: Group related parameters
|
||||
**Move Method**: Move to the class that uses the data
|
||||
**Extract Class**: Split responsibilities
|
||||
|
||||
## PRESENT CHANGE
|
||||
|
||||
```
|
||||
Step 4: Change Made
|
||||
===================
|
||||
|
||||
Iteration: {{N}}
|
||||
Change: {{one-sentence description}}
|
||||
Type: {{Extract Method / Rename / etc.}}
|
||||
File: {{file:line}}
|
||||
Before: {{brief summary}}
|
||||
After: {{brief summary}}
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 5: Run Tests**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `4` to `stepsCompleted` (or update if looping)
|
||||
- Append the change description to the log
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-05-run-tests.md`.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
name: 'step-05-run-tests'
|
||||
description: 'Verify tests still pass after the change'
|
||||
nextStepFile: './step-06-commit.md'
|
||||
---
|
||||
|
||||
# Step 5: Run Tests
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Verify the change didn't break anything. All tests must still pass.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
### Check
|
||||
|
||||
- All tests pass
|
||||
- No new failures
|
||||
- Coverage hasn't dropped
|
||||
|
||||
### If Tests Fail
|
||||
|
||||
1. **STOP**
|
||||
2. Undo the change (`git checkout`)
|
||||
3. Make a smaller change
|
||||
4. Or fix the issue if it's obvious
|
||||
|
||||
**Important**: Do NOT proceed with failing tests. The safety net must stay intact.
|
||||
|
||||
## PRESENT RESULTS
|
||||
|
||||
```
|
||||
Step 5: Test Results
|
||||
====================
|
||||
|
||||
Iteration: {{N}}
|
||||
Result: PASS / FAIL
|
||||
Tests Run: {{N}}
|
||||
Tests Passed: {{N}}
|
||||
Tests Failed: {{N}} — [list if any]
|
||||
Coverage: {{percentage}} (change: +/-N%)
|
||||
|
||||
Action: {{proceed / undo and retry}}
|
||||
```
|
||||
|
||||
If PASS, ask: **[C] Continue to Step 6: Commit**
|
||||
|
||||
If FAIL, inform the user and undo the change. Return to `step-04-make-change.md` for a smaller change.
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `5` to `stepsCompleted` (or update if looping)
|
||||
- Set `testsGreen` to `true` or `false`
|
||||
- Append test results to the log
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
If tests pass and user confirms `[C]`, load `step-06-commit.md`.
|
||||
|
||||
If tests fail, load `step-04-make-change.md` after undoing the change.
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
name: 'step-06-commit'
|
||||
description: 'Commit the successful change'
|
||||
nextStepFile: './step-07-repeat.md'
|
||||
---
|
||||
|
||||
# Step 6: Commit
|
||||
|
||||
## STEP GOAL
|
||||
|
||||
Save progress by committing the successful refactoring step. Each green test should be committed.
|
||||
|
||||
## EXECUTION
|
||||
|
||||
### Commit the Change
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "refactor: {{one-sentence description of the change}}"
|
||||
```
|
||||
|
||||
### Checklist
|
||||
|
||||
- Commit after each successful step
|
||||
- Commit message describes the refactoring
|
||||
- You can revert to this point if needed
|
||||
|
||||
## PRESENT RESULT
|
||||
|
||||
```
|
||||
Step 6: Commit
|
||||
==============
|
||||
|
||||
Iteration: {{N}}
|
||||
Committed: YES
|
||||
Message: "refactor: {{description}}"
|
||||
SHA: {{commit hash}}
|
||||
```
|
||||
|
||||
Then ask: **[C] Continue to Step 7: Check if Done**
|
||||
|
||||
## FRONTMATTER UPDATE
|
||||
|
||||
Update the output document:
|
||||
- Add `6` to `stepsCompleted` (or update if looping)
|
||||
- Append commit details to the log
|
||||
|
||||
## NEXT STEP
|
||||
|
||||
After user confirms `[C]`, load `step-07-repeat.md`.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user