added base examples and drills

This commit is contained in:
2026-05-20 17:42:01 -06:00
parent 9bf788d9ef
commit 4fc0d851e7
40 changed files with 802 additions and 1 deletions
+18
View File
@@ -0,0 +1,18 @@
# example-seam-drill
This kata is a placeholder example showing the intended practice layout.
## Structure
- `original/`: the frozen messy source you copy from for each drill
- `scenarios/<scenario-name>/code/`: a working copy for one drill attempt
- `scenarios/<scenario-name>/drill-prompt.md`: the drill instructions
- `scenarios/<scenario-name>/notes.md`: what you observed, tried, and learned
## Suggested workflow
1. Put the messy starting code in `original/`.
2. Copy it into a scenario's `code/` directory.
3. Do the drill only inside that scenario copy.
4. Record seam choices, tests added, and refactor steps in `notes.md`.
5. Reset by creating a new scenario from `original/`.
@@ -0,0 +1,17 @@
# original
This is intentionally messy starter code.
## Why it is messy
- mixes file I/O, business rules, time, and logging in one method
- uses raw strings and generic objects instead of stronger primitives
- hard-codes reward logic inline
- depends directly on `new Date()` and `fs`
- mutates loaded state directly
## Suggested drill targets
- characterize current completion behavior
- break the file-system or clock dependency with a seam
- extract the points or badge decision into a pure function
@@ -0,0 +1,12 @@
{
"name": "example-seam-drill",
"version": "1.0.0",
"private": true,
"type": "commonjs",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^29.7.0"
}
}
@@ -0,0 +1,62 @@
const fs = require("fs");
class TaskService {
constructor(filePath) {
this.filePath = filePath;
}
completeTask(taskName, userName) {
const raw = fs.readFileSync(this.filePath, "utf8");
const state = JSON.parse(raw);
const task = state.tasks.find((item) => item.name === taskName);
if (!task) {
throw new Error("Task not found");
}
if (task.completed) {
return {
message: "Task already complete",
pointsAwarded: 0,
badge: null
};
}
task.completed = true;
task.completedAt = new Date().toISOString();
let pointsAwarded = 10;
if (userName && userName.toLowerCase() === "ada") {
pointsAwarded = pointsAwarded + 5;
}
if (new Date().getDay() === 5) {
pointsAwarded = pointsAwarded * 2;
}
state.points = state.points + pointsAwarded;
let badge = null;
if (state.points > 50) {
badge = "consistency-star";
}
fs.writeFileSync(this.filePath, JSON.stringify(state, null, 2));
if (badge) {
console.log("Awarded badge:", badge);
}
return {
message: "Task completed",
pointsAwarded,
badge
};
}
}
module.exports = {
TaskService
};
@@ -0,0 +1,15 @@
{
"points": 45,
"tasks": [
{
"name": "Write daily summary",
"completed": false,
"completedAt": null
},
{
"name": "Review inbox",
"completed": true,
"completedAt": "2026-05-18T08:00:00.000Z"
}
]
}
@@ -0,0 +1,17 @@
# original
This is intentionally messy starter code.
## Why it is messy
- mixes file I/O, business rules, time, and logging in one method
- uses raw strings and generic objects instead of stronger primitives
- hard-codes reward logic inline
- depends directly on `new Date()` and `fs`
- mutates loaded state directly
## Suggested drill targets
- characterize current completion behavior
- break the file-system or clock dependency with a seam
- extract the points or badge decision into a pure function
@@ -0,0 +1,12 @@
{
"name": "example-seam-drill",
"version": "1.0.0",
"private": true,
"type": "commonjs",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^29.7.0"
}
}
@@ -0,0 +1,62 @@
const fs = require("fs");
class TaskService {
constructor(filePath) {
this.filePath = filePath;
}
completeTask(taskName, userName) {
const raw = fs.readFileSync(this.filePath, "utf8");
const state = JSON.parse(raw);
const task = state.tasks.find((item) => item.name === taskName);
if (!task) {
throw new Error("Task not found");
}
if (task.completed) {
return {
message: "Task already complete",
pointsAwarded: 0,
badge: null
};
}
task.completed = true;
task.completedAt = new Date().toISOString();
let pointsAwarded = 10;
if (userName && userName.toLowerCase() === "ada") {
pointsAwarded = pointsAwarded + 5;
}
if (new Date().getDay() === 5) {
pointsAwarded = pointsAwarded * 2;
}
state.points = state.points + pointsAwarded;
let badge = null;
if (state.points > 50) {
badge = "consistency-star";
}
fs.writeFileSync(this.filePath, JSON.stringify(state, null, 2));
if (badge) {
console.log("Awarded badge:", badge);
}
return {
message: "Task completed",
pointsAwarded,
badge
};
}
}
module.exports = {
TaskService
};
@@ -0,0 +1,15 @@
{
"points": 45,
"tasks": [
{
"name": "Write daily summary",
"completed": false,
"completedAt": null
},
{
"name": "Review inbox",
"completed": true,
"completedAt": "2026-05-18T08:00:00.000Z"
}
]
}
@@ -0,0 +1,12 @@
# Drill: break-dependency
## Goal
Introduce one seam that reduces coupling to a hard dependency.
## Prompt
Find one hard dependency in `code/` such as time, randomness, persistence, network, UI, or global state. Introduce the smallest seam you can so that behavior can be controlled more easily in tests.
## Success conditions
- One dependency is isolated behind a seam
- The change is small and reviewable
- You can explain the enabling point and why it is safer to change now
@@ -0,0 +1,9 @@
# Notes
## Dependency chosen
## Seam introduced
## Why this seam
## What I learned
@@ -0,0 +1,17 @@
# original
This is intentionally messy starter code.
## Why it is messy
- mixes file I/O, business rules, time, and logging in one method
- uses raw strings and generic objects instead of stronger primitives
- hard-codes reward logic inline
- depends directly on `new Date()` and `fs`
- mutates loaded state directly
## Suggested drill targets
- characterize current completion behavior
- break the file-system or clock dependency with a seam
- extract the points or badge decision into a pure function
@@ -0,0 +1,12 @@
{
"name": "example-seam-drill",
"version": "1.0.0",
"private": true,
"type": "commonjs",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^29.7.0"
}
}
@@ -0,0 +1,62 @@
const fs = require("fs");
class TaskService {
constructor(filePath) {
this.filePath = filePath;
}
completeTask(taskName, userName) {
const raw = fs.readFileSync(this.filePath, "utf8");
const state = JSON.parse(raw);
const task = state.tasks.find((item) => item.name === taskName);
if (!task) {
throw new Error("Task not found");
}
if (task.completed) {
return {
message: "Task already complete",
pointsAwarded: 0,
badge: null
};
}
task.completed = true;
task.completedAt = new Date().toISOString();
let pointsAwarded = 10;
if (userName && userName.toLowerCase() === "ada") {
pointsAwarded = pointsAwarded + 5;
}
if (new Date().getDay() === 5) {
pointsAwarded = pointsAwarded * 2;
}
state.points = state.points + pointsAwarded;
let badge = null;
if (state.points > 50) {
badge = "consistency-star";
}
fs.writeFileSync(this.filePath, JSON.stringify(state, null, 2));
if (badge) {
console.log("Awarded badge:", badge);
}
return {
message: "Task completed",
pointsAwarded,
badge
};
}
}
module.exports = {
TaskService
};
@@ -0,0 +1,15 @@
{
"points": 45,
"tasks": [
{
"name": "Write daily summary",
"completed": false,
"completedAt": null
},
{
"name": "Review inbox",
"completed": true,
"completedAt": "2026-05-18T08:00:00.000Z"
}
]
}
@@ -0,0 +1,12 @@
# Drill: characterization
## Goal
Capture current behavior before changing the code.
## Prompt
Add characterization tests for the current behavior of the code in `code/` without intentionally changing behavior.
## Success conditions
- At least one important behavior is captured in tests
- You can describe what the code currently does, even if the behavior is awkward
- No production behavior changes are introduced on purpose
@@ -0,0 +1,9 @@
# Notes
## Observed behavior
## Seams discovered
## Tests added
## What I learned
@@ -0,0 +1,17 @@
# original
This is intentionally messy starter code.
## Why it is messy
- mixes file I/O, business rules, time, and logging in one method
- uses raw strings and generic objects instead of stronger primitives
- hard-codes reward logic inline
- depends directly on `new Date()` and `fs`
- mutates loaded state directly
## Suggested drill targets
- characterize current completion behavior
- break the file-system or clock dependency with a seam
- extract the points or badge decision into a pure function
@@ -0,0 +1,12 @@
{
"name": "example-seam-drill",
"version": "1.0.0",
"private": true,
"type": "commonjs",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^29.7.0"
}
}
@@ -0,0 +1,62 @@
const fs = require("fs");
class TaskService {
constructor(filePath) {
this.filePath = filePath;
}
completeTask(taskName, userName) {
const raw = fs.readFileSync(this.filePath, "utf8");
const state = JSON.parse(raw);
const task = state.tasks.find((item) => item.name === taskName);
if (!task) {
throw new Error("Task not found");
}
if (task.completed) {
return {
message: "Task already complete",
pointsAwarded: 0,
badge: null
};
}
task.completed = true;
task.completedAt = new Date().toISOString();
let pointsAwarded = 10;
if (userName && userName.toLowerCase() === "ada") {
pointsAwarded = pointsAwarded + 5;
}
if (new Date().getDay() === 5) {
pointsAwarded = pointsAwarded * 2;
}
state.points = state.points + pointsAwarded;
let badge = null;
if (state.points > 50) {
badge = "consistency-star";
}
fs.writeFileSync(this.filePath, JSON.stringify(state, null, 2));
if (badge) {
console.log("Awarded badge:", badge);
}
return {
message: "Task completed",
pointsAwarded,
badge
};
}
}
module.exports = {
TaskService
};
@@ -0,0 +1,15 @@
{
"points": 45,
"tasks": [
{
"name": "Write daily summary",
"completed": false,
"completedAt": null
},
{
"name": "Review inbox",
"completed": true,
"completedAt": "2026-05-18T08:00:00.000Z"
}
]
}
@@ -0,0 +1,12 @@
# Drill: extract-policy
## Goal
Move one mixed rule out of orchestration code into a pure decision.
## Prompt
Find logic in `code/` that mixes decision-making with mechanics. Extract the decision into a pure function or policy with explicit inputs and outputs.
## Success conditions
- The decision is separated from mechanics
- Inputs and outputs are explicit
- You can describe the command, event, and workflow role of the affected code
@@ -0,0 +1,9 @@
# Notes
## Mixed logic found
## Pure decision extracted
## Command / event / workflow mapping
## What I learned