
October 29th was about discovering that good abstractions make systems composable.
I had built a workflow that worked beautifully for one context. Now I needed it to work for multiple contexts without duplicating everything.
The Problem: Similar But Different
I had two scenarios:
- Context A: Process files in one directory with base rules
- Context B: Process files in another directory with base rules plus context-specific additions
My first instinct: duplicate the entire workflow and modify the copy.
My second instinct: that’s terrible. There has to be a better way.
The Base + Extension Pattern
The insight came from realizing what was actually different between contexts versus what was shared.
Shared: 90% of the workflow logic
Different: Which files to process, plus a few context-specific rules
So I created parameterized base agents:
Base Agent (params: directory, file pattern, extensions)
↓
Does the core work using those parameters
Then context-specific agents would:
- Define their parameters
- Invoke the base agent with those parameters
- Add any context-specific logic
The base agent became reusable. Different contexts just passed different parameters.
Extension Pattern for Specialization
But parameters weren’t enough for some cases. Sometimes Context B needed to do something in addition to what the base did.
This is where extension came in:
Context-Specific Agent
1. Read base agent instructions
2. Apply everything from base
3. Add these additional steps: [...]
The context-specific agent extended the base rather than reimplementing it.
This pattern kept the base as the source of truth while allowing specialization.
Context-Agnostic Design
As I built these patterns, a principle emerged: agents shouldn’t know their callers or consumers.
Bad design:
Worker Agent
- Knows it's called by Manager X
- Formats output specifically for Manager X
- Makes assumptions about what Manager X will do next
Good design:
Worker Agent
- Receives inputs (doesn't care from whom)
- Performs work
- Returns outputs in standard format
Context-agnostic agents are reusable because they don’t couple to their environment.
The Command → Skill → Workflow Hierarchy
On Day 3, I also built the interface layer: commands.
The architecture became three-tiered:
Commands (user-facing shortcuts)
↓
Skills (reusable capabilities)
↓
Workflows (multi-agent orchestration)
Commands are what users type: /commit, /switch-branch
Skills are the implementation of those commands
Workflows are what skills invoke when they need multi-agent coordination
This separation meant I could:
- Change workflow implementation without changing the command interface
- Reuse skills across multiple commands
- Compose skills into new workflows
Safe Operations Pattern
One specific skill taught me something important: check before changing.
When switching branches in git, I need Claude to check for uncommitted changes first. If there are changes, I should get prompted for what to do (stash/commit/discard/cancel).
The pattern:
- Claude checks current state
- If state is unsafe for operation:
- Claude informs me
- Claude offers me options
- Claude waits for my decision
- Claude performs operation
- Claude verifies success
This became a skill and a template for any destructive or state-changing operation.
What I Learned About Abstraction
The right abstractions make systems:
- Composable (build new things from existing pieces)
- Maintainable (change in one place propagates correctly)
- Understandable (clear boundaries between components)
The wrong abstractions make systems:
- Rigid (can’t adapt to new contexts)
- Duplicative (copy-paste programming)
- Fragile (changes break unexpected things)
By the end of Day 3, I had patterns for reusability. But I still wasn’t satisfied with the overall architecture.
That night, I wrote 1,500 lines of planning documents.
Tomorrow, I’d rebuild everything from scratch.