Add initial slides for TDD workshop presentation with custom styles and navigation
This commit is contained in:
parent
3a4521f52a
commit
5b4df9ebd6
4 changed files with 1043 additions and 227 deletions
|
|
@ -1,10 +1,13 @@
|
|||
# Workshop Facilitator Guide — Quick Reference
|
||||
|
||||
**Duration:** 2 hours
|
||||
**Format:** Live demo + hands-on practice with Password Validator or Shopping Cart katas
|
||||
**Duration:** 80 minutes
|
||||
**Format:** Live demo + hands-on practice with Password Validator or Shopping Cart katas using 3-Way Ping-Pong (mob programming)
|
||||
**Team Size:** 3 people (2 developers + you as facilitator/participant)
|
||||
|
||||
> **Note:** For the complete detailed plan with minute-by-minute timing, scripts, and checklists, see **WORKSHOP_PLAN.md**. This guide focuses on facilitation tips and kata-specific insights.
|
||||
|
||||
> **Facilitator Role:** You'll participate as the 3rd person in the rotation, experiencing TDD alongside the developers. This creates a collaborative learning environment rather than traditional top-down teaching.
|
||||
|
||||
---
|
||||
|
||||
## Essential Documents
|
||||
|
|
@ -37,14 +40,11 @@ Before the workshop, familiarize yourself with:
|
|||
|
||||
| Time | Activity | See |
|
||||
|------|----------|-----|
|
||||
| 0:00-0:10 | Welcome & Setup Check | WORKSHOP_PLAN.md |
|
||||
| 0:10-0:25 | **Live Demo: FizzBuzz TDD** | fizzbuzz/DEMO_SCRIPT.md |
|
||||
| 0:25-0:30 | Exercise Introduction | Section below |
|
||||
| 0:30-1:15 | Hands-on Practice (Part 1) | Circulate & nudge |
|
||||
| 1:15-1:30 | Mid-Point Check-in | WORKSHOP_PLAN.md |
|
||||
| 1:30-2:00 | Hands-on Practice (Part 2) | Circulate & nudge |
|
||||
| 2:00-2:10 | Show & Tell | 2-3 volunteers |
|
||||
| 2:10-2:20 | Retrospective | WORKSHOP_PLAN.md |
|
||||
| 0:00-0:05 | Welcome & Setup Check | WORKSHOP_PLAN.md |
|
||||
| 0:05-0:20 | **Live Demo: FizzBuzz TDD** | fizzbuzz/DEMO_SCRIPT.md |
|
||||
| 0:20-0:25 | Exercise Intro (3-Way Ping-Pong) | Section below |
|
||||
| 0:25-1:05 | Hands-on Practice (Mob Programming) | Circulate & nudge |
|
||||
| 1:05-1:20 | Combined Retrospective | WORKSHOP_PLAN.md |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -77,17 +77,26 @@ Before the workshop, familiarize yourself with:
|
|||
|
||||
---
|
||||
|
||||
## What to watch for while circulating
|
||||
## What to watch for while participating
|
||||
|
||||
**Your dual role:** You're both a participant in the rotation AND a gentle guide.
|
||||
|
||||
**Good signs:**
|
||||
- Running `dart test` after every small change
|
||||
- Seeing RED before touching `lib/`
|
||||
- Short, focused commits (or at least talking through each step)
|
||||
- Smooth keyboard passing between the 3 roles (Red, Green, Refactor)
|
||||
|
||||
**Gentle interventions:**
|
||||
**Gentle interventions (when it's not your turn at keyboard):**
|
||||
- *"Have you run the test yet? What did it say?"* — nudge anyone writing too much code before testing
|
||||
- *"Can you make it pass with even less code?"* — push toward minimal Green
|
||||
- *"Now that it's Green, what could you clean up?"* — prompt the Refactor step
|
||||
- *"It's time to pass the keyboard! Let's rotate."* — enforce the rotation
|
||||
|
||||
**When it's your turn:**
|
||||
- Model the behavior you want to see (run tests frequently, minimal code, think aloud)
|
||||
- Ask questions rather than giving answers: *"What's the simplest thing that could work here?"*
|
||||
- Show vulnerability: *"Hmm, I'm not sure. What do you think?"*
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -142,7 +151,8 @@ The point is: TDD forces you to decide, and the test documents the decision perm
|
|||
1. *"What was the first test you wrote? Why that one?"*
|
||||
2. *"Did your design change as you added more tests? How?"*
|
||||
3. *"Did you ever feel tempted to skip the Red step and just write the code?"*
|
||||
4. *"What would have been different if you'd designed the class first, then written tests?"*
|
||||
4. *"How did the 3-way ping-pong dynamic change how you wrote code?"*
|
||||
5. *"What would have been different if you'd designed the class first, then written tests?"*
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
17
README.md
17
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# TDD Workshop
|
||||
|
||||
**A hands-on, 2-hour workshop for learning Test-Driven Development through deliberate practice.**
|
||||
**A hands-on, 80-minute workshop for learning Test-Driven Development through deliberate practice.**
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -11,8 +11,8 @@ This workshop teaches the RED-GREEN-REFACTOR cycle through:
|
|||
2. **Hands-on practice** (Password Validator or Shopping Cart)
|
||||
3. **Group reflection** and retrospective
|
||||
|
||||
**Target audience:** Developers familiar with TDD concepts but lacking hands-on practice
|
||||
**Duration:** 2 hours
|
||||
**Target audience:** 3-person team (2 developers + facilitator)
|
||||
**Duration:** 80 minutes
|
||||
**Language:** Dart
|
||||
|
||||
---
|
||||
|
|
@ -118,12 +118,11 @@ tdd-workshop/
|
|||
2. **Choose a kata:**
|
||||
- **Password Validator:** Rules-based validation, interesting refactoring
|
||||
- **Shopping Cart:** Stateful domain object, data structure decisions
|
||||
3. **Follow the workflow:**
|
||||
- Uncomment one test at a time
|
||||
- Make it RED (watch it fail)
|
||||
- Make it GREEN (minimal code to pass)
|
||||
- Refactor (clean up duplication)
|
||||
- Repeat
|
||||
3. **Follow the 3-Way Ping-Pong workflow:**
|
||||
- **Dev A (RED):** Uncomment one test, run it, watch it fail. Pass the keyboard.
|
||||
- **Dev B (GREEN):** Write minimal code to pass. Pass the keyboard.
|
||||
- **Dev C (REFACTOR):** Clean up the code. Write the next test (RED). Pass the keyboard.
|
||||
- Rotate roles and repeat
|
||||
|
||||
### Kata Instructions
|
||||
|
||||
|
|
|
|||
308
WORKSHOP_PLAN.md
308
WORKSHOP_PLAN.md
|
|
@ -1,25 +1,27 @@
|
|||
# TDD Workshop - Complete Plan
|
||||
|
||||
**Title:** Test-Driven Development Workshop: From Theory to Practice
|
||||
**Duration:** 2 hours (120 minutes)
|
||||
**Duration:** 80 minutes
|
||||
**Format:** In-person, hands-on
|
||||
**Target Audience:** Developers familiar with TDD concepts but lacking hands-on practice
|
||||
**Target Audience:** 3-person team (2 developers + facilitator)
|
||||
**Language:** Dart
|
||||
**Primary Goal:** Build muscle memory for the RED-GREEN-REFACTOR cycle through deliberate practice
|
||||
**Primary Goal:** Build muscle memory for the RED-GREEN-REFACTOR cycle through deliberate practice using 3-Way Ping-Pong (mob programming)
|
||||
|
||||
> **Facilitator Note:** You'll participate as the 3rd person in the rotation rather than observing from the sidelines. This creates a collaborative learning environment where you model TDD practices while gently guiding the team.
|
||||
|
||||
---
|
||||
|
||||
## Pre-Workshop Preparation
|
||||
|
||||
### 1 Week Before
|
||||
- [ ] Confirm participant count and send calendar invite
|
||||
- [x] Confirm participant count and send calendar invite
|
||||
- [ ] Share repository link with setup instructions (SETUP_GUIDE.md)
|
||||
- [ ] Ask participants to complete setup and verify `dart pub get` + `dart test` work
|
||||
- [ ] Prepare a backup laptop with environment pre-configured
|
||||
- [ ] Review this plan and the FACILITATOR_GUIDE.md
|
||||
- [x] Ask participants to complete setup and verify `dart pub get` + `dart test` work
|
||||
- [x] Prepare a backup laptop with environment pre-configured
|
||||
- [x] Review this plan and the FACILITATOR_GUIDE.md
|
||||
|
||||
### 1 Day Before
|
||||
- [ ] Practice FizzBuzz live demo (use DEMO_SCRIPT.md)
|
||||
- [x] Practice FizzBuzz live demo (use DEMO_SCRIPT.md)
|
||||
- [ ] Review password_validator and shopping_cart solution files
|
||||
- [ ] Test all katas on your machine (`dart test` in each directory)
|
||||
- [ ] Print this workshop plan as backup reference
|
||||
|
|
@ -59,20 +61,17 @@
|
|||
|
||||
| Start | Duration | Segment | Format |
|
||||
|-------|----------|---------|--------|
|
||||
| 0:00 | 10 min | Welcome & Setup Check | Facilitator-led |
|
||||
| 0:10 | 15 min | Live Demo: FizzBuzz TDD | Live-coding |
|
||||
| 0:25 | 5 min | Exercise Introduction | Facilitator-led |
|
||||
| 0:30 | 45 min | Hands-on Practice (Part 1) | Solo/pairs coding |
|
||||
| 1:15 | 15 min | Mid-Point Check-in | Group discussion |
|
||||
| 1:30 | 30 min | Hands-on Practice (Part 2) | Solo/pairs coding |
|
||||
| 2:00 | 10 min | Show & Tell | 2-3 volunteers |
|
||||
| 2:10 | 10 min | Retrospective Discussion | Facilitated |
|
||||
| 0:00 | 5 min | Welcome & Setup Check | Facilitator-led |
|
||||
| 0:05 | 15 min | Live Demo: FizzBuzz TDD | Live-coding |
|
||||
| 0:20 | 5 min | Exercise Introduction (3-Way Ping-Pong) | Facilitator-led |
|
||||
| 0:25 | 40 min | Hands-on Practice (Mob Programming) | Team coding |
|
||||
| 1:05 | 15 min | Combined Retrospective | Facilitated discussion |
|
||||
|
||||
---
|
||||
|
||||
## Segment-by-Segment Guide
|
||||
|
||||
### Segment 1: Welcome & Setup (0:00-0:10) — 10 minutes
|
||||
### Segment 1: Welcome & Setup (0:00-0:05) — 5 minutes
|
||||
|
||||
**Objectives:**
|
||||
- Ensure everyone can run tests
|
||||
|
|
@ -118,7 +117,7 @@ Let's see this in action.
|
|||
|
||||
---
|
||||
|
||||
### Segment 2: Live Demo - FizzBuzz (0:10-0:25) — 15 minutes
|
||||
### Segment 2: Live Demo - FizzBuzz (0:05-0:20) — 15 minutes
|
||||
|
||||
**Objectives:**
|
||||
- Demonstrate RED-GREEN-REFACTOR in real-time
|
||||
|
|
@ -144,16 +143,16 @@ Let's see this in action.
|
|||
- Participants see the rhythm clearly
|
||||
- At least one "aha!" moment visible (nodding, note-taking)
|
||||
|
||||
**Time Check:** Should finish by 0:25
|
||||
**Time Check:** Should finish by 0:20
|
||||
|
||||
---
|
||||
|
||||
### Segment 3: Exercise Introduction (0:25-0:30) — 5 minutes
|
||||
### Segment 3: Exercise Introduction (0:20-0:25) — 5 minutes
|
||||
|
||||
**Objectives:**
|
||||
- Explain the two kata choices
|
||||
- Clarify workflow
|
||||
- Get people started
|
||||
- Introduce 3-Way Ping-Pong workflow
|
||||
- Get the team started on a single shared screen
|
||||
|
||||
**Script:**
|
||||
```
|
||||
|
|
@ -169,61 +168,67 @@ SHOPPING CART:
|
|||
- You'll discover when to use Map vs List
|
||||
- Good for practicing: Value Objects, data structure decisions
|
||||
|
||||
Both are good. Pick what sounds interesting.
|
||||
Pick what sounds interesting to your team.
|
||||
|
||||
Workflow:
|
||||
1. Open the test file
|
||||
2. Uncomment ONE test group
|
||||
3. Run dart test - see it RED
|
||||
4. Write code to make it GREEN
|
||||
5. Refactor if needed
|
||||
6. Next test
|
||||
Today we're using "3-Way Ping-Pong" - a mob programming format that maps perfectly to RED-GREEN-REFACTOR:
|
||||
|
||||
Pairing optional - if you pair, try "ping-pong":
|
||||
- Person A writes test
|
||||
- Person B makes it pass
|
||||
- Switch roles
|
||||
1. Developer A (RED): Uncomment ONE test, run it, watch it fail. Pass the keyboard.
|
||||
2. Developer B (GREEN): Write minimal code to make it pass. Pass the keyboard.
|
||||
3. Developer C (REFACTOR): Clean up the code if needed. Then write the next test (uncomment it). Pass the keyboard back to Developer A.
|
||||
|
||||
Questions? Good. Choose your kata and start with Step 1!
|
||||
Rotate roles with each cycle. Everyone gets to experience all three phases.
|
||||
|
||||
Use one shared screen. The person with the keyboard is the "driver" - everyone else navigates.
|
||||
|
||||
Questions? Good. Choose your kata and let's start with Step 1!
|
||||
```
|
||||
|
||||
**Actions:**
|
||||
- [ ] Show README files on screen briefly
|
||||
- [ ] Start 45-minute timer
|
||||
- [ ] Let people move to pair up if desired
|
||||
- [ ] Ensure team has chosen one kata
|
||||
- [ ] Confirm they understand the rotation: A (Red) → B (Green) → C (Refactor + next Red) → A (Green) → ...
|
||||
- [ ] Start 40-minute timer
|
||||
|
||||
**What Success Looks Like:**
|
||||
- Everyone has chosen a kata
|
||||
- Files are open
|
||||
- First test is being uncommented
|
||||
- Team has chosen a kata
|
||||
- Files are open on shared screen
|
||||
- Roles are assigned (A, B, C)
|
||||
- First developer is ready to uncomment first test
|
||||
|
||||
---
|
||||
|
||||
### Segment 4: Hands-on Practice Part 1 (0:30-1:15) — 45 minutes
|
||||
### Segment 4: Hands-on Practice (0:25-1:05) — 40 minutes
|
||||
|
||||
**Objectives:**
|
||||
- Build RED-GREEN-REFACTOR muscle memory
|
||||
- Experience how tests drive design
|
||||
- Practice mob programming rotation
|
||||
- Encounter and resolve "stuck" moments
|
||||
|
||||
**Your Role:** Circulate, observe, nudge with questions (don't give solutions)
|
||||
**Your Role:** Participate as the 3rd person in the rotation. When it's your turn, model good TDD practices. When it's not your turn, gently guide with questions.
|
||||
|
||||
**Good Signs:**
|
||||
- Tests running frequently
|
||||
- Seeing RED before touching lib/
|
||||
- Small commits or at least incremental changes
|
||||
- Discussing design (if pairing)
|
||||
- Smooth keyboard passing between roles
|
||||
- Team discussing design together
|
||||
- Each person respecting their role (Red only writes test, Green only makes it pass, Refactor cleans up)
|
||||
|
||||
**Interventions (Use these phrases):**
|
||||
**When It's Your Turn at the Keyboard:**
|
||||
- Model the behavior: run tests frequently, write minimal code, think aloud
|
||||
- Show vulnerability: "Hmm, what's the simplest thing here?"
|
||||
- Narrate your thinking: "I'm going to run the test first to see it fail..."
|
||||
|
||||
**When Others Have the Keyboard (Gentle Guidance):**
|
||||
|
||||
| Situation | What to Say |
|
||||
|-----------|-------------|
|
||||
| Writing code without test | "Have you run the test yet? What did it say?" |
|
||||
| Multiple tests at once | "Let's focus on making just ONE test pass first" |
|
||||
| Overthinking | "What's the simplest thing that could work?" |
|
||||
| Not refactoring | "See any duplication? What could you extract?" |
|
||||
| Developer doing too much in their role | "Remember, you're on GREEN - just make it pass. We'll clean up in REFACTOR." |
|
||||
| Not refactoring | "See any duplication? What could we extract?" |
|
||||
| Stuck on data structure | "What makes 'find by name' really easy?" (Shopping Cart) |
|
||||
| Hardcoding too long | "Try another test with different input. What happens?" |
|
||||
| Keyboard not being passed | "It's time to pass the keyboard! Let's rotate." |
|
||||
| Overthinking | "What's the simplest thing that could work?" |
|
||||
|
||||
**Guiding Questions:**
|
||||
- "What's the next simplest test?"
|
||||
|
|
@ -233,161 +238,49 @@ Questions? Good. Choose your kata and start with Step 1!
|
|||
- "Why List vs Map here?" (Shopping Cart)
|
||||
|
||||
**Time Checks:**
|
||||
- **At 15 min (0:45):** Most should be on Step 2-3
|
||||
- **At 30 min (1:00):** Most should be on Step 3-4
|
||||
- **At 40 min (1:10):** Some on Step 5-6, some still on 3-4 (both fine)
|
||||
- **At 15 min (0:40):** Team should be on Step 2-3
|
||||
- **At 25 min (0:50):** Team should be on Step 3-4
|
||||
- **At 35 min (1:00):** Give heads up: "5 minutes left - finish your current test"
|
||||
|
||||
**What Success Looks Like:**
|
||||
- Steady typing and test running
|
||||
- Some "aha!" moments (facial expressions, discussions)
|
||||
- Steady rotation and test running
|
||||
- Team experiencing "aha!" moments together
|
||||
- A few stuck moments that resolve with nudging
|
||||
- Energy remains positive
|
||||
- Energy remains positive and collaborative
|
||||
- Team completes at least Steps 1-4
|
||||
|
||||
---
|
||||
|
||||
### Segment 5: Mid-Point Check-in (1:15-1:30) — 15 minutes
|
||||
|
||||
**Objectives:**
|
||||
- Surface common struggles
|
||||
- Share discoveries
|
||||
- Re-energize
|
||||
|
||||
**Script:**
|
||||
```
|
||||
Alright, pause for a moment. Stretch if you need to.
|
||||
|
||||
Quick progress check:
|
||||
- Who's completed Steps 1-2? [Show of hands]
|
||||
- Step 3-4? [Hands]
|
||||
- Step 5 or beyond? [Hands]
|
||||
|
||||
Good mix!
|
||||
|
||||
What surprised you so far?
|
||||
[Let 2-3 people share - listen for:]
|
||||
- How often tests run
|
||||
- Refactor step insights
|
||||
- Data structure discoveries (Map vs List)
|
||||
|
||||
Anyone get stuck? What helped you get unstuck?
|
||||
[Usually: reading test carefully, trying minimal solution]
|
||||
|
||||
Any design insights?
|
||||
[Password folks might mention: rules as functions
|
||||
Shopping Cart folks might mention: Value Objects]
|
||||
|
||||
Okay, 30 more minutes. Options:
|
||||
- Continue your current kata
|
||||
- If finished, try the other one
|
||||
- If deep in refactoring, keep going
|
||||
|
||||
The TDD_REFERENCE_CARD.md is available if you need a reminder on anything.
|
||||
|
||||
Let's go!
|
||||
```
|
||||
|
||||
**Actions:**
|
||||
- [ ] Capture themes on whiteboard
|
||||
- [ ] Validate their experiences ("Yes! That's exactly what TDD teaches")
|
||||
- [ ] Note time remaining
|
||||
|
||||
**What Success Looks Like:**
|
||||
- Participants feel heard and validated
|
||||
- Common struggles are normalized
|
||||
- Energy is refreshed for part 2
|
||||
|
||||
---
|
||||
|
||||
### Segment 6: Hands-on Practice Part 2 (1:30-2:00) — 30 minutes
|
||||
|
||||
**Objectives:**
|
||||
- Complete current kata OR start second one
|
||||
- Experience full cycle including refactoring
|
||||
|
||||
**Your Role:**
|
||||
- Continue circulating
|
||||
- Focus attention on people doing Step 6 (Password) or Value Objects (Cart)
|
||||
- Encourage those who finished to try the other kata
|
||||
|
||||
**Deep Dive Conversations:**
|
||||
|
||||
**Password Validator (Step 6):**
|
||||
```
|
||||
Them: "I have 5 if-blocks. Works but feels repetitive."
|
||||
You: "What if each rule was a function? What would that look like?"
|
||||
[Pause - let them think]
|
||||
You: "Each rule is: String → Optional Error, right? Could you make a list?"
|
||||
```
|
||||
|
||||
**Shopping Cart (Value Object):**
|
||||
```
|
||||
Them: "Should I validate price in ShoppingCart.addItem()?"
|
||||
You: "What if someone creates CartItem outside the cart? Who validates?"
|
||||
[Pause - let them think]
|
||||
You: "What if CartItem couldn't be created invalid? Where would validation go?"
|
||||
```
|
||||
|
||||
**Time Checks:**
|
||||
- **At 10 min (1:40):** Give heads up: "20 minutes left"
|
||||
- **At 20 min (1:50):** Give heads up: "10 minutes - finish your current test"
|
||||
- **At 28 min (1:58):** "2 minutes - get to a stopping point"
|
||||
|
||||
**What Success Looks Like:**
|
||||
- Most finish Step 4-5 or beyond
|
||||
- Some discover refactoring patterns
|
||||
- Code is messy but working (that's fine!)
|
||||
|
||||
---
|
||||
|
||||
### Segment 7: Show & Tell (2:00-2:10) — 10 minutes
|
||||
|
||||
**Objectives:**
|
||||
- See different approaches
|
||||
- Celebrate progress
|
||||
- Learn from peers
|
||||
|
||||
**Script:**
|
||||
```
|
||||
Time's up! Let's see what you built.
|
||||
|
||||
I'd like 2-3 volunteers to share briefly:
|
||||
1. Which tests are passing?
|
||||
2. Show your implementation
|
||||
3. ONE interesting decision you made
|
||||
|
||||
Who wants to go first?
|
||||
```
|
||||
|
||||
**What to Highlight:**
|
||||
- Test organization and naming
|
||||
- Simple → complex progression
|
||||
- Refactorings (if they reached Step 6)
|
||||
- Different valid approaches (e.g., replace vs accumulate in cart)
|
||||
|
||||
**Feedback Template:**
|
||||
```
|
||||
"I love that you [specific good practice].
|
||||
Look at how tests tell a story: [point to names].
|
||||
This design [refactoring] is the [pattern name] pattern."
|
||||
```
|
||||
|
||||
**What Success Looks Like:**
|
||||
- 2-3 people share
|
||||
- Variety in progress levels shown
|
||||
- Participants see multiple valid approaches
|
||||
|
||||
---
|
||||
|
||||
### Segment 8: Retrospective (2:10-2:20) — 10 minutes
|
||||
### Segment 5: Combined Retrospective (1:05-1:20) — 15 minutes
|
||||
|
||||
**Objectives:**
|
||||
- Solidify learning
|
||||
- Reflect on the 3-way rotation experience
|
||||
- Connect to daily work
|
||||
- Inspire continued practice
|
||||
|
||||
**Script:**
|
||||
```
|
||||
Great work! Let's reflect on what we just experienced.
|
||||
|
||||
Since you all worked on the same code together, let's discuss:
|
||||
|
||||
1. Which tests are passing now?
|
||||
2. Show your implementation - what does it look like?
|
||||
3. What interesting decisions did you make as a team?
|
||||
```
|
||||
|
||||
**Discussion Questions:**
|
||||
|
||||
**1. "What was different about writing tests FIRST vs AFTER?"**
|
||||
**1. "How did the 3-way ping-pong rotation help enforce TDD discipline?"**
|
||||
|
||||
Expected answers:
|
||||
- Forced us to stay in role (couldn't skip ahead)
|
||||
- Made the cycle explicit and visible
|
||||
- Kept us from writing too much code at once
|
||||
- Everyone had to understand what was happening
|
||||
|
||||
**2. "What was different about writing tests FIRST vs AFTER?"**
|
||||
|
||||
Expected answers:
|
||||
- Tests were easier to write
|
||||
|
|
@ -395,25 +288,26 @@ Expected answers:
|
|||
- Caught edge cases earlier
|
||||
- Felt slower at first, faster later
|
||||
|
||||
**2. "Did you feel tempted to skip the RED step?"**
|
||||
**3. "Did you feel tempted to skip the RED step?"**
|
||||
|
||||
Expected: "Yes!"
|
||||
|
||||
Your response: "That's the discipline. RED proves the test can fail."
|
||||
|
||||
**3. "What design insight surprised you?"**
|
||||
**4. "What design insight surprised you?"**
|
||||
|
||||
Reinforce:
|
||||
- Password: Rules as data (Open-Closed Principle)
|
||||
- Shopping Cart: Map for lookup, Value Objects for validation
|
||||
- General: Small steps → emergent design
|
||||
|
||||
**4. "How would you apply this at work?"**
|
||||
**5. "How would you apply this at work?"**
|
||||
|
||||
Let them think out loud:
|
||||
- "That pricing calculation module..."
|
||||
- "Our validation logic could be structured like this..."
|
||||
- "Next util function, test first"
|
||||
- "We could do mob programming for complex features"
|
||||
|
||||
**Closing Message:**
|
||||
```
|
||||
|
|
@ -426,6 +320,9 @@ Three takeaways:
|
|||
2. Small steps - one test at a time
|
||||
3. Refactor - don't skip this
|
||||
|
||||
The 3-way rotation you just experienced is a great way to learn TDD as a team.
|
||||
You can use this format for any new feature or complex problem.
|
||||
|
||||
Want more practice? The tdd-katas repo has 5 complete examples with commit history showing every step:
|
||||
[Show link on screen]
|
||||
|
||||
|
|
@ -433,12 +330,14 @@ Thanks for participating! Questions?
|
|||
```
|
||||
|
||||
**Actions:**
|
||||
- [ ] Capture key insights on whiteboard
|
||||
- [ ] Share link to tdd-katas repo
|
||||
- [ ] Share TDD_REFERENCE_CARD.md
|
||||
- [ ] Mention optional office hours if offering them
|
||||
|
||||
**What Success Looks Like:**
|
||||
- Clear takeaways articulated
|
||||
- Team reflects on both TDD and mob programming benefits
|
||||
- Participants know where to practice next
|
||||
- Positive energy to continue learning
|
||||
|
||||
|
|
@ -450,13 +349,11 @@ Thanks for participating! Questions?
|
|||
|
||||
| Time | Energy Level | Your Role |
|
||||
|------|-------------|-----------|
|
||||
| 0-10 min | High | Welcoming, enthusiastic |
|
||||
| 10-25 min | Focused | Demonstrating, teaching |
|
||||
| 25-30 min | Enthusiastic | Clarifying, motivating |
|
||||
| 30-1:15 | Calm | Supportive, circulating |
|
||||
| 1:15-1:30 | Re-energize | Celebrating, validating |
|
||||
| 1:30-2:00 | Encouraging | Nudging toward completion |
|
||||
| 2:00-2:20 | Reflective | Connecting dots, inspiring |
|
||||
| 0-5 min | High | Welcoming, enthusiastic |
|
||||
| 5-20 min | Focused | Demonstrating, teaching |
|
||||
| 20-25 min | Enthusiastic | Clarifying, motivating |
|
||||
| 25-1:05 | Collaborative | Participating in rotation, modeling TDD, gentle coaching |
|
||||
| 1:05-1:20 | Reflective | Celebrating, connecting dots, inspiring |
|
||||
|
||||
### Common Questions and Answers
|
||||
|
||||
|
|
@ -499,21 +396,22 @@ At workshop end, participants should:
|
|||
|
||||
### Running Behind Schedule
|
||||
|
||||
- **Skip:** Detailed mid-point discussion (keep it to 5 min)
|
||||
- **Skip:** Show & tell (or reduce to 1 person, 3 min)
|
||||
- **Keep:** Live demo and hands-on time
|
||||
- **Skip:** Detailed retrospective discussion (keep it to 10 min)
|
||||
- **Reduce:** Live demo to 12 minutes (skip one cycle)
|
||||
- **Keep:** Hands-on practice time (this is the core learning)
|
||||
|
||||
### Running Ahead of Schedule
|
||||
|
||||
- **Add:** 6th test to FizzBuzz demo (input 6 → "Fizz")
|
||||
- **Add:** Bonus questions in retrospective
|
||||
- **Add:** Quick discussion of when NOT to use TDD
|
||||
- **Extend:** Hands-on practice time
|
||||
|
||||
### Technical Difficulties
|
||||
|
||||
- **Network down:** Use USB backup of repo
|
||||
- **Projector fails:** Do demo at participant's screen, others gather
|
||||
- **Participant computer issues:** Pair them with someone or use backup laptop
|
||||
- **Projector fails:** Do demo at team's screen, gather around
|
||||
- **Computer issues:** Use backup laptop or share one machine
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
909
slides.html
Normal file
909
slides.html
Normal file
|
|
@ -0,0 +1,909 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TDD Workshop — 80 Minutes</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700;800&family=Archivo+Black&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--red: #FF3B30;
|
||||
--green: #34C759;
|
||||
--refactor: #007AFF;
|
||||
--bg-dark: #0A0A0A;
|
||||
--bg-light: #F5F5F0;
|
||||
--text-dark: #1A1A1A;
|
||||
--text-light: #FAFAFA;
|
||||
--accent: #FFD60A;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
background: var(--bg-dark);
|
||||
color: var(--text-light);
|
||||
overflow: hidden;
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
/* Custom Cursor */
|
||||
#cursor {
|
||||
position: fixed;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid var(--accent);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
z-index: 10000;
|
||||
transition: transform 0.15s ease, border-color 0.2s;
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
|
||||
#cursor.click {
|
||||
transform: scale(0.7);
|
||||
border-color: var(--green);
|
||||
}
|
||||
|
||||
/* Slide Container */
|
||||
.slides {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.slide {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 4rem;
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.slide.active {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.slide.prev {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
/* Slide Backgrounds */
|
||||
.slide-title { background: linear-gradient(135deg, var(--bg-dark) 0%, #1a1a2e 100%); }
|
||||
.slide-red { background: linear-gradient(135deg, #2D0A0A 0%, var(--bg-dark) 100%); }
|
||||
.slide-green { background: linear-gradient(135deg, #0A2D0F 0%, var(--bg-dark) 100%); }
|
||||
.slide-refactor { background: linear-gradient(135deg, #0A1A2D 0%, var(--bg-dark) 100%); }
|
||||
.slide-neutral { background: var(--bg-dark); }
|
||||
|
||||
/* Typography */
|
||||
h1 {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: clamp(3rem, 8vw, 7rem);
|
||||
line-height: 0.95;
|
||||
letter-spacing: -0.03em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 2rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h1 .highlight {
|
||||
display: inline-block;
|
||||
padding: 0.1em 0.3em;
|
||||
background: var(--accent);
|
||||
color: var(--bg-dark);
|
||||
transform: skewX(-5deg);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(2rem, 5vw, 4rem);
|
||||
font-weight: 800;
|
||||
margin-bottom: 1.5rem;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.6;
|
||||
padding: 0.5em 0.2em;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: clamp(1.5rem, 3vw, 2.5rem);
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
p, li {
|
||||
font-size: clamp(1rem, 2vw, 1.5rem);
|
||||
line-height: 1.6;
|
||||
opacity: 0.85;
|
||||
max-width: 50ch;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: clamp(1.2rem, 2.5vw, 2rem);
|
||||
opacity: 0.7;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* Cycle Visualization */
|
||||
.cycle {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
margin: 3rem 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cycle-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
animation: fadeInUp 0.6s ease forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cycle-step:nth-child(1) { animation-delay: 0.2s; }
|
||||
.cycle-step:nth-child(2) { animation-delay: 0.4s; }
|
||||
.cycle-step:nth-child(3) { animation-delay: 0.6s; }
|
||||
.cycle-step:nth-child(4) { animation-delay: 0.8s; }
|
||||
|
||||
.cycle-icon {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3.5rem;
|
||||
font-weight: 800;
|
||||
border: 4px solid;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cycle-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: skewX(-20deg);
|
||||
animation: shine 3s infinite;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.red-step .cycle-icon {
|
||||
background: var(--red);
|
||||
border-color: var(--red);
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.green-step .cycle-icon {
|
||||
background: var(--green);
|
||||
border-color: var(--green);
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.refactor-step .cycle-icon {
|
||||
background: var(--refactor);
|
||||
border-color: var(--refactor);
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.repeat-step .cycle-icon {
|
||||
background: transparent;
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cycle-label {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
.cycle-desc {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.7;
|
||||
text-align: center;
|
||||
max-width: 20ch;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 2rem;
|
||||
opacity: 0.4;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
/* Timeline */
|
||||
.timeline {
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
margin: 2rem 0;
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 1fr;
|
||||
gap: 2rem;
|
||||
padding: 1.5rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-left: 4px solid var(--accent);
|
||||
animation: slideInLeft 0.5s ease forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.timeline-item:nth-child(1) { animation-delay: 0.1s; }
|
||||
.timeline-item:nth-child(2) { animation-delay: 0.2s; }
|
||||
.timeline-item:nth-child(3) { animation-delay: 0.3s; }
|
||||
.timeline-item:nth-child(4) { animation-delay: 0.4s; }
|
||||
.timeline-item:nth-child(5) { animation-delay: 0.5s; }
|
||||
|
||||
.timeline-time {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.timeline-content h3 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.timeline-content p {
|
||||
font-size: 1rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Ping Pong Diagram */
|
||||
.ping-pong {
|
||||
display: flex;
|
||||
gap: 3rem;
|
||||
margin: 3rem 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dev-role {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
animation: fadeInUp 0.6s ease forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dev-role:nth-child(1) { animation-delay: 0.2s; }
|
||||
.dev-role:nth-child(2) { animation-delay: 0.5s; }
|
||||
.dev-role:nth-child(3) { animation-delay: 0.8s; }
|
||||
|
||||
.dev-avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
border: 4px solid;
|
||||
}
|
||||
|
||||
.dev-role:nth-child(1) .dev-avatar {
|
||||
background: var(--red);
|
||||
border-color: var(--red);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dev-role:nth-child(2) .dev-avatar {
|
||||
background: var(--green);
|
||||
border-color: var(--green);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dev-role:nth-child(3) .dev-avatar {
|
||||
background: var(--refactor);
|
||||
border-color: var(--refactor);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dev-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dev-task {
|
||||
font-size: 1rem;
|
||||
opacity: 0.7;
|
||||
text-align: center;
|
||||
max-width: 20ch;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 3rem;
|
||||
opacity: 0.5;
|
||||
animation: slideRight 1.5s infinite;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
ul {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding-left: 2rem;
|
||||
position: relative;
|
||||
animation: fadeInUp 0.5s ease forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
li:nth-child(1) { animation-delay: 0.1s; }
|
||||
li:nth-child(2) { animation-delay: 0.2s; }
|
||||
li:nth-child(3) { animation-delay: 0.3s; }
|
||||
li:nth-child(4) { animation-delay: 0.4s; }
|
||||
li:nth-child(5) { animation-delay: 0.5s; }
|
||||
|
||||
li::before {
|
||||
content: '▸';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--accent);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
/* Progress Bar */
|
||||
.progress {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background: var(--accent);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
/* Slide Counter */
|
||||
.counter {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
opacity: 0.5;
|
||||
z-index: 1000;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Navigation Hint */
|
||||
.nav-hint {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
left: 2rem;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.4;
|
||||
z-index: 1000;
|
||||
animation: fadeIn 1s ease 2s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
to { opacity: 0.4; }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
50% { transform: translateX(10px); }
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
to { left: 100%; }
|
||||
}
|
||||
|
||||
/* Code Block */
|
||||
.code-block {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin: 2rem 0;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.8;
|
||||
max-width: 600px;
|
||||
animation: fadeInUp 0.6s ease 0.3s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.code-comment {
|
||||
color: #6A9955;
|
||||
}
|
||||
|
||||
.code-keyword {
|
||||
color: #569CD6;
|
||||
}
|
||||
|
||||
.code-string {
|
||||
color: #CE9178;
|
||||
}
|
||||
|
||||
/* Big Number */
|
||||
.big-number {
|
||||
font-size: clamp(8rem, 20vw, 15rem);
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
color: var(--accent);
|
||||
opacity: 0.2;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.content-over {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 90%;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
/* Phase indicator circles */
|
||||
.phase-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.3em;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -0.05em;
|
||||
}
|
||||
|
||||
.phase-red {
|
||||
background: var(--red);
|
||||
box-shadow: 0 0 20px rgba(255, 59, 48, 0.5);
|
||||
}
|
||||
|
||||
.phase-green {
|
||||
background: var(--green);
|
||||
box-shadow: 0 0 20px rgba(52, 199, 89, 0.5);
|
||||
}
|
||||
|
||||
.phase-blue {
|
||||
background: var(--refactor);
|
||||
box-shadow: 0 0 20px rgba(0, 122, 255, 0.5);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.cycle {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.ping-pong {
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.arrow, .flow-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="cursor"></div>
|
||||
|
||||
<div class="slides">
|
||||
<!-- Slide 1: Title -->
|
||||
<div class="slide active slide-title">
|
||||
<div class="content-over">
|
||||
<h1>
|
||||
<span class="highlight">TDD</span><br>
|
||||
WORKSHOP
|
||||
</h1>
|
||||
<p class="subtitle">80 minutes · 3-Way Ping-Pong · Hands-on Practice</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 2: The Cycle -->
|
||||
<div class="slide slide-neutral">
|
||||
<div class="content-over">
|
||||
<h2>The TDD Rhythm</h2>
|
||||
<div class="cycle">
|
||||
<div class="cycle-step red-step">
|
||||
<div class="cycle-icon">1</div>
|
||||
<div class="cycle-label">Red</div>
|
||||
<div class="cycle-desc">Write failing test</div>
|
||||
</div>
|
||||
<div class="arrow">→</div>
|
||||
<div class="cycle-step green-step">
|
||||
<div class="cycle-icon">2</div>
|
||||
<div class="cycle-label">Green</div>
|
||||
<div class="cycle-desc">Make it pass</div>
|
||||
</div>
|
||||
<div class="arrow">→</div>
|
||||
<div class="cycle-step refactor-step">
|
||||
<div class="cycle-icon">3</div>
|
||||
<div class="cycle-label">Refactor</div>
|
||||
<div class="cycle-desc">Clean up code</div>
|
||||
</div>
|
||||
<div class="arrow">→</div>
|
||||
<div class="cycle-step repeat-step">
|
||||
<div class="cycle-icon">↻</div>
|
||||
<div class="cycle-label">Repeat</div>
|
||||
<div class="cycle-desc">Next test</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 3: Schedule -->
|
||||
<div class="slide slide-neutral">
|
||||
<div class="big-number">80</div>
|
||||
<div class="content-over">
|
||||
<h2>Today's Schedule</h2>
|
||||
<div class="timeline">
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">0:00</div>
|
||||
<div class="timeline-content">
|
||||
<h3>Welcome & Setup</h3>
|
||||
<p>5 minutes · Verify environment</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">0:05</div>
|
||||
<div class="timeline-content">
|
||||
<h3>Live Demo: FizzBuzz</h3>
|
||||
<p>15 minutes · Watch TDD in action</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">0:20</div>
|
||||
<div class="timeline-content">
|
||||
<h3>Exercise Intro</h3>
|
||||
<p>5 minutes · 3-Way Ping-Pong explained</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">0:25</div>
|
||||
<div class="timeline-content">
|
||||
<h3>Hands-on Practice</h3>
|
||||
<p>40 minutes · Mob programming</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-time">1:05</div>
|
||||
<div class="timeline-content">
|
||||
<h3>Retrospective</h3>
|
||||
<p>15 minutes · Reflect & discuss</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 4: 3-Way Ping-Pong -->
|
||||
<div class="slide slide-neutral">
|
||||
<div class="content-over">
|
||||
<h2>3-Way Ping-Pong</h2>
|
||||
<div class="ping-pong">
|
||||
<div class="dev-role">
|
||||
<div class="dev-avatar">A</div>
|
||||
<div class="dev-name">Red</div>
|
||||
<div class="dev-task">Uncomment test<br>Watch it fail</div>
|
||||
</div>
|
||||
<div class="flow-arrow">→</div>
|
||||
<div class="dev-role">
|
||||
<div class="dev-avatar">B</div>
|
||||
<div class="dev-name">Green</div>
|
||||
<div class="dev-task">Write minimal code<br>Make it pass</div>
|
||||
</div>
|
||||
<div class="flow-arrow">→</div>
|
||||
<div class="dev-role">
|
||||
<div class="dev-avatar">C</div>
|
||||
<div class="dev-name">Refactor</div>
|
||||
<div class="dev-task">Clean up<br>Next test</div>
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin-top: 3rem; text-align: center; opacity: 0.7;">
|
||||
Rotate roles with each cycle · One shared screen
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 5: RED Phase -->
|
||||
<div class="slide slide-red">
|
||||
<div class="content-over">
|
||||
<h2 style="color: var(--red);"><span class="phase-indicator phase-red"></span>RED Phase</h2>
|
||||
<h3>Write a failing test</h3>
|
||||
<ul>
|
||||
<li>Uncomment ONE test</li>
|
||||
<li>Run <code>dart test</code></li>
|
||||
<li>Watch it fail (proves it can fail)</li>
|
||||
<li>Pass the keyboard</li>
|
||||
</ul>
|
||||
<div class="code-block">
|
||||
<span class="code-comment">// Uncomment this test</span><br>
|
||||
<span class="code-keyword">test</span>(<span class="code-string">'returns 1 for input 1'</span>, () {<br>
|
||||
<span class="code-keyword">expect</span>(fizzbuzz(<span class="code-string">1</span>), <span class="code-string">'1'</span>);<br>
|
||||
});
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 6: GREEN Phase -->
|
||||
<div class="slide slide-green">
|
||||
<div class="content-over">
|
||||
<h2 style="color: var(--green);"><span class="phase-indicator phase-green"></span>GREEN Phase</h2>
|
||||
<h3>Make it pass (minimal code)</h3>
|
||||
<ul>
|
||||
<li>Write the simplest code that works</li>
|
||||
<li>Don't worry about perfection</li>
|
||||
<li>Run tests to confirm GREEN</li>
|
||||
<li>Pass the keyboard</li>
|
||||
</ul>
|
||||
<div class="code-block">
|
||||
<span class="code-keyword">String</span> fizzbuzz(<span class="code-keyword">int</span> n) {<br>
|
||||
<span class="code-keyword">return</span> <span class="code-string">'1'</span>; <span class="code-comment">// Hardcode is OK!</span><br>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 7: REFACTOR Phase -->
|
||||
<div class="slide slide-refactor">
|
||||
<div class="content-over">
|
||||
<h2 style="color: var(--refactor);"><span class="phase-indicator phase-blue"></span>REFACTOR Phase</h2>
|
||||
<h3>Clean up the code</h3>
|
||||
<ul>
|
||||
<li>Remove duplication</li>
|
||||
<li>Improve names</li>
|
||||
<li>Tests stay GREEN</li>
|
||||
<li>Uncomment next test (back to RED)</li>
|
||||
<li>Pass the keyboard</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 8: Kata Choices -->
|
||||
<div class="slide slide-neutral">
|
||||
<div class="content-over">
|
||||
<h2>Choose Your Kata</h2>
|
||||
<div style="display: grid; gap: 2rem; margin-top: 2rem;">
|
||||
<div style="padding: 2rem; background: rgba(255, 255, 255, 0.05); border-left: 4px solid var(--accent);">
|
||||
<h3>Password Validator</h3>
|
||||
<p>Rules-based validation · Refactoring challenge · Open-Closed Principle</p>
|
||||
</div>
|
||||
<div style="padding: 2rem; background: rgba(255, 255, 255, 0.05); border-left: 4px solid var(--green);">
|
||||
<h3>Shopping Cart</h3>
|
||||
<p>Stateful domain object · Data structures · Value Objects</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 9: Key Principles -->
|
||||
<div class="slide slide-neutral">
|
||||
<div class="content-over">
|
||||
<h2>TDD Principles</h2>
|
||||
<ul>
|
||||
<li><strong>Tests first</strong> — Even for "obvious" code</li>
|
||||
<li><strong>Small steps</strong> — One test at a time</li>
|
||||
<li><strong>See RED</strong> — Proves the test can fail</li>
|
||||
<li><strong>Minimal GREEN</strong> — Hardcoding is OK initially</li>
|
||||
<li><strong>Refactor fearlessly</strong> — Tests protect you</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 10: Questions -->
|
||||
<div class="slide slide-title">
|
||||
<div class="content-over">
|
||||
<h1>
|
||||
Ready to<br>
|
||||
<span class="highlight">Practice?</span>
|
||||
</h1>
|
||||
<p class="subtitle">Let's build muscle memory for TDD</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div class="progress">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
|
||||
<!-- Slide Counter -->
|
||||
<div class="counter">
|
||||
<span id="current">1</span> / <span id="total">10</span>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Hint -->
|
||||
<div class="nav-hint">
|
||||
← → arrows or click to navigate
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Custom cursor
|
||||
const cursor = document.getElementById('cursor');
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
cursor.style.left = e.clientX + 'px';
|
||||
cursor.style.top = e.clientY + 'px';
|
||||
});
|
||||
|
||||
document.addEventListener('mousedown', () => {
|
||||
cursor.classList.add('click');
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
cursor.classList.remove('click');
|
||||
});
|
||||
|
||||
// Slide navigation
|
||||
const slides = document.querySelectorAll('.slide');
|
||||
const progressBar = document.querySelector('.progress-bar');
|
||||
const currentCounter = document.getElementById('current');
|
||||
const totalCounter = document.getElementById('total');
|
||||
|
||||
let currentSlide = 0;
|
||||
const totalSlides = slides.length;
|
||||
|
||||
totalCounter.textContent = totalSlides;
|
||||
|
||||
function updateSlide() {
|
||||
slides.forEach((slide, index) => {
|
||||
slide.classList.remove('active', 'prev');
|
||||
if (index === currentSlide) {
|
||||
slide.classList.add('active');
|
||||
} else if (index < currentSlide) {
|
||||
slide.classList.add('prev');
|
||||
}
|
||||
});
|
||||
|
||||
// Update progress bar
|
||||
const progress = ((currentSlide + 1) / totalSlides) * 100;
|
||||
progressBar.style.width = progress + '%';
|
||||
|
||||
// Update counter
|
||||
currentCounter.textContent = currentSlide + 1;
|
||||
}
|
||||
|
||||
function nextSlide() {
|
||||
if (currentSlide < totalSlides - 1) {
|
||||
currentSlide++;
|
||||
updateSlide();
|
||||
}
|
||||
}
|
||||
|
||||
function prevSlide() {
|
||||
if (currentSlide > 0) {
|
||||
currentSlide--;
|
||||
updateSlide();
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard navigation
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowRight' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
nextSlide();
|
||||
} else if (e.key === 'ArrowLeft') {
|
||||
e.preventDefault();
|
||||
prevSlide();
|
||||
} else if (e.key === 'Home') {
|
||||
currentSlide = 0;
|
||||
updateSlide();
|
||||
} else if (e.key === 'End') {
|
||||
currentSlide = totalSlides - 1;
|
||||
updateSlide();
|
||||
}
|
||||
});
|
||||
|
||||
// Click navigation
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.clientX > window.innerWidth / 2) {
|
||||
nextSlide();
|
||||
} else {
|
||||
prevSlide();
|
||||
}
|
||||
});
|
||||
|
||||
// Touch navigation
|
||||
let touchStartX = 0;
|
||||
document.addEventListener('touchstart', (e) => {
|
||||
touchStartX = e.touches[0].clientX;
|
||||
});
|
||||
|
||||
document.addEventListener('touchend', (e) => {
|
||||
const touchEndX = e.changedTouches[0].clientX;
|
||||
const diff = touchStartX - touchEndX;
|
||||
|
||||
if (Math.abs(diff) > 50) {
|
||||
if (diff > 0) {
|
||||
nextSlide();
|
||||
} else {
|
||||
prevSlide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
updateSlide();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue