189 lines
6.9 KiB
Markdown
189 lines
6.9 KiB
Markdown
|
|
---
|
||
|
|
name: design-an-interface
|
||
|
|
description: "Backend Agent uses this skill to explore multiple API interface design options. Based on 'Design It Twice' principle, generate multiple distinct designs, compare, and select the best option. Trigger: API design phase (Stage 4), called by be-api-design skill."
|
||
|
|
---
|
||
|
|
|
||
|
|
# /design-an-interface — Interface Design Exploration
|
||
|
|
|
||
|
|
Backend Agent uses this skill to explore API interface design options.
|
||
|
|
|
||
|
|
Based on "Design It Twice" principle from "A Philosophy of Software Design": The first idea is usually not the best. Generate multiple distinct designs, then compare and choose.
|
||
|
|
|
||
|
|
## Responsibilities
|
||
|
|
|
||
|
|
1. Generate 2-3 distinct interface design options for module requirements
|
||
|
|
2. Each option uses different design constraints
|
||
|
|
3. Compare pros and cons of options
|
||
|
|
4. Assist in selecting or synthesizing the best option
|
||
|
|
|
||
|
|
## Input
|
||
|
|
|
||
|
|
- Module description (functional requirements from PRD)
|
||
|
|
- User stories and usage scenarios
|
||
|
|
|
||
|
|
## Output
|
||
|
|
|
||
|
|
- Multiple interface design options (including interface signatures, usage examples, pros/cons analysis)
|
||
|
|
- Option comparison and recommendations
|
||
|
|
|
||
|
|
## Flow
|
||
|
|
|
||
|
|
```
|
||
|
|
Collect requirements
|
||
|
|
↓
|
||
|
|
Generate 2-3 design options (parallel sub-agents)
|
||
|
|
↓
|
||
|
|
Present each option
|
||
|
|
↓
|
||
|
|
Compare options (interface simplicity, generality, implementation efficiency, depth)
|
||
|
|
↓
|
||
|
|
Synthesize best option or select most suitable
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step Details
|
||
|
|
|
||
|
|
**1. Collect Requirements**
|
||
|
|
|
||
|
|
Before designing, understand:
|
||
|
|
- [ ] What problem does this module solve?
|
||
|
|
- [ ] Who are the callers? (other modules, external users, tests)
|
||
|
|
- [ ] What are the key operations?
|
||
|
|
- [ ] What are the constraints? (performance, compatibility, existing patterns)
|
||
|
|
- [ ] What should be hidden internally? What should be exposed externally?
|
||
|
|
|
||
|
|
Ask: "What does this module need to do? Who will use it?"
|
||
|
|
|
||
|
|
**2. Generate Design Options (Parallel Sub-Agents)**
|
||
|
|
|
||
|
|
Generate 3+ distinct options simultaneously. Each option must follow different constraints:
|
||
|
|
|
||
|
|
```
|
||
|
|
Option A: Minimize method count — target 1-3 methods
|
||
|
|
Option B: Maximize flexibility — support multiple use cases
|
||
|
|
Option C: Optimize for most common operations
|
||
|
|
Option D (optional): Reference specific paradigm or framework design
|
||
|
|
```
|
||
|
|
|
||
|
|
Each option should include:
|
||
|
|
1. Interface signature (types/methods)
|
||
|
|
2. Usage examples (how callers actually use it)
|
||
|
|
3. What complexity this design hides
|
||
|
|
4. Trade-offs of this option
|
||
|
|
|
||
|
|
**3. Present Options**
|
||
|
|
|
||
|
|
Present each option one by one, including:
|
||
|
|
- Interface signature: types, methods, params
|
||
|
|
- Usage examples: how callers actually use it
|
||
|
|
- Hidden complexity: small interface hiding large implementation (good) vs large interface with thin implementation (bad)
|
||
|
|
|
||
|
|
Allow user to fully absorb each option before comparison.
|
||
|
|
|
||
|
|
**4. Compare Options**
|
||
|
|
|
||
|
|
Compare across dimensions:
|
||
|
|
|
||
|
|
- **Interface simplicity**: Few methods, simple parameters → Easier to learn and use correctly
|
||
|
|
- **Generality vs Specificity**: Flexibility vs focus, where are the trade-offs
|
||
|
|
- **Implementation efficiency**: Does interface shape allow efficient internal implementation?
|
||
|
|
- **Depth**: Small interface hiding large complexity (deep module, good) vs large interface with thin implementation (shallow module, bad)
|
||
|
|
- **Ease of correct usage vs ease of misuse**
|
||
|
|
|
||
|
|
Discuss trade-offs in text, not just tables. Emphasize where options diverge most.
|
||
|
|
|
||
|
|
**5. Synthesize Best Option**
|
||
|
|
|
||
|
|
The best design often combines insights from multiple options. Ask:
|
||
|
|
- "Which option fits your primary use case best?"
|
||
|
|
- "Are there elements from other options worth incorporating?"
|
||
|
|
|
||
|
|
## Evaluation Criteria
|
||
|
|
|
||
|
|
From "A Philosophy of Software Design":
|
||
|
|
|
||
|
|
**Interface simplicity**: Few methods, simple parameters = Easier to learn and use correctly.
|
||
|
|
|
||
|
|
**Generality**: Can handle future use cases without modification. But avoid over-generalization.
|
||
|
|
|
||
|
|
**Implementation efficiency**: Does interface shape allow efficient implementation? Or does it force awkward internal implementation?
|
||
|
|
|
||
|
|
**Depth**: Small interface hiding large complexity = deep module (good). Large interface with thin implementation = shallow module (avoid).
|
||
|
|
|
||
|
|
```
|
||
|
|
Deep module (good):
|
||
|
|
┌─────────────────────┐
|
||
|
|
│ Small Interface │ ← Few methods, simple parameters
|
||
|
|
├─────────────────────┤
|
||
|
|
│ │
|
||
|
|
│ │
|
||
|
|
│ Deep Implementation│ ← Complex logic hidden inside
|
||
|
|
│ │
|
||
|
|
│ │
|
||
|
|
└─────────────────────┘
|
||
|
|
|
||
|
|
Shallow module (avoid):
|
||
|
|
┌─────────────────────────────────┐
|
||
|
|
│ Large Interface │ ← Many methods, complex parameters
|
||
|
|
├─────────────────────────────────┤
|
||
|
|
│ Thin Implementation │ ← Just a pass-through
|
||
|
|
└─────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
## Anti-Patterns
|
||
|
|
|
||
|
|
- Don't let sub-agents generate similar designs — force distinct ones
|
||
|
|
- Don't skip comparison — value comes from contrast
|
||
|
|
- Don't implement at this stage — this is pure interface design
|
||
|
|
- Don't evaluate options based on implementation effort — only look at interface quality
|
||
|
|
|
||
|
|
## Golang Interface Design Examples
|
||
|
|
|
||
|
|
```go
|
||
|
|
// Option A: Minimize method count
|
||
|
|
type UserRepository interface {
|
||
|
|
GetByID(ctx context.Context, id string) (*domain.User, error)
|
||
|
|
Save(ctx context.Context, user *domain.User) error
|
||
|
|
}
|
||
|
|
// Pros: Simple, easy to mock
|
||
|
|
// Cons: Save handles both Create and Update, depends on whether it exists
|
||
|
|
|
||
|
|
// Option B: Separate read/write
|
||
|
|
type UserReader interface {
|
||
|
|
GetByID(ctx context.Context, id string) (*domain.User, error)
|
||
|
|
List(ctx context.Context, page, limit int) ([]*domain.User, error)
|
||
|
|
}
|
||
|
|
|
||
|
|
type UserWriter interface {
|
||
|
|
Create(ctx context.Context, user *domain.User) error
|
||
|
|
Update(ctx context.Context, user *domain.User) error
|
||
|
|
Delete(ctx context.Context, id string) error
|
||
|
|
}
|
||
|
|
// Pros: CQRS friendly, separated concerns
|
||
|
|
// Cons: More methods, but each method has clearer semantics
|
||
|
|
|
||
|
|
// Option C: Optimize for common operations
|
||
|
|
type UserService interface {
|
||
|
|
Register(ctx context.Context, email, password, name string) (*domain.User, error)
|
||
|
|
Authenticate(ctx context.Context, email, password string) (*domain.User, error)
|
||
|
|
GetProfile(ctx context.Context, id string) (*domain.User, error)
|
||
|
|
}
|
||
|
|
// Pros: Directly maps to business operations, most intuitive
|
||
|
|
// Cons: Need to add method for each new operation, less flexible
|
||
|
|
```
|
||
|
|
|
||
|
|
## Integration with be-api-design
|
||
|
|
|
||
|
|
This skill is automatically called by `be-api-design` at step 3. API design flow:
|
||
|
|
|
||
|
|
```
|
||
|
|
be-api-design step 1: Read PRD
|
||
|
|
be-api-design step 2: Identify resources and operations
|
||
|
|
→ design-an-interface: Explore 2-3 API design options
|
||
|
|
be-api-design step 4: Select option, define OpenAPI spec
|
||
|
|
```
|
||
|
|
|
||
|
|
## Related Skills
|
||
|
|
|
||
|
|
- **Prerequisite**: `write-a-prd` (PRD complete)
|
||
|
|
- **Follow-up**: `be-api-design` (API spec definition)
|