Dependencies¶
Dependencies model "must complete before" relationships between cards.
Overview¶
When Card B depends on Card A:
- Card B is "blocked by" Card A
- Card A is "blocking" Card B
- Card B cannot be considered complete until Card A is Done
Card A (Login)
│
│ blocks
▼
Card B (Tests) ← blocked until Card A is Done
│
│ blocks
▼
Card C (Deploy) ← blocked until Card B is Done
Adding Dependencies¶
// Card B depends on Card A
err := board.AddDependency(ctx, cardB.ID, cardA.ID)
// Multiple dependencies
err = board.AddDependency(ctx, cardC.ID, cardA.ID)
err = board.AddDependency(ctx, cardC.ID, cardB.ID)
Removing Dependencies¶
Checking Dependencies¶
DependsOn vs BlockedBy¶
card, _ := board.GetCard(ctx, cardID)
// DependsOn - cards this card waits for
fmt.Printf("Depends on: %v\n", card.DependsOn)
// BlockedBy - cards that are blocking (not Done)
fmt.Printf("Blocked by: %v\n", card.BlockedBy)
Key difference:
DependsOn- All declared dependenciesBlockedBy- Dependencies that are not yet Done
Example¶
// Card A is Done
// Card B (depends on A, C) - A is Done, C is not
card, _ := board.GetCard(ctx, cardB.ID)
card.DependsOn // ["cardA", "cardC"]
card.BlockedBy // ["cardC"] - only C is blocking
Blocked Cards¶
List Blocked Cards¶
blocked := board.ListCards(ctx, omniworkboard.ListOptions{
BlockedOnly: true,
})
for _, c := range blocked {
fmt.Printf("%s blocked by: %v\n", c.Title, c.BlockedBy)
}
Check if Blocked¶
card, _ := board.GetCard(ctx, cardID)
if len(card.BlockedBy) > 0 {
fmt.Printf("Card is blocked by %d cards\n", len(card.BlockedBy))
}
Dependency Chains¶
Dependencies can form chains:
// A → B → C → D
board.AddDependency(ctx, cardB.ID, cardA.ID)
board.AddDependency(ctx, cardC.ID, cardB.ID)
board.AddDependency(ctx, cardD.ID, cardC.ID)
When A completes, B unblocks. When B completes, C unblocks. And so on.
Cycle Detection¶
Circular dependencies are prevented:
// This would create A → B → A cycle
board.AddDependency(ctx, cardA.ID, cardB.ID)
board.AddDependency(ctx, cardB.ID, cardA.ID) // Error: cycle detected
Dependency Patterns¶
Sequential Tasks¶
board.AddDependency(ctx, implement.ID, design.ID)
board.AddDependency(ctx, test.ID, implement.ID)
board.AddDependency(ctx, deploy.ID, test.ID)
Parallel with Join¶
board.AddDependency(ctx, frontend.ID, api.ID)
board.AddDependency(ctx, backend.ID, api.ID)
board.AddDependency(ctx, integration.ID, frontend.ID)
board.AddDependency(ctx, integration.ID, backend.ID)
Multiple Prerequisites¶
board.AddDependency(ctx, task.ID, reqA.ID)
board.AddDependency(ctx, task.ID, reqB.ID)
board.AddDependency(ctx, task.ID, reqC.ID)
Unblocking¶
When a blocking card moves to Done, dependents are automatically unblocked:
// B depends on A
board.AddDependency(ctx, cardB.ID, cardA.ID)
// B is blocked
card, _ := board.GetCard(ctx, cardB.ID)
fmt.Println(card.BlockedBy) // ["cardA"]
// Complete A
board.MoveCard(ctx, cardA.ID, omniworkboard.ColumnDone)
// B is unblocked
card, _ = board.GetCard(ctx, cardB.ID)
fmt.Println(card.BlockedBy) // []
Visualizing Dependencies¶
Get Dependency Graph¶
graph := board.DependencyGraph(ctx)
for cardID, deps := range graph {
card, _ := board.GetCard(ctx, cardID)
fmt.Printf("%s depends on:\n", card.Title)
for _, depID := range deps {
dep, _ := board.GetCard(ctx, depID)
fmt.Printf(" - %s\n", dep.Title)
}
}
Critical Path¶
Find the longest dependency chain:
path := board.CriticalPath(ctx)
for i, cardID := range path {
card, _ := board.GetCard(ctx, cardID)
fmt.Printf("%d. %s\n", i+1, card.Title)
}
Best Practices¶
Keep Chains Short¶
Long chains increase risk:
// Risky: long chain
A → B → C → D → E → F → G
// Better: parallel where possible
A → B ─┐
A → C ─┼→ G
A → D ─┘
Explicit Dependencies¶
Only add necessary dependencies:
// Don't add if not truly dependent
// ❌ "Write tests" depends on "Update README"
// ✓ Add if there's a real dependency
// "Write tests" depends on "Implement feature"
Review Blocked Cards¶
Regularly check blocked cards:
blocked := board.ListCards(ctx, omniworkboard.ListOptions{
BlockedOnly: true,
})
if len(blocked) > 5 {
fmt.Println("Warning: many blocked cards, review dependencies")
}
Error Handling¶
err := board.AddDependency(ctx, cardA.ID, cardB.ID)
if err != nil {
switch {
case errors.Is(err, omniworkboard.ErrCardNotFound):
fmt.Println("Card not found")
case errors.Is(err, omniworkboard.ErrCycleDetected):
fmt.Println("Would create circular dependency")
case errors.Is(err, omniworkboard.ErrSelfDependency):
fmt.Println("Card cannot depend on itself")
}
}